// File SimNode.h by az

#ifndef SIMNODE_H
#define SIMNODE_H

#include "globals.h"
#include <Inventor/nodes/SoSeparator.h>
#include <map>
#include <set>
#include <vector>

class SoMaterial;
class RTMSimulator;
class SoTranslation;
class SbColor;
class SoCube;
class SoBaseColor;
class SimTriangle;

/**
 * @brief Class that is used to create simulation nodes for the RTMSimulator simulation process.
 *
 * Each node is displayed in the viewer widget. Further it holds a lot of information such as
 * fill state, pressure, porosity, cavity height, permeability, etc.
 * Together with the RTMSimulator class these are the most important classes for this application.
 */
class SimNode : public SoSeparator
{
public:
    /** Enumeration that is being used by the state machine, to define this node as empty, half full or full */
    enum State { EMPTY, HALF, FULL };

private:
    int m_nodeId;
    /** The Limit from which on a cell is HALF filled (Pascal) */
    const double s_halfLimit;
    /** The default pressure in a Node */
    const double s_defaultPressure;
    /** The default permeability in a Node */
    const double s_defaultPermeability;
    /** The default cavity height of a Node */
    const double s_defaultCavityHeight;
    /** The default porosity of a Node */
    const double s_defaultPorosity;
    const double s_defaultInjectionDiameter;
    /** The default injection pressure (Pressure of the injection at an injection point) */
    const double s_defaultInjectionPressure;

    static SoType       myClassTypeId;
    SoMaterial*         m_pMaterial;
    SoTranslation*      m_pTranslation;
    SoBaseColor*        m_pColor;
    RTMSimulator*       m_pSimulator;
    SoCube*             m_cube;

    double m_x;
    double m_y;
    double m_z;

    /** We don't want anybody to use this constructor */
    SimNode();

    /** A member of type State that stores the current state of this Node. (Works as a state machine) */
    State m_state;
    State m_memorizedState;
    /** The current permeability of this Node */
    double m_permeability;
    /** The current cavity height of this Node */
    double m_cavityHeight;
    /** The current porosity of this Node */
    double m_porosity;
    /** The current pressure of this Node */
    double m_pressure;
    /** The memorized pressure of this Node \n This will be copied to m_pressure in next cycle */
    double m_memorizedPressure;
    /** The injection pressure of this Node. \n This member is only interesting, if this is an injection point at all! */
    double m_injectionPressure;
    /** The diameter of the injection in this Node \n This member is only interesting, if this is an injection point at all! */
    double m_injectionDiameter;
    /** TRUE, if this Node was defined as an injection point */
    bool m_fInjectionPoint;
    bool m_fMarkedForSweep;
    bool m_fMarkedGreen;

    int m_permeabilityColor;

    double m_distanceToMostFarNeighbor;

    std::multimap<double /*distance*/,SimNode*> m_neighbors;
    std::set<SimNode*> m_directNeighbors;
    std::vector<SimNode*> m_gradeNeighbors;
    std::set<SimNode*> m_sweptNeighbors;
    std::set<SimNode*> m_nodesThatReferenceMe;
    std::set<SimNode*> m_adoptedDirectNeighbors; //neighbors that were copied from direct neighbors' neighbors-list
    std::set<SimTriangle*> m_adjacentTriangles;

public:
    SimNode(int id, double x, double y, double z, RTMSimulator* pSimulator);

    static void initClass();
    static SoType getClassTypeId();
    double getX() const;
    double getY() const;
    double getZ() const;
    int id();
    void setCellSize(float size);

    double  getPermeability();
    double  getCavityHeight();
    double  getPorosity();
    double  getPressure();
    double  getInjectionPressure();
    double  getInjectionDiameter();
    bool    isInjectionPoint();
    int     getNumOfNeighbors();
    State   getState();

    void setPermeability( double new_permeability );
    void setCavityHeight( double new_cavityHeight );
    void setPorosity( double new_porosity );
    void setInjectionDiameter( double injection_diameter );
    bool addNeighbor( SimNode* new_neighbor);
    bool addNeighbor( SimNode* new_neighbor, double distance);
    bool addAdoptedDirectNeighbor( SimNode* );
    void addAdjacentTriangle( SimTriangle* new_triangle );
    int copyGradeNeighbors(int grade);
    int commitGradeNeighbors(unsigned int numberOfRelevantNeighbors);
    bool copyDirectNeighbors();
    void resetTriangleAdjacence();
    void adoptDirectNeighborsFrom(SimNode* sourceNode);
    void removeMostFarNeighbor();
    bool hasDirectNeighborConnectionTo(SimNode* neighborToCheck);
    void markForSweep(bool);
    void sweep();
    bool isMarkedForSweep();
    void markGreen(bool);
    bool isMarkedGreen();
    void addReference( SimNode* nodeThatCallsMeNeighbor);
    void removeReference( SimNode* nodeThatStopsCallingMeNeighbor);
    void removeDirectNeighbor(SimNode* nodeToRemoveFromDirectNeighborList);
    void removeSweptNeighbor(SimNode* nodeToRemoveFromSweptNeighborList);
    void removeAdoptedNeighbor(SimNode* nodeToRemoveFromAdoptedNeighborList);
    void removeAllReferences();
    void moveReferenceToSweptNeighbors(SimNode*);
    void clearNeighbors();
    void clearAdoptedDirectNeighbors();
    void clearGradeNeighbors();
    void clearSweptNeighbors();
    void clearReferences();
    void resetNeighborSettings();
    void searchCancerNodes(double minDistance);
    void recalculateNeighborDistances();

    std::multimap<double,SimNode*>* getNeighbors();
    std::set<SimNode*>* getDirectNeighbors();
    std::vector<SimNode*>* getGradeNeighbors();
    std::set<SimNode*>* getSweptNeighbors();
    std::set<SimNode*>* getNodesThatReferenceMe();
    std::set<SimNode*>* getAdoptedDirectNeighbors();
    void addDirectNeighbor( SimNode* new_direct_neighbor);
    void clearDirectNeighbors();
    void resetPressure();

    void calculateNextPressure();
    void memorizePressure(double new_pressure);
    bool commitPressure();
    void setInjectionPoint(bool fIsInjectionPoint,double injectionPressure);
    void setInjectionPointSettings(double injection_pressure, double injectionDiameter, double medianl);
    void updateNodeSettings(double injectionPressure, double injectionDiameter, double permeability, double cavityHeight, double porosity, double medianl);
    void updateNodeSettings(bool fIsInjectionPoint, double injectionPressure, double injectionDiameter, double permeability, double cavityHeight, double porosity, double medianl);
    void setState( State new_state );
    void memorizeState( State new_state );
    void commitState();
    void setColor(SbColor color);
    void setTransparent(bool);
    void setPermeabilityColor(int colorGradientValue);
    void displayPermeabilityColor(bool displayPermeability);
    void updateColor();
    void reset();
    void rescaleTranslation(double factor);

    virtual SoType getTypeId(void) const;


/**
 * This static function can be used, to calculate the distance between two Nodes. \n
 * @param node1 guess what.... .... yes... a Node!
 * @param node2 guess what.... .... yes... another Node!
 * @return The distance between the nodes in unit that is used by the plot m_pPlot
 */
    static double getDistance(const SimNode* node1, const SimNode* node2)
    {
        double a = fabs(node1->getX() - node2->getX());
        double b = fabs(node1->getY() - node2->getY());
        double c = fabs(node1->getZ() - node2->getZ());
        return sqrt(pow(a,2)+pow(b,2)+pow(c,2))/*/STATIC_STRETCH_FACTOR*//1000; //"static stretching factor" = 5.0 ; we want to have m, but model is mm based => /1000

    }

/**
 * @returns True if the node specified by parameter is full.
 * @param node Node to check for state.
 */
    static bool	isFull(SimNode* node)		{ return node->m_state == SimNode::FULL; }

/**
 * @returns True if the node specified by parameter is half full.
 * @param node Node to check for state.
 */
    static bool	isHalfFull(SimNode* node)	{
        SimNode::State state = node->m_state;
        return (state == SimNode::FULL || state == SimNode::HALF);
    }
};

/**
 * @brief Functor class for stl algorithms to mark or unmark a SimNode.
 */
class MarkGreen
{
    bool mark;
public:
    explicit MarkGreen(bool m) : mark(m) {}
    void operator() (SimNode* simnode) {
        simnode->markGreen(mark);
    }
};

/**
 * @brief Functor class for stl algorithms to update the color of a SimNode from a pair.
 */
class UpdateColor
{
public:
    explicit UpdateColor() {}
    void operator() (std::pair<int,SimNode*> simnode_pair) {
        simnode_pair.second->updateColor();
    }
};

/**
 * @brief Functor class for stl algorithms to switch from permeability gradient color to usual color, vice versa.
 */
class DisplayPermColor
{
    bool perm;
public:
    explicit DisplayPermColor(bool p) : perm(p) {}
    void operator() (std::pair<int,SimNode*> simnode_pair) {
        simnode_pair.second->displayPermeabilityColor(perm);
    }
};

/**
 * @brief Functor class for stl algorithms to execute removeAllReferences() function on a SimNode from a pair.
 */
class RemAllRef
{
public:
    explicit RemAllRef() {}
    void operator() (std::pair<int,SimNode*> simnode_pair) {
        simnode_pair.second->removeAllReferences();
    }
};

/**
 * @brief Functor class for stl algorithms to mark or unmark a node for sweeping.
 */
class MarkForSweep
{
    bool mark;
public:
    explicit MarkForSweep(bool m) : mark(m) {}
    void operator() (std::pair<int,SimNode*> simnode_pair) {
        simnode_pair.second->markForSweep(mark);
    }
};

/**
 * @brief Functor class for stl algorithms to execute resetNeighborSettings() on a node from a pair.
 */
class ResetNeighborSettings
{
public:
    explicit ResetNeighborSettings() {}
    void operator() (std::pair<int,SimNode*> simnode_pair) {
        simnode_pair.second->resetNeighborSettings();
    }
};

/**
 * @brief Functor class for stl algorithms to execute copyDirectNeighbors() and resetTriangleAdjacence() on a node from a pair.
 */
class ResetTriangleAdjacence
{
public:
    explicit ResetTriangleAdjacence() {}
    void operator() (std::pair<int,SimNode*> simnode_pair) {
        simnode_pair.second->copyDirectNeighbors();
        simnode_pair.second->resetTriangleAdjacence();
    }
};

/**
 * @brief Functor class for stl algorithms to raise the state of a SimNode out of a pair from EMPTY to HALF.
 */
class RaiseFromEmptyToHalf
{
public:
    explicit RaiseFromEmptyToHalf() {}
    void operator() (std::pair<int,SimNode*> simnode_pair) {
         if(simnode_pair.second->getState() == SimNode::EMPTY)
         {
             simnode_pair.second->memorizeState(SimNode::HALF);
         }
    }
};

/**
 * @brief Functor class for stl algorithms to execute reset on a SimNode from a pair.
 */
class ResetSimNode
{
public:
    explicit ResetSimNode() {}
    void operator() (std::pair<int,SimNode*> simnode_pair) {
         simnode_pair.second->reset();
    }
};

/**
 * @brief Functor class for stl algorithms to change the size of a SimNode from a pair.
 */
class SetCellSize
{
    float size;
public:
    explicit SetCellSize(float s) : size(s) {}
    void operator() (std::pair<int,SimNode*> simnode_pair) {
         simnode_pair.second->setCellSize(size);
    }
};

/**
 * @brief Functor class for stl algorithms to rescale the translations of a SimNode from a pair.
 */
class RescaleTranslation
{
    double factor;
public:
    explicit RescaleTranslation(double f) : factor(f) {}
    void operator() (std::pair<int,SimNode*> simnode_pair) {
         simnode_pair.second->rescaleTranslation(factor);
    }
};

/**
 * @brief Functor class for stl algorithms to recalculate the neighbor distances of a SimNode from a pair.
 */
class RecalcNeighborDist
{
public:
    explicit RecalcNeighborDist() {}
    void operator() (std::pair<int,SimNode*> simnode_pair) {
         simnode_pair.second->recalculateNeighborDistances();
    }
};



#endif //SIMNODE_H
