// File RTMSimulator.h by az

#ifndef RTMSIMULATOR_H
#define RTMSIMULATOR_H

#include "RTMTabWidget.h"
#include "Controls.h"

#include <QMainWindow>
#include <QScrollArea>
#include <QThread>

#include <map>
#include <set>

#define MM_FACTOR 1000.0

class SoQtExaminerViewer;
class QHBoxLayout;
class QWidget;
class ExaminerViewer;
class InfoHelp;
class SimNode;
class SimTriangle;
class RTMSimulator;
class SoCamera;
struct TriangleSettings;

/*namespace Simulator {
    class LonesomeNodesThread;
    class SimulationThread;
    class Triangle;
    class RTMSimulator;
}*/

/**
 * @brief Thread class to check for nodes that are not reachable by neigbor traversal.
 */
class LonesomeNodesThread : public QThread
{
private:
    RTMSimulator* m_pSimulator;
public:
    LonesomeNodesThread(RTMSimulator* pSimulator);
    void run();
};

/**
 * @brief Thread for executing the simulation task.
 */
class SimulationThread : public QThread
{
private:
    RTMSimulator* m_pSimulator;
    int m_cycles;
public:
    SimulationThread(RTMSimulator* pSimulator, int nCycles=0);
    void run();
};

/**
 * @brief Class for displaying a triangle in the viewer widget.
 */
class Triangle
{
public:
    Triangle()
    {
        coords[0] = 0;
        coords[1] = 0;
        coords[2] = 0;
        coords[3] = 0;
        coords[4] = 0;
        coords[5] = 0;
        coords[6] = 0;
        coords[7] = 0;
        coords[8] = 0;
    }

    std::vector<double> getPoint(int pointnr = 0)
    {
        if(pointnr < 3)
        {
            std::vector<double> point(coords+(pointnr*3),coords+(pointnr*3)+3);
            return point;
        }
        else
        {
            std::vector<double> point;
            return point;
        }
    }

    void setPoint(int point, double* point_coords)
    {
        if(point < 3)
        {
            int pos = point*3;
            coords[pos++] = point_coords[0];
            coords[pos++] = point_coords[1];
            coords[pos++] = point_coords[2];
        }
    }
private:
    double coords[9];
};

/**
 * @brief Main class for the RTMSimulator application.
 *
 * Handles all major information and does handling between simulation and user interface.
 */
class RTMSimulator : public QMainWindow
{
    Q_OBJECT
public:
    enum State {
        CALCULATING_NEIGHBORS,
        SIMULATION_RUNNING,
        RECORDING_RUNNING,
        INTERRUPTING_FOR_PAUSE,
        SIMULATION_PAUSED,
        INTERRUPTING_FOR_STOP,
        IDLE,
        SIMULATION_FINISHED
    };

    enum NodeSource {
        DAT_FILE,   //marc mentat file
        UNDEFINED   //no nodes available yet
    };

private:
    State                    m_state;
    NodeSource               m_nodeSource;
    QHBoxLayout*             m_pMainHBoxLayout;
    QWidget*                 m_pMainWidget;
    QWidget*                 m_pExaminerParent;
    ExaminerViewer*          m_pViewerWidget;
    QAction*                 m_pShowSettings;
    Controls*                m_pControls;
    InfoHelp*                m_pInfoHelp;


    double                   m_Medianl;
    double                   m_MedianPermeability;
    double                   m_q;
    double                   m_r;
    double                   m_s;
    double                   m_viscosity;
    double                   m_pictureTimeInterval;
    double                   m_timeOfLastSnapShot;
    double                   m_maxPermeability;
    double                   m_minPermeability;
    double                   m_translationFactor;
    double                   m_currentTime;
    double                   m_timeStep;
    float                    m_cellSize;

    bool                     m_fRecalculateMedianl;
    bool                     m_fPermeabilityGlobalsInvalid;
    bool                     m_fMadeChangesInCycle;
    bool                     m_fShowSweptNodes;
    bool                     m_fMarkNeighbors;
    bool                     m_fMarkDirectNeighbors;
    bool                     m_fTakePictures;
    bool                     m_fUpdateCameraPosition;

    unsigned int             m_currentNumberOfRelevantNeighbors;
    int                      m_currentCycle;
    int                      m_numNodesFull;

    std::map<int,SimNode*>          m_activeNodes;
    std::map<int,SimNode*>          m_sweptNodes;
    std::map<int,SimTriangle*>      m_triangles;
    std::multimap<double,SimNode*>  m_injectionPoints;
    std::set<SimNode*>              m_markedNodes;

#ifdef DEBUG
    SimNode* m_testNode;
#endif //DEBUG

protected:
    virtual void closeEvent(QCloseEvent* e);

public:
    RTMSimulator(const char* name);
    ~RTMSimulator();
    void init();

    State state();
    void setState(State new_state);

    SimNode* addNode(int nodeId, double x, double y, double z, bool swept=false);
    SimTriangle* addTriangle(int nodeId1, int nodeId2, int nodeId3);
    SimTriangle* addTriangle(int triangleId, SimNode* node1, SimNode* node2, SimNode* node3);
    SimTriangle* addTriangle(TriangleSettings &settings);
    void addQuadrangle(int nodeId1, int nodeId2, int nodeId3, int nodeId4);
    void addInjectionPoint(SimNode* injectionPoint, double injectionPressure);
    void removeInjectionPoint(SimNode* injectionPoint);
    void setDirectNeighbor(int nodeId1, int neighborId2);
    void setShowSweptNodes(bool showsweptNodes);
    void setShowNeighbors(bool showNeighbors);
    void setShowDirectNeighbors(bool showDirectNeighbors);
    void setShowPermeabilityGradient(bool showGradient);

    void calculateMedianCellDistance();
    bool calculateNeighbors();
    bool copyDirectNeighbors();
    bool calculateNeighbors_dat_file();
    bool calculateGradeNeighbors(int numberOfRelevantNeighbors);

    bool sweep(double midDistanceFactor,bool fRecursiveSweep=true);
    void resetSweep();
    bool searchCancer(double midDistanceFactor);
    int sweepCancerNodes();
    void deleteSweptNodes();

    void incrementFullCounter();
    double timeDelta();
    double currentTime();

    void invalidatePermeabilityGlobals();
    void updatePermeabilityGlobals();
    void updatePermeabilityColors();

    double vacuumPressure();
    void setVacuumPressure(double pressure);
    unsigned int numRelevantNeighbors();
    double q();
    double r();
    double s();
    double viscosity();
    double highestInjectionPressure();
    double pictureTimeInterval();
    double timeOfLastSnapShot();
    double medianPermeability();
    double maxPermeability();
    double minPermeability();
    double medianCellDistance();
    double translationFactor();

    float cellSize();
    int currentCycle();
    void setCurrentCycle(int cycle);
    int numNodesFull();
    void setNumNodesFull(int num);

    void render();
    void reset();
    void doCycle();

    /// @returns True if any node changed its pressure during the last cycle.
    bool changesWereMadeInCycle()   { return m_fMadeChangesInCycle; }
    /// @returns True if the flag showSweptNodes is set.
    bool isSetShowSweptNodes()    { return m_fShowSweptNodes; }
    /// @returns True if the flag to mark neighbors of the selected nodes is set.
    bool isSetMarkNeighbors()       { return m_fMarkNeighbors; }
    /// @returns True if the flag to mark direct neighbors of the selected nodes is set.
    bool isSetMarkDirectNeighbors() { return m_fMarkDirectNeighbors; }
    /// @returns True if the flag for taking pictures is set.
    bool isSetTakePictures()        { return m_fTakePictures; }

    void takeSnapShotIfRequired();
    void takeSnapShot(QString);

    SimNode* getNodeById(int nodeId);
    float nodeTransparency();

    std::map<int,SimNode*>* activeNodes();
    std::map<int,SimNode*>* sweptNodes();
    std::multimap<double,SimNode*>* injectionPoints();
    std::map<int,SimTriangle*>* triangles();

    SoCamera* camera();
    void moveCamera(float x, float y, float z);
    void rotateCamera(float val0, float val1, float val2, float val3);

public slots:
    void clear();
    void toggleSettingsTab();
    void importFile(QString);
    void exportToFile(QString);
    void updateSelectionData();
    void testForLonesomeNodes();
    void setQ(double);
    void setR(double);
    void setS(double);
    void setViscosity(double);
    void takePictures(bool);
    void setPictureTimeInterval(double);
    void setTimeOfLastSnapShot(double);
    void setCurrentTime(double);
    void setTranslationFactor(double);
    void showSweptNodes(int toggleState);
    void showNeighbors(int toggleState);
    void showDirectNeighbors(int toggleState);
    void showPermeabilityGradient(int toggleState);

    void startSimulation();
    void pauseSimulation();
    void stepSimulation();
    void stepSimulation(int nCycles);
    void stopSimulation();
    void createSnapShot();
    void startLonesomeNodesTest();
    void setCellSize(float size);
    void rescaleTranslations(double factor);
    void recalculateNeighborDistances();

    void showInfoHelp();
    void open_clicked();
    void save_clicked();


#ifdef DEBUG
    void printNodeTable();
    void writeNodeSettingsToFile(std::ofstream& file, std::map<int,SimNode*>* nodes);
    void createTestNode();
    void deleteTestNode();
    void createTestShape();
    void setCellSize4();
    void moveCam();
    void testMultipleTriangleStrips();
#endif //_DEBUG

signals:
    void startButtonEnabled(bool);
    void stopButtonEnabled(bool);
    void pauseButtonEnabled(bool);
    void stepButtonEnabled(bool);

};

#endif //RTMSIMULATOR_H

