/***************************************************************************
 *   Copyright (C) 2012 by Santiago González                               *
 *                                                                         *
 ***( see copyright.txt file at root folder )*******************************/

#pragma once

#include <QGraphicsScene>
#include <QTimer>

#include "component.h"
#include "connector.h"
#include "pin.h"

#define COMP_STATE_NEW "__COMP_STATE_NEW__"

class CircuitView; 
class SubPackage; 
class Simulator; 
class SubCircuit; 
class Node; 

class Circuit : public QGraphicsScene
{
    friend class SubCircuit; 
    friend class Simulator; 

    Q_OBJECT

    public:
        Circuit( int width, int height, CircuitView* parent ); 
        ~Circuit(); 

 static Circuit* self() { return m_pSelf;  }

        bool drawGrid() { return !m_hideGrid;  }
        void setDrawGrid( bool draw ); 

        bool animateLogic() { return m_animateLogic;  }
        void setAnimateLogic( bool an ); 

        bool animateCurr() { return m_animateCurr;  }
        void setAnimateCurr( bool an ); 

        bool ansiSymbols() { return m_ansiSymbols;  }
        void setAnsiSymbols( bool an ); 

        int sceneWidth() { return m_sceneWidth;  }
        void setSceneWidth( int w ); 

        int sceneHeight() { return m_sceneHeight;  }
        void setSceneHeight( int h ); 

        int undoSteps() { return m_maxUndoSteps;  }
        void setUndoSteps( int steps ); 

        int autoBck(); 
        void setAutoBck( int secs ); 

        void removeItems(); 
        void removeComp( Component* comp) ; 
        void compRemoved( bool removed ) { m_compRemoved = removed;  }
        void removeNode( Node* node ); 
        void removeConnector( Connector* conn ); 
        void clearCircuit(); 

        //--- Undo/Redo ----------------------------------
        void saveChanges(); 
        void removeLastUndo(); 
        void addCompChange(  QString component, QString property, QString undoVal ); 
        void saveCompChange( QString component, QString property, QString undoVal ); 
        void beginCircuitBatch(); 
        void endCircuitBatch(); 
        void calcCircuitChanges();  // Calculate total changes
        void cancelUndoStep();      // Revert changes done
        void beginUndoStep();       // Record current state
        void endUndoStep();         // Does create/remove
        bool undoRedo() { return m_undo || m_redo;  }
        //------------------------------------------------

        void setChanged(); 

        void accepKeys( bool a ) { m_acceptKeys = a;  }

        Pin* findPin( int x, int y, QString id ); 
        Pin* findPin( QString id ); 

        void loadCircuit( QString filePath ); 
        bool saveCircuit( QString filePath ); 

        QString newSceneId() { return QString::number(++m_seqNumber);  }
        QString newConnectorId() { return QString::number(++m_conNumber);  }

        void newconnector( Pin* startpin, bool save=true ); 
        void closeconnector( Pin* endpin, bool save=false ); 
        void deleteNewConnector(); 
        //void updateConnectors(); 
        Connector* getNewConnector() { return m_newConnector;  }

        Component* createItem( QString type, QString id, bool map=true ); 
        Component* createComponent( QString type, QString name, QPoint pos, bool map=true ); 
        void addComponent( Component* comp ); 

        void addNode( Node* node ); 

        QList<Component*>* compList() { return &m_compList;  }
        QList<Connector*>* conList()  { return &m_connList;  }
        QList<Node*>*      nodeList() { return &m_nodeList;  }
        QMap<QString, CompBase*>* compMap() { return &m_compMap; }

        Component* getCompById( QString id ); 
        QString origId( QString name ) { return m_idMap.value( name );  } // used by Shield

        bool is_constarted() { return m_conStarted ;  }

        SubPackage* getBoard() { return m_board;  }
        void setBoard( SubPackage* b ) { m_board = b;  }

        SubCircuit* getSubcircuit() { return m_subCircuit;  }
        void setSubcircuit( SubCircuit* sc ) { m_subCircuit = sc;  }

        bool pasting() { return m_pasting;  }
        bool isBusy()  { return m_busy || m_pasting | m_deleting;  }

        void addPin( Pin* pin, QString pinId ) { m_pinMap[ pinId ] = pin;  m_LdPinMap[ pinId ] = pin;  }
        void remPin( QString pinId ) { m_pinMap.remove( pinId );  }
        void updatePin( ePin* epin, QString oldId, QString newId ); 
        Pin* getPin( QString pin ){ return m_pinMap.value( pin );  }

        QString getSeqNumber( QString name ); 
        QString replaceId( QString pinName ); 

        const QString getFilePath() const { return m_filePath;  }
        void setFilePath( QString f ) { m_filePath = f;  }

        bool saveString( QString fileName, QString doc ); 
        QString circuitToString(); 

        int circuitRev() { return m_circRev;  }

        void drawBackground( QPainter* painter, const QRectF &rect ); 

    signals:
        void keyEvent( QString key, bool pressed ); 

    public slots:
        void copy( QPointF eventpoint ); 
        void paste( QPointF eventpoint ); 
        void undo(); 
        void redo(); 
        void importCircuit(); 
        //void bom(); 
        void saveBackup(); 

    protected:
        void mousePressEvent( QGraphicsSceneMouseEvent* event ); 
        void mouseReleaseEvent( QGraphicsSceneMouseEvent* event ); 
        void mouseMoveEvent( QGraphicsSceneMouseEvent* event ); 
        void keyPressEvent( QKeyEvent* event ); 
        void keyReleaseEvent( QKeyEvent* event ); 
        void dropEvent( QGraphicsSceneDragDropEvent* event ); 

    private:
 static Circuit*  m_pSelf; 

        void loadStrDoc( QString &doc ); 

        QString circuitHeader(); 
        void updatePinName( QString* name ); 

        void setSize( int width, int height ); 

        QString m_filePath; 
        QString m_backupPath; 

        QRect        m_scenerect; 
        CircuitView* m_graphicView; 
        Connector*   m_newConnector; 

        int m_sceneWidth; 
        int m_sceneHeight; 

        int m_circRev; 
        int m_seqNumber; 
        int m_conNumber; 
        int m_error; 

        bool m_pasting; 
        bool m_deleting; 
        bool m_loading; 
        bool m_conStarted; 
        bool m_hideGrid; 

        bool m_compRemoved; 
        bool m_animateLogic; 
        bool m_animateCurr; 
        bool m_ansiSymbols; 
        bool m_changed; 
        bool m_busy; 
        bool m_undo; 
        bool m_redo; 
        bool m_acceptKeys; 
        int m_cicuitBatch; 

        QPointF m_eventpoint; 
        QPointF m_deltaMove; 

        QList<Component*> m_compList;    // Component list
        QList<Connector*> m_connList;    // Connector list
        QList<Node*>      m_nodeList;    // Node list

        SubPackage* m_board; 
        SubCircuit* m_subCircuit; 

        QMap<QString, Pin*>      m_pinMap;    // Pin Id to Pin*
        QMap<QString, Pin*>      m_LdPinMap;  // Pin Id to Pin* while loading/pasting/importing
        QMap<QString, QString>   m_idMap;     // Component seqNumber to new seqNumber (pasting)
        QMap<QString, CompBase*> m_compMap;   // Component Id to Component*

        QTimer m_bckpTimer; 

        Simulator* m_simulator; 

        //--- Undo/Redo ----------------------------------

        struct compChange{      // Component Change to be performed by Undo/Redo to complete a Circuit change
            QString component;   // Component name
            QString property;    // Property name
            QString undoValue;   // Property value for Undo step
            QString redoValue;   // Property value for Redo step
        }; 
        struct circChange{       // Circuit Change to be performed by Undo/Redo to restore circuit state
            QList<compChange> compChanges; 
            int size() { return compChanges.size();  }
            void clear() { compChanges.clear();  }
        }; 

        inline void clearCircChanges() { m_circChange.clear();  }
        void deleteRemoved(); 
        void restoreState(); 

        int m_maxUndoSteps; 
        int m_undoIndex; 

        circChange m_circChange; 
        QList<circChange> m_undoStack; 

        QList<CompBase*>  m_removedComps;  // removed component list; 
        QList<Connector*> m_oldConns; 
        QList<Component*> m_oldComps; 
        QList<Node*>      m_oldNodes; 
        QMap<CompBase*, QString> m_compStrMap; 

        uint32_t* m_background; 
        uint32_t* m_foreground; 
}; 
