#include <qglobal.h>

#include "SimNode.h"
#include "SimTriangle.h"
#include "RTMSimulator.h"

#include <Inventor/nodes/SoTranslation.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoCube.h>
#include <Inventor/nodes/SoBaseColor.h>

#include <iostream>

SoType SimNode::myClassTypeId;

/**
 * Creates a new simulation node and displays this node in the viewer widget of the RTMSimulator.
 * @param id ID for the new node
 * @param x X coordinate of the new node
 * @param y Y coordinate of the new node
 * @param z Z coordinate of the new node
 * @param pSimulator Pointer to the RTMSimulator to work with
 */
SimNode::SimNode(int id, double x, double y, double z, RTMSimulator* pSimulator):
        m_nodeId(id),
        s_halfLimit(0),
        s_defaultPressure(0),
        s_defaultPermeability(0.000000002),
        s_defaultCavityHeight(0.002),
        s_defaultPorosity(0.5),
        s_defaultInjectionDiameter(0.01),
        s_defaultInjectionPressure(500000),

        m_pSimulator(pSimulator),m_x(x),m_y(y),m_z(z),
        m_state(SimNode::EMPTY),
        m_memorizedState(SimNode::EMPTY),
        m_permeability(s_defaultPermeability),
        m_cavityHeight(s_defaultCavityHeight),
        m_porosity(s_defaultPorosity),
        m_pressure(s_defaultPressure),
        m_memorizedPressure(s_defaultPressure),
        m_injectionPressure(s_defaultInjectionPressure),
        m_injectionDiameter(s_defaultInjectionDiameter),
        m_fInjectionPoint(FALSE),
        m_fMarkedForSweep(FALSE),
        m_fMarkedGreen(FALSE),
        m_permeabilityColor(0),
        m_distanceToMostFarNeighbor(0)
{
    m_pMaterial=NULL;
    m_pTranslation = new SoTranslation;
    m_pTranslation->translation.setValue(x*STATIC_STRETCH_FACTOR, y*STATIC_STRETCH_FACTOR, z*STATIC_STRETCH_FACTOR);
    addChild(m_pTranslation);

    m_pColor = new SoBaseColor;
    m_pColor->rgb = SbColor(0.82f, 0.82f, 0.82f); //grey
    addChild(m_pColor);

    m_cube = new SoCube;
    m_cube->width = 15;
    m_cube->height = 15;
    m_cube->depth = 15;
    addChild(m_cube);
}

/**
 * Defines the width, height, depth of this node
 * @param size New size of the node
 */
void SimNode::setCellSize(float size)
{
    m_cube->width = size;
    m_cube->height = size;
    m_cube->depth = size;
}

/**
 * Initializes the ClassTypeId for this class. This is needed to recognize nodes of this type in the scene graph.
 */
void SimNode::initClass()
{
    SimNode::myClassTypeId = SoType::createType(SoSeparator::getClassTypeId(),"SimNode");
}

/**
 * @returns ClassTypeId for an instance of this class
 */
SoType SimNode::getClassTypeId()
{
    return SimNode::myClassTypeId;
}

/**
 * @returns ClassTypeId for an instance of this class
 */
SoType SimNode::getTypeId(void) const
{
    return SimNode::myClassTypeId;
}

/**
 * @returns X coordinate in the scene graph of this node
 */
double SimNode::getX() const
{
    return m_x;
}

/**
 * @returns Y coordinate in the scene graph of this node
 */
double SimNode::getY() const
{
    return m_y;
}

/**
 * @returns Z coordinate in the scene graph of this node
 */
double SimNode::getZ() const
{
    return m_z;
}

/**
 * @returns Unique ID of this node
 */
int SimNode::id()
{
    return m_nodeId;
}

/** @return The current permeability of this Node */
double  SimNode::getPermeability() { return m_permeability; }

/** @return The current cavity height of this Node */
double  SimNode::getCavityHeight() { return m_cavityHeight; }

/** @return The current porosity of this Node */
double  SimNode::getPorosity() { return m_porosity; }

/** @return The current pressure inside this Node */
double  SimNode::getPressure() { return m_pressure; }

/** @return The current injection pressure of this Node. \n This value is only interesting, if this is an injection point at all */
double  SimNode::getInjectionPressure() { return m_injectionPressure; }

/** @return The current diameter of the injection at this Node. \n This value is only interesting, if this Node was defined as an injection point  */
double  SimNode::getInjectionDiameter() { return m_injectionDiameter; }

/** @return TRUE if this Node was defined as an injection point */
bool    SimNode::isInjectionPoint() { return m_fInjectionPoint; }

/** @return The number of neighbors that are stored in the vector m_neighbors */
int     SimNode::getNumOfNeighbors() { return m_neighbors.size(); }

/** @return The current state of this Node */
SimNode::State SimNode::getState(){ return m_state; }

/**
* This function is used to update the current permeability of this Node. Changes will take effect immediately!
* @param new_permeability The new permeability for this Node.
*/
void SimNode::setPermeability( double new_permeability ) { m_permeability = new_permeability; }
/**
* This function is used to update the current cavity height of this Node. Changes will take effect immediately!
* @param new_cavityHeight The new cavity height for this Node.
*/
void SimNode::setCavityHeight( double new_cavityHeight ) { m_cavityHeight = new_cavityHeight; }
/**
* This function is used to update the current porosity of this Node. Changes will take effect immediately!
* @param new_porosity The new porosity for this Node.
*/
void SimNode::setPorosity( double new_porosity ) { m_porosity = new_porosity; }

void SimNode::setInjectionDiameter( double injection_diameter ) { m_injectionDiameter = injection_diameter; }
/**
* This function is used to add a pointer of a neighbor-Node to the m_neighbors map of this Node.
* @param new_neighbor Pointer to the neighbor that should be added.
* @param distance Distance to the neighbor
*/
bool SimNode::addNeighbor( SimNode* new_neighbor, double distance)
{
    if(new_neighbor != this)
    {
        for(std::multimap<double,SimNode*>::const_iterator it = m_neighbors.begin(); it != m_neighbors.end(); it++)
        {
            if((*it).second == new_neighbor)
                return false;     //we already have this Node in the map, so we don't need to insert it once again
        }

        for(std::set<SimNode*>::const_iterator it = m_sweptNeighbors.begin(); it != m_sweptNeighbors.end(); it++)
        {
            if((*it) == new_neighbor)
                return false;     //we already have this Node in the map, so we don't need to insert it once again
        }

        if(!new_neighbor->isMarkedForSweep())
        {
            m_neighbors.insert(std::make_pair(distance,new_neighbor));
        }
        else
        {
            m_sweptNeighbors.insert(new_neighbor);
        }
        new_neighbor->addReference(this); //add myself to the reference count of the new neighbor. So this node knows that he needs to inform me, if he is being swept.
        if(m_neighbors.size()>0)
        {
            m_distanceToMostFarNeighbor = (*(m_neighbors.rbegin())).first;
        }
        return true;
    }
    else
    {
        return false;
    }
}

/**
* This function is used to add a pointer of a neighbor-Node to the m_neighbors map of this Node.
* It automatically calculates the distance to the neighbor and adds it to the m_neighbors map.
* @param new_neighbor Pointer to the neighbor that should be added.
*/
bool SimNode::addNeighbor( SimNode* new_neighbor)
{
    return addNeighbor(new_neighbor, getDistance(this,new_neighbor));
}

/**
 * Removes the neighbor from the m_neighbors map which is most far away.
 */
void SimNode::removeMostFarNeighbor()
{
    if(m_neighbors.size() > 0)
    {
        std::multimap<double,SimNode*>::reverse_iterator it = m_neighbors.rbegin();
        //directNeighbors have more priority, cause they are always closer. So we try to delete other nodes first!
        while(hasDirectNeighborConnectionTo((*it).second))
        {
            it++;
            if(it == m_neighbors.rend())
                break;
        }


        if(it == m_neighbors.rend())
        {
            //seems we still have too many neighbors, and deleted all gradedNeighbors.
            //so we reset it to the most far direct neighbor, to delete this one!
            it = m_neighbors.rbegin();
        }

        (*it).second->removeReference(this);
        m_neighbors.erase(--it.base());


        if(m_neighbors.size()>0)
            m_distanceToMostFarNeighbor = (*(m_neighbors.rbegin())).first;
    }
}

/**
 * @returns True if neighborToCheck is a direct neighbor of this node.
 */
bool SimNode::hasDirectNeighborConnectionTo(SimNode* neighborToCheck)
{
    for(std::set<SimNode*>::iterator it = m_directNeighbors.begin(); it != m_directNeighbors.end(); it++)
    {
        if((*it) == neighborToCheck)
            return TRUE;
    }
    return FALSE;
}

/**
 * Marks or unmarks this node for sweeping according to the provided parameter.
 * @param fMarkForSweep If true, this node will be marked for sweeping and the color of this node will be updated.
 */
void SimNode::markForSweep(bool fMarkForSweep)
{
    m_fMarkedForSweep = fMarkForSweep;
    updateColor();
}

/**
 * @returns True if this node is marked for sweeping.
 */
bool SimNode::isMarkedForSweep()
{
    return m_fMarkedForSweep;
}

/**
 * Marks or unmarks this node with green color.
 * @param mark If true, this node will become green colored.
 */
void SimNode::markGreen(bool mark)
{
    m_fMarkedGreen = mark;
    updateColor();
}

/**
 * @returns True if this node is marked green.
 */
bool SimNode::isMarkedGreen()
{
    return m_fMarkedGreen;
}

/**
 * Marks all direct neighbor connections for adoption.
 * Forces all nodes that are calling it a neighbor to change this relationship to a swept-neighbor relationship.
 */
void SimNode::sweep()
{
    //before we sweep this node we should copy the direct connections to the nearest neighbor of this node
    for(std::set<SimNode*>::iterator it = m_directNeighbors.begin(); it != m_directNeighbors.end(); it++)
    {
        SimNode* node = (*it);
        if(!node->isMarkedForSweep())
        {
            node->adoptDirectNeighborsFrom(this);
            break;
        }
    }

    for(std::set<SimNode*>::iterator it = m_nodesThatReferenceMe.begin(); it != m_nodesThatReferenceMe.end(); it++)
    {
        (*it)->moveReferenceToSweptNeighbors(this);
    }
}

/**
 * Adds a new node to the m_directNeighbors set of this node.
 * @param new_direct_neighbor The node to add to the neighbor set of this node
 */
void SimNode::addDirectNeighbor( SimNode* new_direct_neighbor)
{
    if(new_direct_neighbor != this)
    {
        m_directNeighbors.insert(new_direct_neighbor);
        new_direct_neighbor->addReference(this); //add myself to the reference count of the new neighbor. So this node knows that he needs to inform me, if he is being swept.
    }
}

/**
 * Adds a new node to the m_adoptedDirectNeighbors set of this node.
 * @param new_adopted_direct_neighbor Node to add to neighbor set
 */
bool SimNode::addAdoptedDirectNeighbor( SimNode* new_adopted_direct_neighbor )
{
    if(new_adopted_direct_neighbor != this)
    {
        m_adoptedDirectNeighbors.insert(new_adopted_direct_neighbor);
        new_adopted_direct_neighbor->addReference(this);
        return true;
    }
    return false;
}

/**
 * Adds triangle to the adjacence set of this node.
 * @param new_triangle Triangle to add to the m_adjacentTriangles set of this node
 */
void SimNode::addAdjacentTriangle( SimTriangle* new_triangle )
{
    m_adjacentTriangles.insert(new_triangle);
}

/**
 * Adds a node to the set of nodes that call this node a neighbor.
 * @param nodeThatCallsMeNeighbor Node to add to the set
 */
void SimNode::addReference( SimNode* nodeThatCallsMeNeighbor)
{
    m_nodesThatReferenceMe.insert(nodeThatCallsMeNeighbor);
}

/**
 * Removes a node from the set of nodes that call this node a neighbor.
 * @param nodeThatStopsCallingMeNeighbor Node to remove from the set
 */
void SimNode::removeReference( SimNode* nodeThatStopsCallingMeNeighbor)
{
    std::set<SimNode*>::iterator it = m_nodesThatReferenceMe.find(nodeThatStopsCallingMeNeighbor);
    if(it != m_nodesThatReferenceMe.end())
    {
        m_nodesThatReferenceMe.erase(it);
    }
}

/**
 * Removes a node from the set of direct neighbors.
 * @param nodeToRemove Node to remove from the set
 */
void SimNode::removeDirectNeighbor(SimNode* nodeToRemove)
{
    std::set<SimNode*>::iterator it = m_directNeighbors.find(nodeToRemove);
    if(it != m_directNeighbors.end())
    {
        m_directNeighbors.erase(it);
    }
}

/**
 * Removes a node from the set of swept neighbors.
 * @param nodeToRemove Node to remove from set
 */
void SimNode::removeSweptNeighbor(SimNode* nodeToRemove)
{
    std::set<SimNode*>::iterator it = m_sweptNeighbors.find(nodeToRemove);
    if(it != m_sweptNeighbors.end())
    {
        m_sweptNeighbors.erase(it);
    }
}

/**
 * Removes a node from the adopted direct neighbors set.
 * @param nodeToRemove Node to remove from set
 */
void SimNode::removeAdoptedNeighbor(SimNode* nodeToRemove)
{
    std::set<SimNode*>::iterator it = m_adoptedDirectNeighbors.find(nodeToRemove);
    if(it != m_adoptedDirectNeighbors.end())
    {
        m_adoptedDirectNeighbors.erase(it);
    }
}

/**
 * Removes this node from all neighbor sets of all other nodes and from the triangle vertex list as well.
 * This should be executed before destroying this node, otherwise you might experience dangling pointers.
 */
void SimNode::removeAllReferences()
{
    for(std::set<SimNode*>::iterator it = m_nodesThatReferenceMe.begin(); it != m_nodesThatReferenceMe.end(); it++)
    {
        SimNode* node = *it;
        node->removeDirectNeighbor(this);
        node->removeSweptNeighbor(this);
        node->removeAdoptedNeighbor(this);
    }
    for(std::set<SimTriangle*>::iterator it = m_adjacentTriangles.begin(); it != m_adjacentTriangles.end(); it++)
    {
        SimTriangle* triangle = *it;
        triangle->removeVertexNode(this);
        std::multimap<double,SimNode*>* neighbors = this->getNeighbors();
        for(std::multimap<double,SimNode*>::iterator it = neighbors->begin(); it != neighbors->end(); it++)
        {
            triangle->addVertexNode((*it).second);
        }
    }
    m_nodesThatReferenceMe.clear();
}

/**
 * Removes the specified node from the map of neighbors and adds it to the set of swept neighbors.
 * @param node Node to move to swept neighbors
 */
void SimNode::moveReferenceToSweptNeighbors(SimNode* node)
{
    for(std::multimap<double,SimNode*>::iterator it = m_neighbors.begin(); it != m_neighbors.end(); it++)
    {
        if((*it).second == node)
        {
            m_sweptNeighbors.insert((*it).second);
            m_neighbors.erase(it);
            return;
        }
    }
}

/**
 * Searches for nodes in the neighbors map that have a smaller distance to this node than the provided value,
 * and marks this node for sweeping.
 * @param minDistance Minimal distance to search
 */
void SimNode::searchCancerNodes(double minDistance)
{
    if(!isMarkedForSweep())
    {
        for(std::multimap<double,SimNode*>::iterator it = m_neighbors.begin(); it != m_neighbors.end(); it++)
        {
            if((*it).first > minDistance)
                break;
            else
            {
                // if the neighbor isn't an injection point we mark it for sweep
                // if this and neighbor are injection points we mark the neighbor for sweep as well
                if(( !(*it).second->isInjectionPoint()) || (isInjectionPoint() && (*it).second->isInjectionPoint()))
                {
                    (*it).second->markForSweep(TRUE);
                }
            }
        }
    }
}

/**
 * Recalculates the distances to all the neighbors of this node.
 */
void SimNode::recalculateNeighborDistances()
{
    std::multimap<double,SimNode*> newNeighbors;
    for(std::multimap<double,SimNode*>::iterator it = m_neighbors.begin(); it != m_neighbors.end(); it++)
    {
        newNeighbors.insert(std::make_pair<double,SimNode*>(getDistance((*it).second,this),(*it).second));
    }
    m_neighbors.clear();

    m_neighbors.insert(newNeighbors.begin(),newNeighbors.end());
}

/**
* This function can be used, to clear the vector m_neighbors of this Node. \n
* All neighbor relations of this node will be removed!
*/
void SimNode::clearNeighbors() { m_neighbors.clear(); }
void SimNode::clearGradeNeighbors() { m_gradeNeighbors.clear(); }
void SimNode::clearSweptNeighbors() { m_sweptNeighbors.clear(); }
void SimNode::clearReferences() { m_nodesThatReferenceMe.clear(); }
void SimNode::clearAdoptedDirectNeighbors() {
    m_adoptedDirectNeighbors.clear();
}

/**
 * Resets all neighbor settings. This means all maps and sets that hold neighbor information will be cleared.
 */
void SimNode::resetNeighborSettings()
{
    clearNeighbors();
    clearGradeNeighbors();
    clearAdoptedDirectNeighbors();
    clearSweptNeighbors();
    clearReferences();
}

/**
 * Clears the set that holds the pointers to all the direct neighbors.
 */
void SimNode::clearDirectNeighbors() { m_directNeighbors.clear(); }

/**
* This function is used to reset the current pressure of this Node to default values. Changes will take effect immediately!
*/
void SimNode::resetPressure() { memorizePressure(s_defaultPressure); commitPressure(); }

/**
 * This function can be used to change/initialize general settings of this Node
 * @param fIsInjectionPoint TRUE, if this Node should be defined as an injection point, FALSE otherwise
 * @param injection_pressure pressure of the injection at this point (not equal to the pressure inside this Node)
 * @param injectionDiameter diameter of the injection at this point
 * @param permeability New value for the permeability of this Node
 * @param cavityHeight New value for the cavity height of this Node
 * @param porosity New value for the porosity of this Node
 */
void SimNode::updateNodeSettings(bool fIsInjectionPoint, double injectionPressure, double injectionDiameter, double permeability, double cavityHeight, double porosity, double medianl)
{
    setInjectionPoint(fIsInjectionPoint,injectionPressure);
    setInjectionPointSettings(injectionPressure,injectionDiameter,medianl);

    setPermeability(permeability);
    setCavityHeight(cavityHeight);
    setPorosity(porosity);
}

/**
 * This function can be used to change/initialize general settings of this Node
 * @param injection_pressure pressure of the injection at this point (not equal to the pressure inside this Node)
 * @param injectionDiameter diameter of the injection at this point
 * @param permeability New value for the permeability of this Node
 * @param cavityHeight New value for the cavity height of this Node
 * @param porosity New value for the porosity of this Node
 */
void SimNode::updateNodeSettings(double injectionPressure, double injectionDiameter, double permeability, double cavityHeight, double porosity, double medianl)
{
    setInjectionPointSettings(injectionPressure,injectionDiameter,medianl);
    setPermeability(permeability);
    setCavityHeight(cavityHeight);
    setPorosity(porosity);
}

/**
 * Defines injection settings for this node.
 * @param fIsInjectionPoint If true this node will become an injection point.
 * @param injectionPressure Injection pressure to be used for this injection point, if it is an injection point at all.
 */
void SimNode::setInjectionPoint(bool fIsInjectionPoint, double injectionPressure)
{
    if(fIsInjectionPoint && !m_fInjectionPoint)
    {
        //this becomes a new injection point
        m_pSimulator->addInjectionPoint(this,injectionPressure);
    }
    else if(!fIsInjectionPoint && m_fInjectionPoint)
    {
        //this becomes a usual point (not an injection point anymore)
        m_pSimulator->removeInjectionPoint(this);
    }
    m_fInjectionPoint = fIsInjectionPoint;
}

/**
 * Changes the injection settings for this node, if this is an injection point at all.
 * @param injection_pressure Pressure to use as an injection pressure for this node
 * @param injectionDiameter Diameter for the injection
 * @param medianl Average cell distance (This value is neede for the pressure calculation)
 */
void SimNode::setInjectionPointSettings(double injection_pressure, double injectionDiameter, double medianl)
{
    m_injectionPressure = injection_pressure;
    m_injectionDiameter = injectionDiameter;
    //calculation of injection pressure
    double calculated_pressure = s_defaultPressure;
    if(m_fInjectionPoint)
        calculated_pressure = injection_pressure*medianl/(medianl-(injectionDiameter/(double)2));
    memorizePressure(calculated_pressure);
    commitPressure();
}

/**
 * When we iterate trough all nodes, we always first need to calculate the new pressure
 * and memorize it. This is what we use this function for.
 * When we finished the calculation for all nodes in a cycle and memorized all the new pressures,
 * we can call commitPressure() to replace the pressure in a node with the memorized pressure.
 * @param new_pressure The new pressure to memorize
 */
void SimNode::memorizePressure(double new_pressure)
{
    m_memorizedPressure = new_pressure;
}

/**
 * This function is only used to update the currend pressure with the value of the previously memorized pressure.
 */
bool SimNode::commitPressure()
{
    if(m_pressure != m_memorizedPressure)
    {
        m_pressure = m_memorizedPressure;
        if(m_pressure > s_halfLimit)
        {
            if(m_pressure >= m_pSimulator->vacuumPressure())
            {
                if(getState() == SimNode::FULL)
                {
                    return FALSE;
                }
                else
                {
                    setState(FULL);
                }
            }
            else
            {
                setState(HALF);
            }
        }
        else
        {
            setState(EMPTY);
        }
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

/**
  * This function is used to update the state machine of this Node and set this Node either to EMPTY, HALF or FULL
  * @param new_state The new State for this Node
  */
void SimNode::setState( State new_state )
{
    m_state = new_state;
    if(new_state == SimNode::FULL && !m_fInjectionPoint)
    {
        m_pSimulator->incrementFullCounter();
    }
    m_memorizedState = new_state;
    updateColor();
}

/**
 * Memorizes the state for this simulation node to be updated in next cycle.
 * @param new_state New simulation state for this node
 */
void SimNode::memorizeState( State new_state )
{
    m_memorizedState = new_state;
}

/**
 * Makes the previously memorized state of this node become the current simulation state for this node.
 */
void SimNode::commitState()
{
    setState(m_memorizedState);
}

/**
 * Changes the color for this simulation node.
 * @param color New color for this node
 */
void SimNode::setColor(SbColor color)
{
    m_pColor->rgb = color;
}

/**
 * Sets or resets full transparency for this node.
 * @param fTransparent If true, this node will be completely transparent.
 */
void SimNode::setTransparent(bool fTransparent)
{

    if(fTransparent)
    {
        m_pMaterial = new SoMaterial;
        m_pMaterial->transparency = 1.0;
        addChild(m_pMaterial);
    }
    else
    {

        if(m_pMaterial != NULL)
        {
            m_pMaterial->transparency.setValue(m_pSimulator->nodeTransparency());
        }
    }
}

/**
 * Updates the color for this node when a permeability gradient is being displayed.
 * @param colorGradientValue Gradient value for this node (0-100)
 */
void SimNode::setPermeabilityColor(int colorGradientValue)
{
    m_permeabilityColor = colorGradientValue;
}

void SimNode::displayPermeabilityColor(bool displayPermeability)
{
    if(displayPermeability)
    {
        float red = 0.0f;
        float green = 0.0f;
        float blue = (1.0f/255.0f)*(m_permeabilityColor*2+55);
        SbColor color(red, green, blue);
        setColor(color);
    }
    else
    {
        updateColor();
    }
}

/**
  * This private function is being used to update the display color of this node according to the state machine m_state
  */
void SimNode::updateColor()
{
    SbColor color(0.82f, 0.82f, 0.82f);

    if(!m_fInjectionPoint)
    {
        switch(m_state)
        {
        case EMPTY :
            if( m_cavityHeight != s_defaultCavityHeight ||
                m_permeability != s_defaultPermeability ||
                m_porosity != s_defaultPorosity)
            {
                //we just change the color if this point was initialized with different values than the default values
                //otherwise we just leave the gray color
                color = SbColor(0.0f, 0.0f, 1.0f);

            }
            break;
        case FULL :
            color = SbColor(1.0f, 0.0f, 0.0f);
            break;
        case HALF :
            {
#pragma message("REPLACE THIS BOOLEAN WITH A CHECKBOX FILLED GLOBAL BOOLEAN")
                bool displayPressureFront = false;
                if(displayPressureFront)
                {
                    color = SbColor(1.0f, 1.0f, 0.0f);
                }
            }
            break;
        default : break;
    }
    }
    else
    {
        color = SbColor(1.0f,0.0f,1.0f);
    }

    if(m_fMarkedForSweep)
    {
        color = SbColor(0.0f,0.4f,1.0f);
        setTransparent(!m_pSimulator->isSetShowSweptNodes());
    }
    else
    {
        setTransparent(false);
    }

    if(m_fMarkedGreen)
        color = SbColor(0.0f,1.0f,0.0f);

    setColor(color);
}

/**
 * Extends the neighbors map with nodes that are neighbors to the neighbors of this node.
 * @param grade Depth to build a neighbors tree to find new neighbors
 */
int SimNode::copyGradeNeighbors(int grade)
{
    for(std::multimap<double,SimNode*>::iterator it = m_neighbors.begin(); it != m_neighbors.end(); it++)
    {
        std::multimap<double,SimNode*>* p_neighborsOfNeighbor = (*it).second->getNeighbors();
        for(std::multimap<double,SimNode*>::const_iterator nONit = p_neighborsOfNeighbor->begin(); nONit != p_neighborsOfNeighbor->end(); nONit++)
        {
            m_gradeNeighbors.push_back((*nONit).second);
        }
    }

    // we have to take the neighbors of swept neighbors as well
    for(std::set<SimNode*>::iterator it = m_sweptNeighbors.begin(); it != m_sweptNeighbors.end(); it++)
    {
        std::multimap<double,SimNode*>* p_neighborsOfNeighbor = (*it)->getNeighbors();
        for(std::multimap<double,SimNode*>::const_iterator nONit = p_neighborsOfNeighbor->begin(); nONit != p_neighborsOfNeighbor->end(); nONit++)
        {
            m_gradeNeighbors.push_back((*nONit).second);
        }
    }

    std::vector<SimNode*> gradeContainer(m_gradeNeighbors.size());
    std::copy(m_gradeNeighbors.begin(), m_gradeNeighbors.end(),gradeContainer.begin());
    while(grade>0)
    {
        for(std::vector<SimNode*>::iterator it = m_gradeNeighbors.begin(); it != m_gradeNeighbors.end(); it++)
        {
            std::multimap<double,SimNode*>* p_neighborsOfNeighbor = (*it)->getNeighbors();
            for(std::multimap<double,SimNode*>::const_iterator nONit = p_neighborsOfNeighbor->begin(); nONit != p_neighborsOfNeighbor->end(); nONit++)
            {
                gradeContainer.push_back((*nONit).second);
            }
        }

        m_gradeNeighbors = gradeContainer;
        grade--;
    }
    return true;
}

/**
 * Copys the nodes from the direct neighbors set to the neighbors map.
 * The nodes from the neighbors map are the relevant nodes for the simulation process.
 */
bool SimNode::copyDirectNeighbors()
{
    for(std::set<SimNode*>::const_iterator dNit = m_directNeighbors.begin(); dNit != m_directNeighbors.end(); dNit++)
    {
        if(m_pSimulator->state() == RTMSimulator::INTERRUPTING_FOR_STOP) { m_pSimulator->setState(RTMSimulator::IDLE); return FALSE; } //stop was pressed... ...so we break up with our work
        addNeighbor(*dNit);
    }

    while(m_neighbors.size() > m_pSimulator->numRelevantNeighbors()) { removeMostFarNeighbor(); } //remove most far neighbors if we require less neighbors as direct neighbors are available
    return TRUE;
}

/**
 * Adds this node as vertex node to all adjacent triangles.
 * This is needed for resetting after sweeping.
 */
void SimNode::resetTriangleAdjacence()
{
    for(std::set<SimTriangle*>::iterator it = m_adjacentTriangles.begin(); it != m_adjacentTriangles.end(); it++)
    {
        (*it)->addVertexNode(this);
    }
}

/**
 * Copys all direct neighbors from the node passed as a parameter.
 * @param sourceNode Simulation node to copy neighbors from
 */
void SimNode::adoptDirectNeighborsFrom(SimNode* sourceNode)
{
    std::set<SimNode*>* neighborsToAdopt = sourceNode->getDirectNeighbors();
    for(std::set<SimNode*>::iterator it = neighborsToAdopt->begin(); it != neighborsToAdopt->end(); it++)
    {
        addAdoptedDirectNeighbor(*it);
        (*it)->removeReference(sourceNode);
    }
}

/**
 * Commits previously copied neighbors and afterwards reduces the neighbors to the desired number.
 * @param numberOfRelevantNeighbors Number of neighbors to take in account for the simulation
 */
int SimNode::commitGradeNeighbors(unsigned int numberOfRelevantNeighbors)
{
    int numAddedNeighbors = 0;

    for(std::vector<SimNode*>::const_iterator newNit = m_gradeNeighbors.begin(); newNit != m_gradeNeighbors.end(); newNit++)
    {

        if(m_neighbors.size() < numberOfRelevantNeighbors)
        {
            if(addNeighbor(*newNit))
                numAddedNeighbors++;
        }
        else
        {
            double distance = getDistance(this, *newNit);
            if(distance < m_distanceToMostFarNeighbor)
            {
                if(addNeighbor(*newNit,distance))
                {
                    removeMostFarNeighbor();
                    numAddedNeighbors++;
                }
            }
        }
    }

    m_gradeNeighbors.clear();
    return numAddedNeighbors;
}

/**
 * @returns Pointer to the map that holds pointers to all the neighbors of this node.
 */
std::multimap<double,SimNode*>* SimNode::getNeighbors()
{
    return &m_neighbors;
}

/**
 * @returns Pointer to the set that holds pointers to all the direct neighbors of this node.
 */
std::set<SimNode*>* SimNode::getDirectNeighbors()
{
    return &m_directNeighbors;
}

/**
 * @returns Pointer to the set that holds pointers to all the adopted direct neighbors of this node.
 */
std::set<SimNode*>* SimNode::getAdoptedDirectNeighbors()
{
    return &m_adoptedDirectNeighbors;
}

/**
 * @returns Pointer to the vector that holds pointers to all the grade neighbors of this node.
 */
std::vector<SimNode*>*  SimNode::getGradeNeighbors()
{
    return &m_gradeNeighbors;
}

/**
 * @returns Pointer to the set that holds pointers to all the swept but not deleted neighbors.
 */
std::set<SimNode*>*  SimNode::getSweptNeighbors()
{
    return &m_sweptNeighbors;
}

/**
 * @returns Pointer to the set that holds pointers to all the nodes that call this node a neighbor.
 */
std::set<SimNode*>*  SimNode::getNodesThatReferenceMe()
{
    return &m_nodesThatReferenceMe;
}

/**
 * Calculates the new pressure of this node for the next cycle.
 * This calculation is done based on the pressures of all the neighbor nodes.
 */
void SimNode::calculateNextPressure()
{

    if(!m_fInjectionPoint) //we don't need to calculate a new pressure if we have constant pressure cause it's an injection point
    {
        double _K = m_pSimulator->medianPermeability();
        double K = m_permeability;
        double m = m_neighbors.size();
        double phi = m_porosity;
        double d = getCavityHeight();
        double _l = m_pSimulator->medianCellDistance();
        double q = m_pSimulator->q();
        double r = m_pSimulator->r();
        double s = m_pSimulator->s();
        double p_t = getPressure();

        double brace0 = pow(K/_K,q);

        double sum=0;
        double highestNeighborPressure = 0;
        for(std::multimap<double,SimNode*>::const_iterator it = m_neighbors.begin(); it != m_neighbors.end(); it++)
        {
            SimNode* neighbor = (*it).second;
            double distance = (*it).first;
            double d_i = neighbor->getCavityHeight();
            double phi_i = neighbor->getPorosity();
            double brace1 = pow(((d_i*phi_i)/(d*phi)),r);
            double brace2 = pow(_l/distance,s);
            double p_it = neighbor->getPressure();
            double brace3 = p_it-p_t;
            sum += (brace1*brace2*brace3);

            if(p_it > highestNeighborPressure)
            {
                highestNeighborPressure = p_it;
            }
        }

        double calculated_pressure = p_t+(brace0/m)*sum;
        if(calculated_pressure < p_t)
            calculated_pressure = p_t;
        if(calculated_pressure >= m_pSimulator->highestInjectionPressure())
            calculated_pressure = m_pSimulator->highestInjectionPressure();

        if(calculated_pressure > highestNeighborPressure)
        {
            calculated_pressure = highestNeighborPressure;
        }


        memorizePressure(calculated_pressure);
    }
}

/**
 * Resets pressure and simulation state of this node.
 */
void SimNode::reset()
{
    if(!isInjectionPoint())
    {
        resetPressure();
        setState(SimNode::EMPTY);
    }
}

/**
 * Rescales the translations for the coordinates of this nodes and updates
 * @param factor Factor for rescaling the translations
 */
void SimNode::rescaleTranslation(double factor)
{
    m_x = m_x*factor;
    m_y = m_y*factor;
    m_z = m_z*factor;
    m_pTranslation->translation.setValue(m_x*STATIC_STRETCH_FACTOR, m_y*STATIC_STRETCH_FACTOR, m_z*STATIC_STRETCH_FACTOR);
}

