// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// 
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
// 
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 
// This notice may not be removed or altered from any source distribution.

#pragma once

#include <map>
#include <set>
#include <string>

#include "Parasitics.hh"

namespace sta {

class ConcretePoleResidue;
class ConcreteParasiticDevice;
class ConcreteParasiticNode;

using NetIdPair = std::pair<const Net*, int>;

class NetIdPairLess
{
public:
  NetIdPairLess(const Network *network);
  bool operator()(const NetIdPair &net_id1,
                  const NetIdPair &net_id2) const;

private:
  const NetIdLess net_less_;
};

using ConcreteElmoreLoadMap = std::map<const Pin*, float>;
using ConcretePoleResidueMap = std::map<const Pin*, ConcretePoleResidue>;
using ConcreteParasiticSubNodeMap = std::map<NetIdPair,ConcreteParasiticNode*,
                                               NetIdPairLess>;
using ConcreteParasiticPinNodeMap = std::map<const Pin*,ConcreteParasiticNode*,PinIdLess>;
using ParasiticNodeSet = std::set<ParasiticNode*>;
using ParasiticResistorSet = std::set<ParasiticResistor*>;
using ParasiticResistorSeq = std::vector<ParasiticResistor*>;

// Empty base class definitions so casts are not required on returned
// objects.
class Parasitic {};
class ParasiticNode {};
class ParasiticResistor {};
class ParasiticCapacitor {};
class ParasiticNetwork {};

// Base class for parasitics.
class ConcreteParasitic : public Parasitic
{
public:
  virtual ~ConcreteParasitic() = default;
  virtual float capacitance() const = 0;
  virtual bool isPiElmore() const;
  virtual bool isPiModel() const;
  virtual bool isPiPoleResidue() const;
  virtual bool isPoleResidue() const;
  virtual bool isParasiticNetwork() const;
  virtual void piModel(float &c2,
                       float &rpi,
                       float &c1) const;
  virtual void setPiModel(float c2,
                          float rpi,
                          float c1);
  virtual bool isReducedParasiticNetwork() const;
  virtual void setIsReduced(bool reduced);
  virtual void findElmore(const Pin *load_pin,
                          float &elmore,
                          bool &exists) const;
  virtual void setElmore(const Pin *load_pin,
                         float elmore);
  virtual Parasitic *findPoleResidue(const Pin *load_pin) const;
  virtual void setPoleResidue(const Pin *load_pin,
                              ComplexFloatSeq *poles,
                              ComplexFloatSeq *residues);
  virtual PinSet unannotatedLoads(const Pin *drvr_pin,
                                  const Parasitics *parasitics) const = 0;
};

// Pi model for a driver pin.
class ConcretePi
{
public:
  ConcretePi(float c2,
             float rpi,
             float c1);
  float capacitance() const;
  void piModel(float &c2,
               float &rpi,
               float &c1) const;
  void setPiModel(float c2,
                  float rpi,
                  float c1);
  bool isReducedParasiticNetwork() const { return is_reduced_; }
  void setIsReduced(bool reduced);

protected:
  float c2_;
  float rpi_;
  float c1_;
  bool is_reduced_{false};
};

// Pi model for a driver pin and the elmore delay to each load.
class ConcretePiElmore : public ConcretePi,
                         public ConcreteParasitic
{
public:
  ConcretePiElmore(float c2,
                   float rpi,
                   float c1);
  bool isPiElmore() const override { return true; }
  bool isPiModel() const override { return true; }
  float capacitance() const override;
  void piModel(float &c2,
               float &rpi,
               float &c1) const override;
  void setPiModel(float c2,
                  float rpi,
                  float c1) override;
  bool isReducedParasiticNetwork() const override;
  void setIsReduced(bool reduced) override;
  void findElmore(const Pin *load_pin,
                  float &elmore,
                  bool &exists) const override;
  void setElmore(const Pin *load_pin,
                 float elmore) override;
  PinSet unannotatedLoads(const Pin *drvr_pin,
                          const Parasitics *parasitics) const override;
  void deleteLoad(const Pin *load_pin);
  ConcreteElmoreLoadMap &loads() { return loads_; }

private:
  ConcreteElmoreLoadMap loads_;
};

class ConcretePoleResidue : public ConcreteParasitic
{
public:
  ~ConcretePoleResidue() override;
  bool isPoleResidue() const override { return true; }
  float capacitance() const override { return 0.0; }
  PinSet unannotatedLoads(const Pin *drvr_pin,
                          const Parasitics *parasitics) const override;

  void setPoleResidue(ComplexFloatSeq *poles,
                      ComplexFloatSeq *residues);
  void poleResidue(int index,
                   ComplexFloat &pole,
                   ComplexFloat &residue) const;
  size_t poleResidueCount() const;
  using ConcreteParasitic::setPoleResidue;

private:
  ComplexFloatSeq *poles_{nullptr};
  ComplexFloatSeq *residues_{nullptr};
};

// Pi model for a driver pin and the pole/residues to each load.
class ConcretePiPoleResidue : public ConcretePi,
                              public ConcreteParasitic
{
public:
  ConcretePiPoleResidue(float c2,
                        float rpi,
                        float c1);
  bool isPiPoleResidue() const override { return true; }
  bool isPiModel() const override { return true; }
  float capacitance() const override;
  void piModel(float &c2,
               float &rpi,
               float &c1) const override;
  void setPiModel(float c2,
                  float rpi,
                  float c1) override;
  bool isReducedParasiticNetwork() const override;
  void setIsReduced(bool reduced) override;
  Parasitic *findPoleResidue(const Pin *load_pin) const override;
  void setPoleResidue(const Pin *load_pin,
                      ComplexFloatSeq *poles,
                      ComplexFloatSeq *residues) override;
  PinSet unannotatedLoads(const Pin *drvr_pin,
                          const Parasitics *parasitics) const override;
  void deleteLoad(const Pin *load_pin);
  ConcretePoleResidueMap &loadResidues() { return load_pole_residue_; }

private:
  ConcretePoleResidueMap load_pole_residue_;
};

class ConcreteParasiticNetwork : public ParasiticNetwork,
                                 public ConcreteParasitic
{
public:
  ConcreteParasiticNetwork(const Net *net,
                           bool includes_pin_caps,
                           const Network *network);
  ConcreteParasiticNetwork(ConcreteParasiticNetwork &&parasitic) noexcept;
  ~ConcreteParasiticNetwork() override;
  bool isParasiticNetwork() const override { return true; }
  const Net *net() const { return net_; }
  bool includesPinCaps() const { return includes_pin_caps_; }
  ConcreteParasiticNode *findParasiticNode(const Net *net,
                                           uint32_t id,
                                           const Network *network) const;
  ConcreteParasiticNode *ensureParasiticNode(const Net *net,
                                             uint32_t id,
                                             const Network *network);
  ConcreteParasiticNode *findParasiticNode(const Pin *pin) const;
  ConcreteParasiticNode *ensureParasiticNode(const Pin *pin,
                                             const Network *network);
  float capacitance() const override;
  ParasiticNodeSeq nodes() const;
  void disconnectPin(const Pin *pin,
                     const Net *net,
                     const Network *network);
  ParasiticResistorSeq resistors() const { return resistors_; }
  void addResistor(ParasiticResistor *resistor);
  ParasiticCapacitorSeq capacitors() const { return capacitors_; }
  void addCapacitor(ParasiticCapacitor *capacitor);
  PinSet unannotatedLoads(const Pin *drvr_pin,
                          const Parasitics *parasitics) const override;

private:
  void unannotatedLoads(ParasiticNode *node,
                        ParasiticResistor *from_res,
                        PinSet &loads,
                        ParasiticNodeSet &visited_nodes,
                        ParasiticResistorSet &loop_resistors,
                        ParasiticNodeResistorMap &resistor_map,
                        const Parasitics *parasitics) const;

  void deleteNodes();
  void deleteDevices();

  const Net *net_;
  ConcreteParasiticSubNodeMap sub_nodes_;
  ConcreteParasiticPinNodeMap pin_nodes_;
  ParasiticResistorSeq resistors_;
  ParasiticCapacitorSeq capacitors_;
  unsigned max_node_id_:31{0};
  bool includes_pin_caps_:1;
};

class ConcreteParasiticNode : public ParasiticNode
{
public:
  ConcreteParasiticNode(const Net *net,
                        uint32_t id,
                        bool is_external);
  ConcreteParasiticNode(const Pin *pin,
                        bool is_external);
  float capacitance() const { return cap_; }
  std::string name(const Network *network) const;
  const Net *net(const Network *network) const;
  unsigned id() const { return id_; }
  bool isExternal() const { return is_external_; }
  const Pin *pin() const;
  void incrCapacitance(float cap);

protected:
  ConcreteParasiticNode();

  union {
    const Net *net_;
    const Pin *pin_;
  } net_pin_;
  bool is_net_:1;
  bool is_external_:1;
  unsigned id_:30;
  float cap_;

  friend class ConcreteParasiticNetwork;
};

class ConcreteParasiticDevice
{
public:
  ConcreteParasiticDevice(uint32_t id,
                          float value,
                          ConcreteParasiticNode *node1,
                          ConcreteParasiticNode *node2);
  uint32_t id() const { return id_; }
  float value() const { return value_; }
  ConcreteParasiticNode *node1() const { return node1_; }
  ConcreteParasiticNode *node2() const { return node2_; }
  void replaceNode(ConcreteParasiticNode *from_node,
                   ConcreteParasiticNode *to_node);

protected:
  uint32_t id_;
  float value_;
  ConcreteParasiticNode *node1_;
  ConcreteParasiticNode *node2_;
};

class ConcreteParasiticResistor : public ParasiticResistor,
                                  public ConcreteParasiticDevice 
{
public:
  ConcreteParasiticResistor(uint32_t id,
                            float value,
                            ConcreteParasiticNode *node1,
                            ConcreteParasiticNode *node2);
};

class ConcreteParasiticCapacitor : public ParasiticCapacitor,
                                  public ConcreteParasiticDevice 
{
public:
  ConcreteParasiticCapacitor(uint32_t id,
                             float value,
                             ConcreteParasiticNode *node1,
                             ConcreteParasiticNode *node2);
};

} // namespace sta
