// OpenSTA, Static Timing Analyzer
// Copyright (c) 2019, 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/>.

#ifndef STA_CONCRETE_PARASITICS_PVT_H
#define STA_CONCRETE_PARASITICS_PVT_H

#include "Parasitics.hh"

namespace sta {

class ConcretePoleResidue;
class ConcreteParasiticDevice;
class ConcreteParasiticPinNode;
class ConcreteParasiticSubNode;
class ConcreteParasiticNode;

typedef Map<const Pin*, float> ConcreteElmoreLoadMap;
typedef ConcreteElmoreLoadMap::Iterator ConcretePiElmoreLoadIterator;
typedef Map<const Pin*, ConcretePoleResidue*> ConcretePoleResidueMap;
typedef std::pair<Net*, int> NetId;
struct NetIdLess
{
  bool operator()(const NetId *net_id1,
		  const NetId *net_id2) const;
};
typedef Map<NetId*, ConcreteParasiticSubNode*,
	    NetIdLess > ConcreteParasiticSubNodeMap;
typedef Map<const Pin*,
	    ConcreteParasiticPinNode*> ConcreteParasiticPinNodeMap;
typedef Vector<ConcreteParasiticDevice*> ConcreteParasiticDeviceSeq;
typedef Set<ConcreteParasiticDevice*> ConcreteParasiticDeviceSet;
typedef Vector<ConcreteParasiticNode*> ConcreteParasiticNodeSeq;

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

// Base class for parasitics.
class ConcreteParasitic : public Parasitic
{
public:
  virtual ~ConcreteParasitic();
  virtual float capacitance() const = 0;
  virtual bool isLumpedElmore() const;
  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 ParasiticDeviceIterator *deviceIterator();
  virtual ParasiticNodeIterator *nodeIterator();
};

class ConcreteElmore
{
public:
  void findElmore(const Pin *load_pin,
		  float &elmore,
		  bool &exists) const;
  void deleteLoad(const Pin *load_pin);
  void setElmore(const Pin *load_pin,
		 float elmore);

protected:
  ConcreteElmore();
  virtual ~ConcreteElmore();

private:
  ConcreteElmoreLoadMap *loads_;
};

class ConcreteLumpedElmore : public ConcreteParasitic,
			     public ConcreteElmore
{
public:
  ConcreteLumpedElmore(float cap);
  virtual bool isLumpedElmore() const { return true; }
  virtual float capacitance() const { return cap_; }
  void setCapacitance(float cap);
  virtual void findElmore(const Pin *load_pin,
			  float &elmore,
			  bool &exists) const;
  virtual void setElmore(const Pin *load_pin,
			 float elmore);

private:
  float cap_;
};

// 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_;
};

// Pi model for a driver pin and the elmore delay to each load.
class ConcretePiElmore : public ConcretePi,
			 public ConcreteElmore,
			 public ConcreteParasitic
{
public:
  ConcretePiElmore(float c2,
		   float rpi,
		   float c1);
  virtual bool isPiElmore() const { return true; }
  virtual bool isPiModel() const { return true; }
  virtual float capacitance() 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);
};

// PiElmore from wireload model estimate.
class ConcretePiElmoreEstimated : public ConcretePi,
				  public ConcreteParasitic
{
public:
  ConcretePiElmoreEstimated(float c2,
			    float rpi,
			    float c1,
			    float elmore_res,
			    float elmore_cap,
			    bool elmore_use_load_cap,
			    const TransRiseFall *tr,
			    const OperatingConditions *op_cond,
			    const Corner *corner,
			    const MinMax *min_max,
			    Sdc *sdc);
  virtual float capacitance() const;
  virtual bool isPiElmore() const { return true; }
  virtual bool isPiModel() const { return true; }
  virtual void piModel(float &c2, float &rpi, float &c1) const;
  virtual void findElmore(const Pin *load_pin, float &elmore,
			  bool &exists) const;
  virtual void setElmore(const Pin *load_pin, float elmore);

private:
  float elmore_res_;
  float elmore_cap_;
  bool elmore_use_load_cap_;
  const TransRiseFall *tr_;
  const OperatingConditions *op_cond_;
  const Corner *corner_;
  const MinMax *min_max_;
  Sdc *sdc_;
};

class ConcretePoleResidue : public ConcreteParasitic
{
public:
  ConcretePoleResidue(ComplexFloatSeq *poles,
		      ComplexFloatSeq *residues);
  virtual ~ConcretePoleResidue();
  virtual bool isPoleResidue() const { return true; }
  size_t poleResidueCount() const;
  void poleResidue(int index, ComplexFloat &pole, ComplexFloat &residue) const;
  void setPoleResidue(ComplexFloatSeq *poles, ComplexFloatSeq *residues);
  float capacitance() const { return 0.0; }

  using ConcreteParasitic::setPoleResidue;

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

// 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);
  virtual ~ConcretePiPoleResidue();
  virtual bool isPiPoleResidue() const { return true; }
  virtual bool isPiModel() const { return true; }
  virtual float capacitance() 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 Parasitic *findPoleResidue(const Pin *load_pin) const;
  virtual void setPoleResidue(const Pin *load_pin,
			      ComplexFloatSeq *poles,
			      ComplexFloatSeq *residues);
  void deleteLoad(const Pin *load_pin);

private:
  ConcretePoleResidueMap *load_pole_residue_;
};

class ConcreteParasiticNode : public ParasiticNode
{
public:
  virtual ~ConcreteParasiticNode() {}
  float capacitance() const;
  virtual const char *name(const Network *network) const = 0;
  virtual bool isPinNode() const { return false; }
  ConcreteParasiticDeviceSeq *devices() { return &devices_; }
  void incrCapacitance(float cap);
  void addDevice(ConcreteParasiticDevice *device);

protected:
  ConcreteParasiticNode();

  float cap_;
  ConcreteParasiticDeviceSeq devices_;

  friend class ConcreteParasiticNetwork;
};

class ConcreteParasiticSubNode : public ConcreteParasiticNode
{
public:
  ConcreteParasiticSubNode(Net *net,
			   int id);
  virtual const char *name(const Network *network) const;

private:
  Net *net_;
  int id_;
};

class ConcreteParasiticPinNode : public ConcreteParasiticNode
{
public:
  ConcreteParasiticPinNode(const Pin *pin);
  const Pin *pin() const { return pin_; }
  virtual bool isPinNode() const { return true; }
  virtual const char *name(const Network *network) const;

private:
  const Pin *pin_;
};

class ConcreteParasiticDevice : public ParasiticDevice
{
public:
  ConcreteParasiticDevice(const char *name,
			  ConcreteParasiticNode *node,
			  float value);
  virtual ~ConcreteParasiticDevice();
  virtual bool isResistor() const { return false; }
  virtual bool isCouplingCap() const { return false; }
  const char *name() const { return name_; }
  float value() const { return value_; }
  ConcreteParasiticNode *node1() const { return node_; }
  virtual ConcreteParasiticNode *node2() const = 0;
  virtual ParasiticNode *otherNode(ParasiticNode *node) const = 0;
  virtual void replaceNode(ConcreteParasiticNode *from_node,
			   ConcreteParasiticNode *to_node) = 0;

protected:
  const char *name_;
  ConcreteParasiticNode *node_;
  float value_;

  friend class ConcreteParasiticNetwork;
};

class ConcreteParasiticResistor : public ConcreteParasiticDevice
{
public:
  ConcreteParasiticResistor(const char *name,
			    ConcreteParasiticNode *node,
			    ConcreteParasiticNode *other_node,
			    float res);
  virtual bool isResistor() const { return true; }
  virtual ConcreteParasiticNode *node2() const { return other_node_; }
  virtual ParasiticNode *otherNode(ParasiticNode *node) const;
  virtual void replaceNode(ConcreteParasiticNode *from_node,
			   ConcreteParasiticNode *to_node);

private:
  ConcreteParasiticNode *other_node_;
};

// Base class for coupling capacitors.
class ConcreteCouplingCap : public ConcreteParasiticDevice
{
public:
  ConcreteCouplingCap(const char *name,
		      ConcreteParasiticNode *node,
		      float cap);
  virtual bool isCouplingCap() const { return true; }
  virtual ConcreteParasiticNode *node2() const { return nullptr; }
  virtual void replaceNode(ConcreteParasiticNode *from_node,
			   ConcreteParasiticNode *to_node);
};

class ConcreteCouplingCapInt : public ConcreteCouplingCap
{
public:
  ConcreteCouplingCapInt(const char *name,
			 ConcreteParasiticNode *node,
			 ConcreteParasiticNode *other_node,
			 float cap);
  virtual bool isCouplingCap() const { return true; }
  virtual ConcreteParasiticNode *node2() const { return other_node_; }
  virtual ParasiticNode *otherNode(ParasiticNode *node) const;
  virtual void replaceNode(ConcreteParasiticNode *from_node,
			   ConcreteParasiticNode *to_node);

private:
  ConcreteParasiticNode *other_node_;
};

class ConcreteCouplingCapExtNode : public ConcreteCouplingCap
{
public:
  ConcreteCouplingCapExtNode(const char *name,
			     ConcreteParasiticNode *node,
			     Net *other_node_net,
			     int other_node_id,
			     float cap);
  virtual bool isCouplingCap() const { return true; }
  virtual ParasiticNode *otherNode(ParasiticNode *node) const;
  virtual void replaceNode(ConcreteParasiticNode *from_node,
			   ConcreteParasiticNode *to_node);

private:
};

class ConcreteCouplingCapExtPin : public ConcreteCouplingCap
{
public:
  ConcreteCouplingCapExtPin(const char *name,
			    ConcreteParasiticNode *node,
			    Pin *other_node_pin,
			    float cap);
  virtual bool isCouplingCap() const { return true; }
  virtual ParasiticNode *otherNode(ParasiticNode *node) const;
  virtual void replaceNode(ConcreteParasiticNode *from_node,
			   ConcreteParasiticNode *to_node);

private:
};

class ConcreteParasiticDeviceSetIterator : public ParasiticDeviceIterator
{
public:
  ConcreteParasiticDeviceSetIterator(ConcreteParasiticDeviceSet *devices);
  virtual ~ConcreteParasiticDeviceSetIterator();
  bool hasNext() { return iter_.hasNext(); }
  ParasiticDevice *next() { return iter_.next(); }

private:
  ConcreteParasiticDeviceSet::ConstIterator iter_;
};

class ConcreteParasiticDeviceSeqIterator : public ParasiticDeviceIterator
{
public:
  ConcreteParasiticDeviceSeqIterator(ConcreteParasiticDeviceSeq *devices);
  bool hasNext() { return iter_.hasNext(); }
  ParasiticDevice *next() { return iter_.next(); }

private:
  ConcreteParasiticDeviceSeq::ConstIterator iter_;
};

class ConcreteParasiticNodeSeqIterator : public ParasiticNodeIterator
{
public:
  ConcreteParasiticNodeSeqIterator(ConcreteParasiticNodeSeq *devices);
  virtual ~ConcreteParasiticNodeSeqIterator();
  bool hasNext() { return iter_.hasNext(); }
  ParasiticNode *next() { return iter_.next(); }

private:
  ConcreteParasiticNodeSeq::ConstIterator iter_;
};

class ConcreteParasiticNetwork : public ParasiticNetwork,
				 public ConcreteParasitic
{
public:
  ConcreteParasiticNetwork(bool includes_pin_caps);
  virtual ~ConcreteParasiticNetwork();
  virtual bool isParasiticNetwork() const { return true; }
  bool includesPinCaps() const { return includes_pin_caps_; }
  ConcreteParasiticNode *ensureParasiticNode(Net *net,
					     int id);
  ConcreteParasiticNode *findNode(const Pin *pin);
  ConcreteParasiticNode *ensureParasiticNode(const Pin *pin);
  virtual float capacitance() const;
  ConcreteParasiticPinNodeMap *pinNodes() { return &pin_nodes_; }
  ConcreteParasiticSubNodeMap *subNodes() { return &sub_nodes_; }
  void disconnectPin(const Pin *pin, Net *net);
  virtual ParasiticDeviceIterator *deviceIterator();
  virtual ParasiticNodeIterator *nodeIterator();
  virtual void devices(// Return value.
		       ConcreteParasiticDeviceSet *devices);

private:
  void deleteNodes();
  void deleteDevices();

  ConcreteParasiticSubNodeMap sub_nodes_;
  ConcreteParasiticPinNodeMap pin_nodes_;
  unsigned max_node_id_:31;
  bool includes_pin_caps_:1;
};

} // namespace
#endif
