/***************************************************************************
 *   Copyright (C) 2003 by David Saxton                                    *
 *   david@bluehaze.org                                                    *
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#ifndef COMPONENT_H
#define COMPONENT_H

#include "cnitem.h"
#include "circuitdocument.h"

#include <QList>

class ICNDocument;
class CircuitDocument;
class ECNode;
class ECSubcircuit;
class Element;
class Node;
class Pin;

class BJT;
class Capacitance;
class CCCS;
class CCVS;
class CurrentSignal;
class CurrentSource;
class Diode;
class JFET;
class Inductance;
class LogicIn;
class LogicOut;
class MOSFET;
class OpAmp;
class Resistance;
class Switch;
class Transformer;
class VCCS;
class VCVS;
class VoltagePoint;
class VoltageSignal;
class VoltageSource;

typedef QList<ECNode*> ECNodeList;
typedef QList<Element*> ElementList;
typedef QList<Switch*> SwitchList;

typedef QList< QList<Pin*> > PinListList;

/**
Contains vital information about the elements in the component.
*/
class ElementMap
{
	public:
		ElementMap();

		Element * e; // The element
		Pin * n[4]; // The Pins associated with the CNodes in the element

		/// @see Component::setInterCircuitDependent
		PinListList interCircuitDependent;

		/// @see Component::setInterGroundDependent
		PinListList interGroundDependent;
};

typedef QList<ElementMap> ElementMapList;

/**
@short Base class for all electrical components
@author David Saxton
*/
class Component : public CNItem
{
	Q_OBJECT
	public:
		Component( ICNDocument *icnDocument, bool newItem, const QString &id );
		~Component() override;
	
		ECNode* createPin( double _x, double _y, int orientation, const QString &name );
		/**
		 * Converts the voltage level to a colour - this is used in drawing
		 * wires and pins.
		 */
		static QColor voltageColor( double v );
		/**
		 * @return a value between 0.0 and 1.0, representing a scaled version of
		 * the absolute value of the voltage.
		 * @see voltageColor
		 */
		static double voltageLength( double v );
		/**
		 * Angle of orientation
		 */
		int angleDegrees() const { return m_angleDegrees; }
		/**
		 * Sets the angle (in degrees)
		 */
		void setAngleDegrees( int degrees );
		/**
		 * Whether or not the item is flipped
		 */
		bool flipped() const { return b_flipped; }
		/**
		 * Sets whether or not the item is flipped
		 */
		void setFlipped( bool flipped );
		/**
		 * After calculating nodal voltages, each component will be
		 * called to tell its nodes what the current flowing *into*
		 * the component is.
		 */
		void setNodalCurrents();
		/**
		 * @return pointer to the CircuitDocument that we're in.
		 */
		CircuitDocument *circuitDocument() const { return m_pCircuitDocument; }
		void initElements( const uint stage );
		void finishedCreation() override;
		/**
		 * If reinherit (and use) the stepNonLogic function, then you must also
		 * reinherit this function so that it returns true. Else your component
		 * will not get called.
		 */
		virtual bool doesStepNonLogic() const { return false; }
		virtual void stepNonLogic() {};
		/**
		 * Returns the translation matrix used for painting et al
		 * @param angleDegrees The orientation to use
		 * @param x x co-ordinate of the center of the object to be mapped
		 * @param y y co-ordinate of the center of the object to be mapped
		 * @param inverse If false, maps the unrotated item to a rotated one, else mapped->unmapped
		 */
		static QMatrix transMatrix( int angleDegrees, bool flipped, int x, int y, bool inverse = false );
		/**
		 * @return Information about the component in an ItemData struct.
		 */
		ItemData itemData() const override;
		/**
		 * Restores the state of the component from the ItemData struct.
		 */
		void restoreFromItemData( const ItemData &itemData ) override;
	
		BJT *		createBJT( Pin *c, Pin *b, Pin *e, bool isNPN = true );
		BJT *		createBJT( ECNode *c, ECNode *b, ECNode *e, bool isNPN = true );

		Capacitance *createCapacitance( Pin *n0, Pin *n1, double capacitance );
		Capacitance *createCapacitance( ECNode *n0, ECNode *n1, double capacitance );

		CCCS *		createCCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain );
		CCCS *		createCCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain );

		CCVS *		createCCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain );
		CCVS *		createCCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain );

		CurrentSignal *createCurrentSignal( Pin *n0, Pin *n1, double current );
		CurrentSignal *createCurrentSignal( ECNode *n0, ECNode *n1, double current );

		CurrentSource *createCurrentSource( Pin *n0, Pin *n1, double current );
		CurrentSource *createCurrentSource( ECNode *n0, ECNode *n1, double current );

		Diode *		createDiode( Pin *n0, Pin *n1 );
		Diode *		createDiode( ECNode *n0, ECNode *n1 );

		JFET *		createJFET( Pin * D, Pin * G, Pin * S, int JFET_type );
		JFET *		createJFET( ECNode * D, ECNode * G, ECNode * S, int JFET_type );

		Inductance *	createInductance( Pin *n0, Pin *n1, double inductance );
		Inductance *	createInductance( ECNode *n0, ECNode *n1, double inductance );

		LogicIn *	createLogicIn( Pin *node );
		LogicIn *	createLogicIn( ECNode *node );

		LogicOut *	createLogicOut( Pin *node, bool isHigh );
		LogicOut *	createLogicOut( ECNode *node, bool isHigh );

		MOSFET *	createMOSFET( Pin * D, Pin * G, Pin * S, Pin * B, int MOSFET_type );
		MOSFET *	createMOSFET( ECNode * D, ECNode * G, ECNode * S, ECNode * B, int MOSFET_type );

		OpAmp *		createOpAmp( Pin * nonInverting, Pin * out, Pin * inverting );
		OpAmp *		createOpAmp( ECNode * nonInverting, ECNode * out, ECNode * inverting );

		Resistance *	createResistance( Pin *n0, Pin *n1, double resistance );
		Resistance *	createResistance( ECNode *n0, ECNode *n1, double resistance );

		Switch *	createSwitch( Pin *n0, Pin *n1, bool open );
		Switch *	createSwitch( ECNode *n0, ECNode *n1, bool open );

		VCCS *		createVCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain );
		VCCS *		createVCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain );

		VCVS *		createVCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain );
		VCVS *		createVCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain );

		VoltagePoint *	createVoltagePoint( Pin *n0, double voltage );
		VoltagePoint *	createVoltagePoint( ECNode *n0, double voltage );

		VoltageSignal *	createVoltageSignal( Pin *n0, Pin *n1, double voltage );
		VoltageSignal *	createVoltageSignal( ECNode *n0, ECNode *n1, double voltage );

		VoltageSource *	createVoltageSource( Pin *n0, Pin *n1, double voltage );
		VoltageSource *	createVoltageSource( ECNode *n0, ECNode *n1, double voltage );


		ECNode* ecNodeWithID( const QString &ecNodeId  );

		/**
		 * Safely delete an element - in this case, calls element->componentDeleted,
		 * and removes it from the element list.
		 * @param setPinsInterIndependent whether to call
		 * setPinsInterIndependent. The call is time-consuming, and unnecessary
		 * if the pins from which the element was originally attached will be/
		 * were removed, or they will become interdependent again.
		 */
		void removeElement( Element * element, bool setPinsInterIndependent );
		/**
		 * Safely remove a switch.
		 */
		void removeSwitch( Switch * sw );
		/**
		 * Removes all elements and switches.
		 * @param setPinsInterIndependent whether to bother calling
		 * setPinsInterIndependent. This is false when calling from the
		 * destructor, or when the dependency information is the same.
		 */
		void removeElements( bool setPinsInterIndependent = false );
		/**
		 * @return the list of switches that this component uses.
		 */
		SwitchList switchList() const { return m_switchList; }

	signals:
		/**
		 * Emitted when an element is created.
		 */
		void elementCreated( Element * element );
		/**
		 * Emitted when an element is destroyed.
		 */
		void elementDestroyed( Element * element );
		/**
		 * Emitted when a switch. is created
		 */
		void switchCreated( Switch * sw );
		/**
		 * Emitted when a switch is destroyed.
		 */
		void switchDestroyed( Switch * sw );

	public slots:
		virtual void slotUpdateConfiguration();
		void removeItem() override;

	protected:
		/**
		 * Convenience functionality provided for components in a port shape
		 * (such as ParallelPortComponent and SerialPortComponent).
		 */
		void drawPortShape( QPainter & p );
		void itemPointsChanged() override;
		void updateAttachedPositioning() override;
		void initPainter( QPainter &p ) override;
		/**
		 * Untranforms the painter from the matrix. This *must* be called after doing
		 * initPainter( QPainter &p );
		 */
		virtual void deinitPainter( QPainter &p );
		/**
		 * This creates a set of nodes with their internal IDs set to those in QStringList pins.
		 * The pins are in a DIP arrangement, and are spaced width() apart.
		 */
		void initDIP( const QStringList & pins );
		/**
		 * Creates the DIP symbol:
		 * @li constructs rectangular shape
		 * @li puts on text labels in appropriate positions from QStringList pins
		 */
		void initDIPSymbol( const QStringList & pins, int width );
		/**
		 * Create 1 pin on the left of the component, placed half way down if h1 is
		 * -1 - else at the position of h1.
		 */
		void init1PinLeft( int h1 = -1 );
		/**
		 * Create 2 pins on the left of the component, either spread out, or at the
		 * given heights.
		 */
		void init2PinLeft( int h1 = -1, int h2 = -1 );
		/**
		 * Create 3 pins on the left of the component, either spread out, or at the
		 * given heights.
		 */
		void init3PinLeft( int h1 = -1, int h2 = -1, int h3 = -1 );
		/**
		 * Create 4 pins on the left of the component, either spread out, or at the
		 * given heights.
		 */
		void init4PinLeft( int h1 = -1, int h2 = -1, int h3 = -1, int h4 = -1 );
		/**
		 * Create 1 pin on the right of the component, placed half way down if h1 is
		 * -1 - else at the position of h1.
		 */
		void init1PinRight( int h1 = -1 );
		/**
		 * Create 2 pins on the right of the component, either spread out, or at the
		 * given heights.
		 */
		void init2PinRight( int h1 = -1, int h2 = -1 );
		/**
		 * Create 3 pins on the right of the component, either spread out, or at the
		 * given heights.
		 */
		void init3PinRight( int h1 = -1, int h2 = -1, int h3 = -1 );
		/**
		 * Create 4 pins on the right of the component, either spread out, or at the
		 * given heights.
		 */
		void init4PinRight( int h1 = -1, int h2 = -1, int h3 = -1, int h4 = -1 );
		/**
		 * When we remove an element, we have to rebuild the list of inter-dependent
		 * nodes. (when adding elements, we just call setInterDependent).
		 */
		void rebuildPinInterDepedence();

		// Pointers to commonly used nodes
// TODO: why do we have two sets of these?
		ECNode *m_pPNode[4];
		ECNode *m_pNNode[4];

// TODO: only Switch cares about this, so either demote it to a member of class switch or
// refactor it out alltogether.
		QPointer<CircuitDocument> m_pCircuitDocument;
		int m_angleDegrees;
		bool b_flipped;

	private:
		/**
		 * Convenience function for calling both setInterCircuitDependent and
		 * setInterGroundDependent.
		 * @param it Which pins are inter-dependent needs to be recorded in case
		 * this information is later needed in rebuildPinInterDependence.
		 */
		void setInterDependent( ElementMapList::iterator it, const QList<Pin*> & pins );
		/**
		 * Sets all pins independent of each other.
		 */
		void setAllPinsInterIndependent();
		/**
		 * The given pins will affect the simulation of each other. Therefore, they
		 * will need to be simulated in the same circuit.
		 */
		void setInterCircuitDependent( ElementMapList::iterator it, const QList<Pin*> & pins );
		/**
		 * If any of the given pins are ground, then that will affect whether
		 * any of the other pins can be ground.
		 */
		void setInterGroundDependent( ElementMapList::iterator it, const QList<Pin*> & pins );
		/**
		 * List of ElementMaps; which contain information on the pins associated
		 * with the element as well as the dependence between the pins for that
		 * element.
		 * @see ElementMap
		 */
		ElementMapList m_elementMapList;
		/**
		 * The switches used by the component.
TODO: ammend this comment with a more complete justification for the design decision to put this here.
		 */
		SwitchList m_switchList;
		/**
		 * @return an iterator to the element in m_elementMapList
		 */
		ElementMapList::iterator handleElement( Element *e, const QList<Pin*> & pins );
};

#endif
