//RTMSimulator.cpp by az

#include <qglobal.h>

#ifdef Q_WS_WIN
#include "windows.h"
#endif

#include <Inventor/nodes/SoCamera.h> 

#include "RTMSimulator.h"
#include "RTMDockWidget.h"
#include "ExaminerViewer.h"
#include "SimNode.h"
#include "SimTriangle.h"
#include "InfoHelp.h"

#include "Import.h"
#include "Export.h"

#ifdef DEBUG
#include <fstream>
#include <QDateTime>
#endif //_DEBUG

#include <QMenuBar>
#include <QToolBar>
#include <QAction>
#include <QWhatsThis>
#include <QHBoxLayout>
#include <QScrollArea>
#include <QApplication>
#include <QProgressDialog>
#include <QFileDialog>
#include <QApplication>
#include <QDesktopWidget>
#include <QSettings>
#include <QFileDialog>

#include <cmath>
#include <map>
#include <algorithm>
#include <functional>


/**
 * Creates a separate Thread object to check for lonsome nodes in the background.
 * @param pSimulator Pointer to the RTMSimulator object that should be checked for lonesome nodes.
 */
LonesomeNodesThread::LonesomeNodesThread(RTMSimulator* pSimulator):
        m_pSimulator(pSimulator){}

/**
 * Will be executed by a separate thread, to check for nodes without any connections.
 * Such nodes should be ignored, because they will never get filled by the simulation.
 */
void LonesomeNodesThread::run()
{
    m_pSimulator->testForLonesomeNodes();
    m_pSimulator->setState(RTMSimulator::SIMULATION_FINISHED);
}

/**
 * Creates a separate Thread object to run the simulation in the background,
 * while still accepting user intervention through the graphical user interface.
 * @param pSimulator Pointer to the RTMSimulator object
 * @param nCycles Number of cycles to calculate and render before stopping.
 *        If nCycles is 0 the simulation will run through completely or untill it is stopped by an external event.
 *
 */
SimulationThread::SimulationThread(RTMSimulator* pSimulator,int nCycles):
        m_pSimulator(pSimulator),
        m_cycles(nCycles)
{}

/**
 * Runs the previous defined number of simulation cycles, and updates the state machine
 * when it is interrupted or when the simulation finished.
 */
void SimulationThread::run()
{
    RTMSimulator::State state = m_pSimulator->state();

    for( int i = 0;
         (state == RTMSimulator::SIMULATION_RUNNING || state == RTMSimulator::RECORDING_RUNNING)
         && m_pSimulator->changesWereMadeInCycle()
         && (i<m_cycles || m_cycles==0); i++)
    {
        m_pSimulator->doCycle();
        state = m_pSimulator->state();
    }

    switch(state)
    {
    case RTMSimulator::INTERRUPTING_FOR_PAUSE :
        m_pSimulator->setState( RTMSimulator::SIMULATION_PAUSED );
        break;
    case RTMSimulator::INTERRUPTING_FOR_STOP :
        m_pSimulator->reset();
        m_pSimulator->setState( RTMSimulator::IDLE );
        break;
    default :
            m_pSimulator->setState( RTMSimulator::SIMULATION_FINISHED );
    break;
}
}

/**
 * Creates an RTMSimulator object. This object handles all major simulation events, and holdes all global simulation data.
 * Further this function creates the GUI objects for the user interaction with the simulation process.
 * @param name Window title for the main RTMSimulator window.
 */
RTMSimulator::RTMSimulator(const char* name) :
        m_state(RTMSimulator::IDLE),
        m_nodeSource(RTMSimulator::UNDEFINED),
        m_Medianl(0),
        m_MedianPermeability(0),
        m_q(0),
        m_r(0),
        m_s(0),
        m_viscosity(0),
        m_pictureTimeInterval(0),
        m_timeOfLastSnapShot(0),
        m_maxPermeability(0),
        m_minPermeability(0),
        m_translationFactor(1.0),
        m_currentTime(0.0),
        m_timeStep(0.0),
        m_cellSize(15),
        m_fRecalculateMedianl(true),
        m_fPermeabilityGlobalsInvalid(true),
        m_fMadeChangesInCycle(true),
        m_fShowSweptNodes(false),
        m_fMarkNeighbors(false),
        m_fMarkDirectNeighbors(false),
        m_fTakePictures(false),
        m_fUpdateCameraPosition(true),
        m_currentNumberOfRelevantNeighbors(0),
        m_currentCycle(0),
        m_numNodesFull(0)
{
    setWindowTitle(name);

    setGeometry(0,0,800,600);
    QWidget* desktop = QApplication::desktop();
    int desktopWidth = desktop->width();
    int desktopHeight = desktop->height();
    move((desktopWidth-width())/2, (desktopHeight-height())/2);

    QToolBar* pToolBar = new QToolBar(this);

    m_pControls = new Controls(this);
    addDockWidget(Qt::RightDockWidgetArea, m_pControls, Qt::Vertical);
    m_pControls->showAdvancedTab(false);
    connect(m_pControls, SIGNAL(startLonesomeNodesTest()),           this, SLOT(startLonesomeNodesTest()));
    connect(m_pControls, SIGNAL(start()),                            this, SLOT(startSimulation()));
    connect(m_pControls, SIGNAL(stop()),                             this, SLOT(stopSimulation()));
    connect(m_pControls, SIGNAL(pause()),                            this, SLOT(pauseSimulation()));
    connect(m_pControls, SIGNAL(step(int )),                         this, SLOT(stepSimulation(int )));
    connect(m_pControls, SIGNAL(updateQ(double )),                   this, SLOT(setQ(double )));
    connect(m_pControls, SIGNAL(updateR(double )),                   this, SLOT(setR(double )));
    connect(m_pControls, SIGNAL(updateS(double )),                   this, SLOT(setS(double )));
    connect(m_pControls, SIGNAL(updateViscosity(double )),           this, SLOT(setViscosity(double)));
    connect(m_pControls, SIGNAL(takePictures(bool)),                 this, SLOT(takePictures(bool)));
    connect(m_pControls, SIGNAL(updatePictureTimeInterval(double)),  this, SLOT(setPictureTimeInterval(double)));

    //initialize simulation variables
    setQ(m_pControls->getQ());
    setR(m_pControls->getR());
    setS(m_pControls->getS());
    setViscosity(m_pControls->getViscosity());

    QAction* pActionStart = pToolBar->addAction(QIcon(":/img/start.png"),QObject::tr("Start Simulation"));
    QAction* pActionPause = pToolBar->addAction(QIcon(":/img/pause.png"),QObject::tr("Pause Simulation"));
    QAction* pActionStop = pToolBar->addAction(QIcon(":/img/stop.png"),QObject::tr("Stop Simulation"));
    QAction* pActionStep = pToolBar->addAction(QIcon(":/img/step.png"),QObject::tr("Step Simulation"));
    pToolBar->addSeparator();
    QAction* pActionSnapShot = pToolBar->addAction(QIcon(":/img/snapshot.png"),QObject::tr("Create SnapShot"));
    pToolBar->addSeparator();
    m_pShowSettings = pToolBar->addAction(QIcon(":/img/settings.png"),QObject::tr("Settings Tab"));
    connect(m_pShowSettings, SIGNAL(triggered()), this, SLOT(toggleSettingsTab()));
    connect(this,SIGNAL(startButtonEnabled(bool )),    pActionStart,   SLOT(setEnabled(bool )));
    connect(this,SIGNAL(stopButtonEnabled(bool )),     pActionStop,    SLOT(setEnabled(bool )));
    connect(this,SIGNAL(pauseButtonEnabled(bool )),    pActionPause,   SLOT(setEnabled(bool )));
    connect(this,SIGNAL(stepButtonEnabled(bool )),     pActionStep,    SLOT(setEnabled(bool )));

    connect(pActionStart,     SIGNAL(triggered()),    this,   SLOT(startSimulation()));
    connect(pActionPause,     SIGNAL(triggered()),    this,   SLOT(pauseSimulation()));
    connect(pActionStep,      SIGNAL(triggered()),    this,   SLOT(stepSimulation()));
    connect(pActionStop,      SIGNAL(triggered()),    this,   SLOT(stopSimulation()));
    connect(pActionSnapShot,  SIGNAL(triggered()),    this,   SLOT(createSnapShot()));

    m_pInfoHelp = new InfoHelp(this);

    QMenuBar* bar = menuBar();
    QMenu* fileMenu = new QMenu(tr("&File"));
    bar->addMenu(fileMenu);
    QAction* actionOpenFile = fileMenu->addAction(tr("&Open File"));
    QAction* actionSaveToFile = fileMenu->addAction(tr("&Save to file"));
    QMenu* settingsMenu = new QMenu(tr("&Settings"));
    bar->addMenu(settingsMenu);
    QAction* actionAdvancedMode = settingsMenu->addAction(tr("&Advanced"));
    actionAdvancedMode->setCheckable(true);

    connect(actionOpenFile, SIGNAL(triggered()), this, SLOT(open_clicked()));
    connect(actionSaveToFile, SIGNAL(triggered()), this, SLOT(save_clicked()));
    connect(actionAdvancedMode, SIGNAL(toggled(bool)),m_pControls, SLOT(showAdvancedTab(bool)));



#ifdef DEBUG
    QMenu* debugMenu = new QMenu("Debug");
    bar->addMenu(debugMenu);
    QAction* actionPrintNodeTable = debugMenu->addAction("Print Node Table");
    QAction* actionCreateTestNode = debugMenu->addAction("Create Test Node");
    QAction* actionDeleteTestNode = debugMenu->addAction("Delete Test Node");
    QAction* actionCreateShape = debugMenu->addAction("Create Test Shape");
    QAction* actionSetCellSize = debugMenu->addAction("Set Cell Size to 4");
    QAction* actionClearGraph = debugMenu->addAction("Clear Graph");
    QAction* actionMoveCam = debugMenu->addAction("Move Cam");
    QAction* actionMultipleTriangleStripsTest = debugMenu->addAction("Multiple Triangle Strips");

    connect(actionPrintNodeTable, SIGNAL(triggered()), this, SLOT(printNodeTable()));
    connect(actionCreateTestNode, SIGNAL(triggered()), this, SLOT(createTestNode()));
    connect(actionDeleteTestNode, SIGNAL(triggered()), this, SLOT(deleteTestNode()));
    connect(actionCreateShape, SIGNAL(triggered()), this, SLOT(createTestShape()));
    connect(actionSetCellSize, SIGNAL(triggered()), this, SLOT(setCellSize4()));
    connect(actionClearGraph, SIGNAL(triggered()), this, SLOT(clear()));
    connect(actionMoveCam, SIGNAL(triggered()), this, SLOT(moveCam()));
    connect(actionMultipleTriangleStripsTest, SIGNAL(triggered()), this, SLOT(testMultipleTriangleStrips()));
#endif //DEBUG


    QAction *action = QWhatsThis::createAction(pToolBar);
    pToolBar->addAction(action);
    addToolBar(pToolBar);

    pActionPause->setWhatsThis(tr("Current simulation will break, but not be reset."));
    pActionSnapShot->setWhatsThis(tr("Create a snapshot png image from the current simulation plot."));
    pActionStart->setWhatsThis(tr("Start the simulation with the current settings"));
    pActionStep->setWhatsThis(tr("Simulate the next single step. If you defined an other value in the steps field. The defined amount of steps will be simulated."));
    pActionStop->setWhatsThis(tr("Stop and reset the current simulation"));

    m_pMainWidget = new QWidget;
    setCentralWidget(m_pMainWidget);
    m_pMainHBoxLayout = new QHBoxLayout;
    m_pMainWidget->setLayout(m_pMainHBoxLayout);

    m_pExaminerParent = new QWidget;
    m_pMainHBoxLayout->addWidget(m_pExaminerParent);

    setState(RTMSimulator::IDLE);
}

/**
 * Slot for controlled shutdown of the simulation process and closing the RTMSimulator window.
 */
void RTMSimulator::closeEvent(QCloseEvent* e)
{
    stopSimulation();
    QMainWindow::closeEvent(e);
}

/**
 * Slot for clearing all data structures of the simulation process, clearing and resetting the visualization widgets.
 */
void RTMSimulator::clear()
{
    m_activeNodes.clear();
    m_activeNodes.clear();
    m_sweptNodes.clear();
    m_triangles.clear();
    m_injectionPoints.clear();
    m_markedNodes.clear();
    m_pViewerWidget->clear();
}

RTMSimulator::~ RTMSimulator()
{}

/**
 * Updates the RTMSimulator internal state machine, and keeps the user interface up to date according to the desired transition.
 * @param new_state Desired new state for the state machine.
 */
void RTMSimulator::setState(State new_state)
{
    switch(new_state)
    {
    case RTMSimulator::IDLE :
        emit( startButtonEnabled(true) );
        emit( stopButtonEnabled(false) );
        emit( pauseButtonEnabled(false) );
        emit( stepButtonEnabled(true) );
        break;
    case RTMSimulator::CALCULATING_NEIGHBORS :
        emit( startButtonEnabled(false) );
        emit( stopButtonEnabled(true) );
        emit( pauseButtonEnabled(false) );
        emit( stepButtonEnabled(false) );
        break;
    case RTMSimulator::INTERRUPTING_FOR_PAUSE :
        emit( startButtonEnabled(false) );
        emit( stopButtonEnabled(true) );
        emit( pauseButtonEnabled(false) );
        emit( stepButtonEnabled(false) );
        break;
    case RTMSimulator::INTERRUPTING_FOR_STOP :
        emit( startButtonEnabled(false) );
        emit( stopButtonEnabled(false) );
        emit( pauseButtonEnabled(false) );
        emit( stepButtonEnabled(false) );
        break;
    case RTMSimulator::SIMULATION_RUNNING :
        emit( startButtonEnabled(false) );
        emit( stopButtonEnabled(true) );
        emit( pauseButtonEnabled(true) );
        emit( stepButtonEnabled(false) );
        break;
    case RTMSimulator::RECORDING_RUNNING :
        emit( startButtonEnabled(false) );
        emit( stopButtonEnabled(true) );
        emit( pauseButtonEnabled(false) );
        emit( stepButtonEnabled(false) );
        break;
    case RTMSimulator::SIMULATION_PAUSED :
        emit( startButtonEnabled(true) );
        emit( stopButtonEnabled(true) );
        emit( pauseButtonEnabled(false) );
        emit( stepButtonEnabled(true) );
        break;
    case RTMSimulator::SIMULATION_FINISHED :
        emit( startButtonEnabled(true) );
        emit( stopButtonEnabled(true) );
        emit( pauseButtonEnabled(false) );
        emit( stepButtonEnabled(true) );
        break;
    }
    m_state = new_state;
    qApp->processEvents();
}

/**
 * @returns current state of the RTMSimulator internal state machine.
 */
RTMSimulator::State RTMSimulator::state()
{
    return m_state;
}

/**
 * @returns current number of relevant neighbors to the simulation process.
 */
unsigned int RTMSimulator::numRelevantNeighbors()
{
    return m_pControls->numRelevantNeighbors();
}

/**
 * @returns current transparency of the simulation nodes.
 */
float RTMSimulator::nodeTransparency()
{
    return m_pViewerWidget->nodeTransparency();
}

/**
 * Searches the simulation data structures for a special simulation node by a specified node ID.
 * @param nodeID the desired node ID to search for.
 * @returns pointer to the simulation node with the specified node ID.
 */
SimNode* RTMSimulator::getNodeById(int nodeId)
{
    std::map<int,SimNode*>::iterator it = m_activeNodes.find(nodeId);
    if(it != m_activeNodes.end())
    {
        return (*it).second;
    }

    it = m_sweptNodes.find(nodeId);
    if(it != m_sweptNodes.end())
    {
        return (*it).second;
    }
    return 0;
}

/**
 * @returns pointer to the map holding pointers to all active simulation nodes.
 */
std::map<int,SimNode*>* RTMSimulator::activeNodes()
{
    return &m_activeNodes;
}

/**
 * @returns pointer to the map holding pointers to all swept simulation nodes.
 *          swept nodes are nodes that have been removed,
 *          because they had neighbors lying too close to them,
 *          what would distort the calculations for the physical simulation.
 */
std::map<int,SimNode*>* RTMSimulator::sweptNodes()
{
    return &m_sweptNodes;
}

/**
 * @returns pointer to the multimap holding pointers to all injection points.
 *          Injection points are the starting points for the simulation process.
 *          This multimap holds the injection pressore as key for each injection point.
 */
std::multimap<double,SimNode*>* RTMSimulator::injectionPoints()
{
    return &m_injectionPoints;
}

/**
 * Can be used to activate periodically taking pictures. This will also update the controls in SimulationTab.
 * The time interval for taking pictures can be set with the function setPictureTimeInterval(double).
 * @param fTakePictures If set to true, pictures will be taken. If set to false,
 *        RTMSimulator will stop taking pictures.
 */
void RTMSimulator::takePictures( bool fTakePictures )
{
    m_fTakePictures = fTakePictures;
    if(m_pControls->isSetTakePictures() != m_fTakePictures)
    {
        m_pControls->setTakePictures(fTakePictures);
    }
}

/**
 * Defines the time interval for taking pictures of the visualized simulation process.
 * The time corresponds to the calculated simulation time.
 * @param sec Amount of simulation time in seconds to wait between taking picture snapshots.
 */
void RTMSimulator::setPictureTimeInterval( double sec )
{
    m_pictureTimeInterval = sec;
    if(m_pControls->getPictureTimeInterval() != m_pictureTimeInterval)
    {
        m_pControls->setPictureTimeInterval(m_pictureTimeInterval);
    }
}

/**
 * @returns Time interval (simulation time) in seconds that RTMSimulator waits between taking pictures.
 */
double RTMSimulator::pictureTimeInterval()
{
    return m_pictureTimeInterval;
}

/**
 * Updates the showsweptNodes controls in the PreprocessingTab.
 */
void RTMSimulator::setShowSweptNodes(bool showsweptNodes)
{
    if(showsweptNodes)
    {
        m_pControls->setShowSweptNodes(Qt::Checked);
    }
    else
    {
        m_pControls->setShowSweptNodes(Qt::Unchecked);
    }
}

/**
 * Updates the showNeighbors controls in the PreprocessingTab.
 */
void RTMSimulator::setShowNeighbors(bool showNeighbors)
{
    if(showNeighbors)
    {
        m_pControls->setShowNeighbors(Qt::Checked);
    }
    else
    {
        m_pControls->setShowNeighbors(Qt::Unchecked);
    }
}

/**
 * Updates the showDirectNeighbors controls in the PreprocessingTab.
 */
void RTMSimulator::setShowDirectNeighbors(bool showDirectNeighbors)
{
    if(showDirectNeighbors)
    {
        m_pControls->setShowDirectNeighbors(Qt::Checked);
    }
    else
    {
        m_pControls->setShowDirectNeighbors(Qt::Unchecked);
    }
}

/**
 * Updates the showPermeabilityGradient controls in the PreprocessingTab.
 */
void RTMSimulator::setShowPermeabilityGradient(bool showPermeabilityGradient)
{
    if(showPermeabilityGradient)
    {
        m_pControls->setShowPermeabilityGradient(Qt::Checked);
    }
    else
    {
        m_pControls->setShowPermeabilityGradient(Qt::Unchecked);
    }
}

/**
 * Displays or hides the swept nodes in the visualization according the toggle state of a checkable Qt control.
 * @param toggleState If set to Qt::Checked, the swept simulation nodes will be visible in the visualization.
 *        If toggleState is Qt::Unchecked, the swept nodes will be hidden.
 */
void RTMSimulator::showSweptNodes( int toggleState )
{
    switch(toggleState)
    {
    case Qt::Unchecked:
        m_fShowSweptNodes = FALSE;
        break;
    case Qt::Checked:
        m_fShowSweptNodes = TRUE;
        break;
    }

    std::for_each(m_sweptNodes.begin(), m_sweptNodes.end(), UpdateColor() );
}

/**
 * Displays neighbors of selected nodes in a special color.
 * @param toggleState If set to Qt::Checked, the neighbors of the selected nodes will be displayed in a separate color.
 *        If toggleState is Qt::Unchecked, the neighbors of the selected nodes will be displayed in the same color as usual.
 */
void RTMSimulator::showNeighbors( int toggleState )
{
    switch(toggleState)
    {
    case Qt::Unchecked:
        m_fMarkNeighbors = FALSE;
        break;
    case Qt::Checked:
        m_fMarkNeighbors = TRUE;
        break;
    }

    updateSelectionData();
}

/**
 * Displays direct neighbors of selected nodes in a special color.
 * @param toggleState If set to Qt::Checked, the direct neighbors of the selected nodes will be displayed in a separate color.
 *        If toggleState is Qt::Unchecked, the direct neighbors of the selected nodes will be displayed in the same color as usual.
 */
void RTMSimulator::showDirectNeighbors( int toggleState )
{
    switch(toggleState)
    {
    case Qt::Unchecked:
        m_fMarkDirectNeighbors = FALSE;
        break;
    case Qt::Checked:
        m_fMarkDirectNeighbors = TRUE;
        break;
    }
    updateSelectionData();
}

/**
 * Colors all the nodes as a gradient based on the permeability defined for the nodes.
 * @param toggleState If set to Qt::Checked, the nodes will be colored as a gradient.
 *        If toggleState is Qt::Unchecked, the nodes will be colored with normal colors.
 */
void RTMSimulator::showPermeabilityGradient(int toggleState)
{
    updatePermeabilityGlobals();
    bool displayPermCol = (toggleState == Qt::Checked);

    std::for_each(m_activeNodes.begin(), m_activeNodes.end(), DisplayPermColor(displayPermCol));
}

/**
 * Needs to be executed whenever the permeability of a single node has been changed.
 * Therefrom RTMSimulator knows when recalculation of the permeability gradient is needed before displaying.
 */
void RTMSimulator::invalidatePermeabilityGlobals()
{
    m_fPermeabilityGlobalsInvalid = TRUE;
}

/**
 * Adds a single node to the simulation graph.
 * @param nodeId ID of the node to create. This value should be unique in the whole graph.
 * @param x X coordinate for the newly created node.
 * @param y Y coordinate for the newly created node.
 * @param z Z coordinate for the newly created node.
 * @param swept If set to true, the node will be added as a swept node.
 * @returns Pointer to the newly added simulation node.
 */
SimNode* RTMSimulator::addNode(int nodeId, double x, double y, double z, bool swept)
{
    SimNode* node = m_pViewerWidget->addNode(nodeId,x,y,z);
    if(swept)
        m_sweptNodes.insert(std::make_pair(nodeId,node));
    else
        m_activeNodes.insert(std::make_pair(nodeId,node));
    return node;
}

/**
 * Adds a single triangle to the simulation graph for displaying a surface.
 * A triangle always uses three simulation nodes as vertices.
 * @param nodeId1 Node ID of the first vertex.
 * @param nodeId2 Node ID of the second vertex.
 * @param nodeId3 Node ID of the third vertex.
 * @returns Pointer to the newly added triangle.
 */
SimTriangle* RTMSimulator::addTriangle(int nodeId1, int nodeId2, int nodeId3)
{
    SimNode* node1 = (m_activeNodes.find(nodeId1))->second;
    SimNode* node2 = (m_activeNodes.find(nodeId2))->second;
    SimNode* node3 = (m_activeNodes.find(nodeId3))->second;

    return addTriangle(m_triangles.size(),node1,node2,node3);
}

/**
 * Adds a single quadrangle to the simulation graph for displaying a surface.
 * A quadrangle always uses four simulation nodes as vertices.
 * Internally a quadrangle will be split up into two triangles.
 * @param nodeId1 Node ID of the first vertex.
 * @param nodeId2 Node ID of the second vertex.
 * @param nodeId3 Node ID of the third vertex.
 * @param nodeId4 Node ID of the fourth vertex.
 */
void RTMSimulator::addQuadrangle(int nodeId1, int nodeId2, int nodeId3, int nodeId4)
{
    SimNode* node1 = (m_activeNodes.find(nodeId1))->second;
    SimNode* node2 = (m_activeNodes.find(nodeId2))->second;
    SimNode* node3 = (m_activeNodes.find(nodeId3))->second;
    SimNode* node4 = (m_activeNodes.find(nodeId4))->second;

    addTriangle(m_triangles.size(),node1,node2,node3);
    addTriangle(m_triangles.size(),node3,node4,node1);
}

/**
 * Adds a single triangle with a specified ID to the simulation graph for displaying a surface.
 * The vertices for the triangle are defined by the provided simulation nodes.
 * @param triangleID ID of the newly created triangle.
 * @param node1 Pointer to the first vertex.
 * @param node2 Pointer to the second vertex.
 * @param node3 Pointer to the third vertex.
 * @returns Pointer to the newly created triangle.
 */
SimTriangle* RTMSimulator::addTriangle(int triangleId, SimNode* node1, SimNode* node2, SimNode* node3)
{
    SimTriangle* triangle = m_pViewerWidget->addTriangle(triangleId,node1,node2,node3);
    m_triangles.insert(std::make_pair(triangleId,triangle));
    return triangle;
}

/**
 * Adds a single triangle with predefined settings to the simulation graph for displaying a surface.
 * @param settings Holds all the required information to add a new triangle.
 * @returns Pointer to the newly created triangle.
 */
SimTriangle* RTMSimulator::addTriangle(TriangleSettings &settings)
{
    SimTriangle* triangle = m_pViewerWidget->addTriangle(settings);
    m_triangles.insert(std::make_pair(settings.triangleId,triangle));
    return triangle;
}

/**
 * @returns Pointer to the map that holds all the simulation triangles.
 */
std::map<int,SimTriangle*>* RTMSimulator::triangles()
{
    return &m_triangles;
}

/**
 * Defines an existing simulation node as injection point.
 * @param injectionPoint Pointer to the simulation node that should become an injection point.
 * @param injectionPressure The desired injection Pressure for the injection point.
 */
void RTMSimulator::addInjectionPoint(SimNode* injectionPoint, double injectionPressure)
{
    removeInjectionPoint(injectionPoint);
    m_injectionPoints.insert(std::make_pair(injectionPressure, injectionPoint));
    m_pControls->updateNumberOfInjectionCells(m_injectionPoints.size());
}

/**
 * Removes an existing injection point. The associated simulation node will then become
 * a usual simulation node again.
 * @param injectionPoint Pointer to the existing injection point to be removed.
 */
void RTMSimulator::removeInjectionPoint(SimNode* injectionPoint)
{
    for(std::multimap<double,SimNode*>::iterator it = m_injectionPoints.begin(); it != m_injectionPoints.end(); it++)
    {
        if((*it).second == injectionPoint)
        {
#ifdef Q_WS_WIN
            it = m_injectionPoints.erase(it);
            if(it != m_injectionPoints.begin())
                it--;
#else
            m_injectionPoints.erase(it);
#endif
            //this fixes an inconvenience in visual c++!
            if(it == m_injectionPoints.end())
                break;
        }
    }
    m_pControls->updateNumberOfInjectionCells(m_injectionPoints.size());
}

/**
 * Creates a unidirectional neighborhood association between a node and a neighbor.
 * @param nodeId ID of a simulation node. This is the node that will call the second node a neighbor.
 * @param neighborId ID of a simulation node that should become a neighbor of the first node.
 *        The neighborhood list of this node will not change.
 */
void RTMSimulator::setDirectNeighbor(int nodeId, int neighborId)
{
    if(nodeId == neighborId)
        return;
    if(m_activeNodes.find(nodeId) != m_activeNodes.end() && m_activeNodes.find(neighborId) != m_activeNodes.end())
    {
        SimNode* node = m_activeNodes[nodeId];
        SimNode* neighbor = m_activeNodes[neighborId];
        node->addDirectNeighbor(neighbor);
    }
}

/**
 * @returns the average distance between neighbors.
 */
double RTMSimulator::medianCellDistance()
{
    calculateNeighbors();
    calculateMedianCellDistance();

    return m_Medianl;
}

/**
 * Calculates the average distance between neighbors.
 */
void RTMSimulator::calculateMedianCellDistance()
{
    if(m_fRecalculateMedianl)
    {
        double midDistance=0.0;
        int countedConnections=0;
        for(std::map<int,SimNode*>::const_iterator it = m_activeNodes.begin(); it != m_activeNodes.end(); it++)
        {
            SimNode* node = (*it).second;
            std::multimap<double,SimNode*>* p_neighbors = node->getNeighbors();
            for(std::multimap<double,SimNode*>::const_iterator it_neighbor = p_neighbors->begin(); it_neighbor != p_neighbors->end(); it_neighbor++)
            {
                midDistance+= SimNode::getDistance((*it).second,(*it_neighbor).second);
                countedConnections++;
            }
        }
        if(countedConnections == 0) //avoid division by 0
            countedConnections = 1;

        m_Medianl = midDistance/(double)countedConnections;
        m_fRecalculateMedianl = false;

        m_pControls->updateMedianCellDistance(m_Medianl);
    }
}

/**
 * Should be used to recalculate the neighborhood associations after importing a dat (marc mentat) file.
 */
bool RTMSimulator::calculateNeighbors()
{
    switch(m_nodeSource)
    {
    case RTMSimulator::DAT_FILE :
        return calculateNeighbors_dat_file();
        break;
    case RTMSimulator::UNDEFINED :
        return false;
        break;
    }
    return false;
}

/**
 * Searches the required amount of neighbors for each node by recursively traversing the node net.
 * @param numberOfRelevantNeighbors The number of required neighbors for every single node.
 */
bool RTMSimulator::calculateGradeNeighbors(int numberOfRelevantNeighbors)
{
    std::map<int,SimNode*> nodesToSearchForNeighbors = m_activeNodes;
    int grade = 0;

    unsigned int formerNumberOfNodesToSearchNeighbors2 = 0;
    unsigned int formerNumberOfNodesToSearchNeighbors = 0;

    // copy & commit grade neighbors
    while(!nodesToSearchForNeighbors.empty())
    {
        // copy grade neighbors
        formerNumberOfNodesToSearchNeighbors2 = formerNumberOfNodesToSearchNeighbors;
        formerNumberOfNodesToSearchNeighbors = nodesToSearchForNeighbors.size();
        for(std::map<int,SimNode*>::iterator it = nodesToSearchForNeighbors.begin(); it != nodesToSearchForNeighbors.end(); it++)
        {
            qApp->processEvents();
            if(state() == RTMSimulator::INTERRUPTING_FOR_STOP) { setState(RTMSimulator::IDLE); return FALSE; } //stop was pressed... ...so we break up with our work
            SimNode* currentNode = (*it).second;
            if(currentNode->getNumOfNeighbors() < numberOfRelevantNeighbors)
            {
                currentNode->copyGradeNeighbors(grade);
            }
            else
            {
#ifdef Q_WS_WIN
                it = nodesToSearchForNeighbors.erase(it);
                if(it != nodesToSearchForNeighbors.begin())
                    it--;
#else
                nodesToSearchForNeighbors.erase(it);
#endif
                //this fixes an inconvenience in visual c++!
                if(it == nodesToSearchForNeighbors.end())
                    break;
            }
        }

        // commit grade neighbors
        for(std::map<int,SimNode*>::iterator it = nodesToSearchForNeighbors.begin(); it != nodesToSearchForNeighbors.end(); it++)
        {
            qApp->processEvents();
            if(state() == RTMSimulator::INTERRUPTING_FOR_STOP) { setState(RTMSimulator::IDLE); return FALSE; } //stop was pressed, so we break up with our work
            SimNode* currentNode = (*it).second;

            int countCommitedNeighbors = currentNode->commitGradeNeighbors(numberOfRelevantNeighbors);
            if(0<countCommitedNeighbors)
            {
#ifdef Q_WS_WIN
                it = nodesToSearchForNeighbors.erase(it);
                if(it != nodesToSearchForNeighbors.begin())
                    it--;
#else
                nodesToSearchForNeighbors.erase(it);
#endif
                //this fixes an inconvenience in visual c++!
                if(it == nodesToSearchForNeighbors.end())
                    break;
            }

            while(currentNode->getNumOfNeighbors() > numberOfRelevantNeighbors)
            {
                currentNode->removeMostFarNeighbor(); //remove most far neighbors if we require less neighbors, as neighbors available
            }
        }

        if(formerNumberOfNodesToSearchNeighbors <= nodesToSearchForNeighbors.size())
        {
            if(formerNumberOfNodesToSearchNeighbors != formerNumberOfNodesToSearchNeighbors2 && formerNumberOfNodesToSearchNeighbors != 0)
            {
                grade++;
            }
            else
            {
                break;
            }
        }
    }
    return TRUE;
}

/**
 * Copies the direct neighbors of each node to its list of active neighbors.
 */
bool RTMSimulator::copyDirectNeighbors()
{
    for(std::map<int,SimNode*>::iterator it = m_activeNodes.begin(); it != m_activeNodes.end(); it++)
    {
        qApp->processEvents();
        if(!(*it).second->copyDirectNeighbors())
            return FALSE;
    }
    return TRUE;
}

/**
 * Rebuilds the neighborhood associations after importing a dat (marc mentat) file.
 */
bool RTMSimulator::calculateNeighbors_dat_file()
{
    unsigned int numberOfRelevantNeighbors = m_pControls->numRelevantNeighbors();
    if(m_currentNumberOfRelevantNeighbors != numberOfRelevantNeighbors)
    {
        // copy direct neighbors
        if(!copyDirectNeighbors())
            return FALSE;

        // calculate grade neighbors
        if(!calculateGradeNeighbors(numberOfRelevantNeighbors))
            return FALSE;

        m_currentNumberOfRelevantNeighbors = numberOfRelevantNeighbors;
    }
    return TRUE;
}

/**
 * Searches the node net for so called cancer nodes. Cander nodes are nodes that are lying
 * too close to each other, what would disort the calculations for the physical simulation.
 * @param midDistanceFactor A factor to the medianCellDistance() as a minimum distance between neighbors.
 */
bool RTMSimulator::searchCancer(double midDistanceFactor)
{
    int progressCounter = 0;
    QProgressDialog progress("Searching for cancer nodes", "Abort Searching", progressCounter, m_activeNodes.size(), this);

    for(std::map<int,SimNode*>::iterator it = m_activeNodes.begin(); it != m_activeNodes.end(); it++)
    {
        qApp->processEvents();
        if(progress.wasCanceled())
        {
            m_pViewerWidget->setAutoRedraw(TRUE);
            return FALSE;
        }

        SimNode* currentNode = (*it).second;
        currentNode->searchCancerNodes(medianCellDistance()*midDistanceFactor);

        progress.setValue(++progressCounter);
    }

    return TRUE;
}

/**
 * Moves the previously found cancer nodes from the list of active nodes to the list of swept nodes.
 * @returns The number of nodes moved.
 */
int RTMSimulator::sweepCancerNodes()
{
    int cancerNodesCounter = 0;
    int progressCounter = 0;
    QProgressDialog progress("Sweeping cancer nodes", "Abort sweeping", progressCounter, m_activeNodes.size(), this);

#ifdef Q_WS_WIN
    for(std::map<int,SimNode*>::iterator it = m_activeNodes.begin(); it != m_activeNodes.end(); it++)
#else
        for(std::map<int,SimNode*>::iterator it = m_activeNodes.begin(); it != m_activeNodes.end(); )
#endif
        {
        qApp->processEvents();
        if(progress.wasCanceled())
        {
            m_pViewerWidget->setAutoRedraw(TRUE);
            return -1;
        }

        if((*it).second->isMarkedForSweep())
        {
            cancerNodesCounter++;
            (*it).second->sweep();
            m_sweptNodes.insert(*it);

#ifdef Q_WS_WIN
            it = m_activeNodes.erase(it);
            if(it != m_activeNodes.begin())
                it--;
#else
            std::map<int,SimNode*>::iterator next = it;
            next++;
            m_activeNodes.erase(it);
            it=next;
#endif
            //this fixes an inconvenience in visual c++!
            if(it == m_activeNodes.end())
                break;
        }
#ifndef Q_WS_WIN
        else
        {
            it++;
        }
#endif
        progress.setValue(++progressCounter);
    }
    m_pControls->updateTotalCells(m_activeNodes.size());
    return cancerNodesCounter;
}

/**
 * Irrevocable deletes the nodes from the swept nodes list.
 */
void RTMSimulator::deleteSweptNodes()
{
    /* DO NOT TRY TO execute removeAllReferences() and removeNode(...) in a single iteration!
     OTHERWISE YOU WILL EXPERIANCE DANGLING POINTERS!!! */
    std::for_each(m_sweptNodes.begin(), m_sweptNodes.end(), RemAllRef());
    std::for_each(m_sweptNodes.begin(), m_sweptNodes.end(), RemNode(m_pViewerWidget));
    m_sweptNodes.clear();
}

/**
 * Moves the nodes from the swept nodes list back into the list of active nodes.
 */
void RTMSimulator::resetSweep()
{
    m_activeNodes.insert(m_sweptNodes.begin(),m_sweptNodes.end());
    m_sweptNodes.clear();

    //These things should not be done in a single iteration,
    //because the states of a node also depends on it's neighbors.
    std::for_each(m_activeNodes.begin(), m_activeNodes.end(), MarkForSweep(FALSE));
    std::for_each(m_activeNodes.begin(), m_activeNodes.end(), ResetNeighborSettings());
    std::for_each(m_activeNodes.begin(), m_activeNodes.end(), ResetTriangleAdjacence());
}

/**
 * Searches for cancer nodes and moves these nodes to swept nodes list.
 * @param midDistanceFactor A factor to the medianCellDistance() as a minimum distance between neighbors.
 * @param fIterativeSweep If set to false, only a single sweep iteration is done. This does not ensure
          that there are no more cancer nodes. If set to true, sweeping is done as long as cancer nodes are found.
          Whenever a node is being swept, the medianCellDistance changes, and therefore new cancer nodes
          may become available.
 */
bool RTMSimulator::sweep(double midDistanceFactor,bool fIterativeSweep)
{
    m_pViewerWidget->setAutoRedraw(FALSE);

    // reset previous sweep
    resetSweep();
    copyDirectNeighbors();

    int sweptNodesCounter = 0;
    do
    {
        if(!searchCancer(midDistanceFactor))
            return FALSE;
        sweptNodesCounter = sweepCancerNodes();
        if(sweptNodesCounter < 0)
            return FALSE;

        if(sweptNodesCounter > 0)
        {
            calculateGradeNeighbors(numRelevantNeighbors());
        }
        m_fRecalculateMedianl = TRUE; //m_Medianl needs to be recalculated!
    }
    while(sweptNodesCounter > 0 && fIterativeSweep);

    m_pViewerWidget->setAutoRedraw(TRUE);
    return TRUE;
}

/**
 * Increments the counter for completely filled nodes and updates the according controls in the AdvancedTab.
 */
void RTMSimulator::incrementFullCounter()
{
    m_numNodesFull++;
    m_pControls->updateCellsFull(m_numNodesFull);
}

/**
 * Imports a 3D model from a filename.
 * @param strFile Fully qualified path to a file.
 */
void RTMSimulator::importFile(QString strFile)
{
    setTranslationFactor(1);
    m_currentNumberOfRelevantNeighbors = 0;
    m_nodeSource = RTMSimulator::DAT_FILE;
    m_fRecalculateMedianl = true;
    m_fUpdateCameraPosition = true;
    clear();

    Import import(strFile,this);

    m_pControls->updateTotalCells(m_activeNodes.size());

    m_pViewerWidget->setCellSize(m_cellSize);
    if(m_fUpdateCameraPosition)
    {
        m_pViewerWidget->viewAll();
    }
    m_pViewerWidget->saveHomePosition();
}

/**
 * Exports the current simulation settings including the 3D model and simulation states to a file.
 * @param file Fully qualified path for the file to store the data in.
 */
void RTMSimulator::exportToFile(QString file)
{
    ExportRTMS::writeToRTMSFile(file,this);
}

/**
 * This is a test procedure to check if there are unreachable nodes in the graph.
 * It starts at the selected nodes and simply colors all neighbors recursivly.
 * If there are still uncolored nodes at the end of this procedure, these nodes
 * will be visible to the user and should probably be removed. Because such nodes will
 * never get filled by the simulation process.
 */
void RTMSimulator::testForLonesomeNodes()
{
    if(m_activeNodes.size() > 0)
    {
        calculateNeighbors();
        std::vector<SimNode*> selection = ExaminerViewer::getSelection();
        if(selection.size() == 0) { selection.push_back((*m_activeNodes.begin()).second); }
        for(std::vector<SimNode*>::iterator it = selection.begin(); it != selection.end(); it++)
        {
            if(state() == RTMSimulator::INTERRUPTING_FOR_STOP) { setState(RTMSimulator::IDLE); return; } //stop was pressed, so we break up with our work
            (*it)->setState(SimNode::HALF);
        }

        bool fChangedAState = true;
        while(fChangedAState)
        {
            fChangedAState = false;
            for(std::map<int,SimNode*>::iterator it = m_activeNodes.begin(); it != m_activeNodes.end(); it++)
            {
                qApp->processEvents();
                if(state() == RTMSimulator::INTERRUPTING_FOR_STOP) { setState(RTMSimulator::IDLE); return; } //stop was pressed... ...so we break up with our work
                if( (*it).second->getState() == SimNode::HALF )
                {
                    (*it).second->memorizeState(SimNode::FULL);
                    fChangedAState = true;
                    std::multimap<double,SimNode*>* p_neighbors = (*it).second->getNeighbors();
                    std::for_each(p_neighbors->begin(), p_neighbors->end(), RaiseFromEmptyToHalf());
                }
            }

            qApp->processEvents();

            for(std::map<int,SimNode*>::iterator it = m_activeNodes.begin(); it != m_activeNodes.end(); it++)
            {
                if(state() == RTMSimulator::INTERRUPTING_FOR_STOP) { setState(RTMSimulator::IDLE); return; } //stop was pressed, so we break up with our work
                (*it).second->commitState();
            }
            m_pViewerWidget->render();
#if defined(WIN32) || defined(__WIN32__)
            Sleep(500);
#elif defined(linux) || defined(__linux__) || defined(Q_WS_MAC)
            usleep(500000);
#else
#error "Unsupported Operating System"
#endif

        }
    }
}

/**
 * Initializes the viewer widgets and the whole graph.
 */
void RTMSimulator::init()
{
    //ExaminerViewer needs to be created and initialized here, so we can call it from main function
    m_pViewerWidget = new ExaminerViewer(m_pExaminerParent,this);
    m_pViewerWidget->initGraph();
    SimNode::initClass();
    showMaximized();
}

/**
 * Toggles (shows or hides) the settings tab.
 */
void RTMSimulator::toggleSettingsTab()
{
    if(m_pControls->isHidden())
    {
        m_pControls->show();
    }
    else
    {
        m_pControls->hide();
    }
}

/**
 * @returns Currently defined vacuum pressure for the whole simulation.
 */
double RTMSimulator::vacuumPressure()
{
    return m_pControls->getVacuumPressure();
}

/**
 * @param pressure Defines the vacuum pressure for the whole simulation.
 */
void RTMSimulator::setVacuumPressure(double pressure)
{
    m_pControls->setVacuumPressure(pressure);
}

/**
 * Updates the data that is displayed about the currently selected nodes.
 * It also updates or removes color markings (neighbors, direct neighbors).
 */
void RTMSimulator::updateSelectionData()
{
    //unmark previously marked nodes
    std::for_each(m_markedNodes.begin(), m_markedNodes.end(), MarkGreen(FALSE));
    m_markedNodes.clear();

    //mark the neighbors of the selection with a special color
    if(m_fMarkNeighbors || m_fMarkDirectNeighbors)
    {
        std::vector<SimNode*> selection = m_pViewerWidget->getSelection();
        for(std::vector<SimNode*>::iterator it = selection.begin(); it != selection.end(); it++)
        {
            if(m_fMarkNeighbors)
            {
                //mark the neighbors
                std::multimap<double,SimNode*>* pNeighbors = (*it)->getNeighbors();
                for(std::multimap<double,SimNode*>::iterator itN = pNeighbors->begin(); itN != pNeighbors->end(); itN++)
                {
                    if(selection.end() == std::find(selection.begin(),selection.end(),(*itN).second))
                    {
                        (*itN).second->markGreen(TRUE);
                        m_markedNodes.insert((*itN).second);
                    }
                }
            }

            if(m_fMarkDirectNeighbors)
            {
                //mark the direct neighbors
                std::set<SimNode*>* pDirectNeighbors = (*it)->getDirectNeighbors();
                for(std::set<SimNode*>::iterator itN = pDirectNeighbors->begin(); itN != pDirectNeighbors->end(); itN++)
                {
                    if(selection.end() == std::find(selection.begin(),selection.end(),*itN))
                    {
                        (*itN)->markGreen(TRUE);
                        m_markedNodes.insert((*itN));
                    }
                }
                //mark the copied direct neighbors
                std::set<SimNode*>* pAdoptedDirectNeighbors = (*it)->getAdoptedDirectNeighbors();
                for(std::set<SimNode*>::iterator itN = pAdoptedDirectNeighbors->begin(); itN != pAdoptedDirectNeighbors->end(); itN++)
                {
                    if(selection.end() == std::find(selection.begin(),selection.end(),*itN))
                    {
                        (*itN)->markGreen(TRUE);
                        m_markedNodes.insert((*itN));
                    }
                }
            }
        }
    }

    m_pControls->selectionChanged();
}

/**
 * Starts the simulation procedure in a separate thread.
 */
void RTMSimulator::startSimulation()
{
    m_fMadeChangesInCycle = TRUE;
    if(state() != RTMSimulator::RECORDING_RUNNING)
        setState(RTMSimulator::CALCULATING_NEIGHBORS);
    if(calculateNeighbors())
    {
        if(state() != RTMSimulator::RECORDING_RUNNING)
            setState( RTMSimulator::SIMULATION_RUNNING );

        SimulationThread thread(this);
        thread.run();
    }
    else
    {
        setState( RTMSimulator::IDLE);
    }
}

/**
 * Interrupts and pauses any currently running simulation process.
 */
void RTMSimulator::pauseSimulation()
{
    setState( RTMSimulator::INTERRUPTING_FOR_PAUSE );
}

/**
 * Executes a single cycle in the simulation.
 */
void RTMSimulator::stepSimulation()
{
    setState( RTMSimulator::SIMULATION_RUNNING );
    SimulationThread thread(this,m_pControls->getNumberOfCycles());
    thread.run();
    setState( RTMSimulator::SIMULATION_PAUSED );
}

/**
 * Executes an arbitary number of cycles in the simulation process.
 * @nCycles Number of cycles to execute.
 */
void RTMSimulator::stepSimulation(int nCycles)
{
    setState( RTMSimulator::SIMULATION_RUNNING );
    SimulationThread thread(this,nCycles);
    thread.run();
    setState( RTMSimulator::SIMULATION_PAUSED );
}

/**
 * Interrupts and stops the any currently running simulation process.
 */
void RTMSimulator::stopSimulation()
{
    if(state() == RTMSimulator::SIMULATION_RUNNING)
    {
        setState( RTMSimulator::INTERRUPTING_FOR_STOP );
    }
    else
    {
        reset();
        setState( RTMSimulator::IDLE );
    }
}

/**
 * Takes a single snapshot from the current visualization.
 */
void RTMSimulator::createSnapShot()
{
    m_pViewerWidget->createSnapShot(QString());
}

/**
 * Starts the test for unreachable nodes in a separate thread.
 */
void RTMSimulator::startLonesomeNodesTest()
{
    setState(RTMSimulator::CALCULATING_NEIGHBORS);
    LonesomeNodesThread thread(this);
    thread.run();
}

/**
 * @returns Highest injection pressure that is defined for this simulation.
 */
double RTMSimulator::highestInjectionPressure()
{
    if(m_injectionPoints.size() > 0)
    {
        std::multimap<double,SimNode*>::const_iterator it = m_injectionPoints.end(); it--;
        return (*it).first;
    }
    else
    {
        return 0;
    }
}

/**
 * @returns Average permeability of all nodes for that simulation.
 */
double RTMSimulator::medianPermeability()
{
    if(m_fPermeabilityGlobalsInvalid)
        updatePermeabilityGlobals();
    return m_MedianPermeability;
}

/**
 * @returns Highest permeability of all nodes for that simulation.
 */
double RTMSimulator::maxPermeability()
{
    if(m_fPermeabilityGlobalsInvalid)
        updatePermeabilityGlobals();
    return m_maxPermeability;
}

/**
 * @returns Lowest permeability of all nodes for that simulation.
 */
double RTMSimulator::minPermeability()
{
    if(m_fPermeabilityGlobalsInvalid)
        updatePermeabilityGlobals();
    return m_minPermeability;

}

/**
 * @returns Q (unit less) Quantifier for the simulation formula.
 */
double RTMSimulator::q()
{
    return m_q;
}

/**
 * @returns R (unit less) Quantifier for the simulation formula.
 */
double RTMSimulator::r()
{
    return m_r;
}

/**
 * @returns S (unit less) Quantifier for the simulation formula.
 */
double RTMSimulator::s()
{
    return m_s;
}

/**
 * @returns Viscosity that has been defined for that simulation.
 */
double RTMSimulator::viscosity()
{
    return m_viscosity;
}

/**
 * @param q (unit less) Quantifier for the simulation formula.
 */
void RTMSimulator::setQ(double q)
{
    m_q = q;
    if(m_pControls->getQ() != m_q)
    {
        m_pControls->setQ(m_q);
    }
}

/**
 * @param r (unit less) Quantifier for the simulation formula.
 */
void RTMSimulator::setR(double r)
{
    m_r = r;
    if(m_pControls->getR() != m_r)
    {
        m_pControls->setR(m_r);
    }
}

/**
 * @param s (unit less) Quantifier for the simulation formula.
 */
void RTMSimulator::setS(double s)
{
    m_s = s;
    if(m_pControls->getS() != m_s)
    {
        m_pControls->setS(m_s);
    }
}

/**
 * @param viscosity Viscosity for the material that should be simulated.
 */
void RTMSimulator::setViscosity(double viscosity)
{
    m_viscosity = viscosity;
    if(m_pControls->getViscosity() != m_viscosity)
    {
        m_pControls->setViscosity(m_viscosity);
    }
}

/**
 * Searches all nodes for the highest and the lowest permeability and stores these values.
 */
void RTMSimulator::updatePermeabilityGlobals()
{
    double permeability_sum = 0;
    int progressCounter = 0;
    int totalNodes = m_activeNodes.size();

    //initialize with settings of a valid node
    double max_permeability = m_activeNodes.begin()->second->getPermeability();
    double min_permeability = m_activeNodes.begin()->second->getPermeability();

    QProgressDialog progress("Calculating Permeability Globals", "Abort Calculation", 0, totalNodes, this);
    for(std::map<int,SimNode*>::iterator it = m_activeNodes.begin(); it != m_activeNodes.end() && !progress.wasCanceled(); it++)
    {
        double permeability = ((*it).second->getPermeability());
        if(permeability > max_permeability) {
            max_permeability = permeability;
        }
        else if(permeability < min_permeability)
        {
            min_permeability = permeability;
        }
        m_minPermeability = min_permeability;
        m_maxPermeability = max_permeability;

        permeability_sum += permeability;
        progress.setValue(++progressCounter);
    }
    m_MedianPermeability = permeability_sum/(double)totalNodes;
    m_fPermeabilityGlobalsInvalid = false;

    updatePermeabilityColors();
}

/**
 * Calculates the color for each node to resemble the permeability in a gradient.
 */
void RTMSimulator::updatePermeabilityColors()
{
    int progressCounter = 0;
    int totalNodes = m_activeNodes.size();
    double permeabilitySpan = maxPermeability() - minPermeability();

    QProgressDialog progress("Updating Permeability Colors", "Abort Process", 0, totalNodes, this);
    for(std::map<int,SimNode*>::iterator it = m_activeNodes.begin(); it != m_activeNodes.end() && !progress.wasCanceled(); it++)
    {
        double permeabilityValue = ((*it).second->getPermeability())-m_minPermeability;
        int colorGradientValue = 100/permeabilitySpan*permeabilityValue;
        (*it).second->setPermeabilityColor(colorGradientValue);
        progress.setValue(++progressCounter);
    }
}

/**
 * Calculates the time delta for a single cycle of the simulation.
 * @returns Time delta for a single cycle.
 */
double RTMSimulator::timeDelta()
{
    double n = viscosity();                             //[N*s/m2]
    double _l = medianCellDistance();                   //[m]
    double l_typ = (sqrt((double)m_numNodesFull)*_l);   //[m]
    double _K = medianPermeability();                   //[m^2]
    double p_inj = highestInjectionPressure();          //[N/m2]
    double p_vac = vacuumPressure();                    //[N/m2]

    double time = n*l_typ*_l/(_K*(p_inj-p_vac));
    return time;
}

/**
 * @returns The currently passed simulation time.
 */
double RTMSimulator::currentTime()
{
    return m_currentTime;
}

/**
 * Updates the currently passed simulation time.
 * @param time New value for the simulation time.
 */
void RTMSimulator::setCurrentTime(double time)
{
    m_currentTime = time;
    m_pControls->displayTime(m_currentTime);
}

/**
 * Stores the new translation factor for the translation rescaling in the visualization.
 * @param translationFactor Factor for resizing the original translations.
 */
void RTMSimulator::setTranslationFactor(double translationFactor)
{
    m_translationFactor = translationFactor;
    m_pViewerWidget->setUnitStretch(translationFactor == MM_FACTOR);
}

/**
 * @returns Current translation factor.
 */
double RTMSimulator::translationFactor()
{
    return m_translationFactor;
}

/**
 * Executes a single simulation cycle. This function should always be called from a separate thread.
 */
void RTMSimulator::doCycle()
{
    m_pViewerWidget->setAutoRedraw(FALSE);
    m_pControls->displayCycleNr(++m_currentCycle);
    setCurrentTime(m_currentTime+timeDelta());
    m_fMadeChangesInCycle = FALSE;
    qApp->processEvents();
    for(std::map<int,SimNode*>::iterator it = m_activeNodes.begin(); it != m_activeNodes.end(); it++)
    {
        SimNode* pNode = (*it).second;
        pNode->calculateNextPressure();
    }

    //now that we calculated all the new values, we can commit them to the single nodes.
    for(std::map<int,SimNode*>::iterator it = m_activeNodes.begin(); it != m_activeNodes.end() ;it++)
    {
        SimNode* pNode = (*it).second;
        if(pNode->commitPressure())
            m_fMadeChangesInCycle = TRUE;
    }

    std::for_each(m_triangles.begin(), m_triangles.end(), UpdateTriangleColor());

    m_pViewerWidget->setAutoRedraw(TRUE);
    m_pViewerWidget->render();

    takeSnapShotIfRequired();
}

/**
 * Renders the simulation graph in the viewer widget.
 */
void RTMSimulator::render()
{
    m_pViewerWidget->render();
}

/**
 * Resets the current simulation completely. Nodes and Triangle will remain valid,
 * just the simulation (states, counters, displays) will be reset.
 */
void RTMSimulator::reset()
{
    m_currentCycle=0;
    m_numNodesFull=0;
    m_currentTime=0.0;
    m_timeOfLastSnapShot = 0.0;

    m_pControls->displayCycleNr(m_currentCycle);
    m_pControls->displayTime(m_currentTime);
    m_pControls->updateCellsFull(m_numNodesFull);
    std::for_each(m_activeNodes.begin(), m_activeNodes.end(), ResetSimNode());
    std::for_each(m_triangles.begin(), m_triangles.end(), ResetTriangleColor());
}

/**
 * Checks if a snapshot should be taken in that cycle and takes such a snapshot if apropriate.
 */
void RTMSimulator::takeSnapShotIfRequired()
{
    if(m_fTakePictures && m_timeOfLastSnapShot <= (m_currentTime - m_pictureTimeInterval))
    {
        QString time = QString().setNum(m_currentTime);
        for(int firstDot = 5-time.indexOf('.'); firstDot>0; firstDot--)
            time = time.prepend('0');
        QString strFileName = "Scene_" + time + ".png";
        takeSnapShot(strFileName);
        m_timeOfLastSnapShot = static_cast<int>(m_currentTime/m_pictureTimeInterval)*m_pictureTimeInterval;
    }
}

/**
 * @returns Simulation time of last snapshot. (sec)
 */
double RTMSimulator::timeOfLastSnapShot()
{
    return m_timeOfLastSnapShot;
}

/**
 * Updates the time when the latest snapshot was taken with the provided argument.
 * @param time Time when latest snapshot was taken. (sec)
 */
void RTMSimulator::setTimeOfLastSnapShot(double time)
{
    m_timeOfLastSnapShot = time;
}

/**
 * @returns Current cycle number of the running simulation.
 */
int RTMSimulator::currentCycle()
{
    return m_currentCycle;
}

/**
 * Updates the current cycle number with the provided argument.
 * @param cycle New cycle number.
 */
void RTMSimulator::setCurrentCycle(int cycle)
{
    m_currentCycle=cycle;
}

/**
 * @returns Number of nodes that reached a completely filled state.
 */
int RTMSimulator::numNodesFull()
{
    return m_numNodesFull;
}

/**
 * Updates the number of nodes that reached a completely filled state.
 * @param num New number of full nodes.
 */
void RTMSimulator::setNumNodesFull(int num)
{
    m_numNodesFull=num;
    m_pControls->updateCellsFull(m_numNodesFull);
}

/**
 * Takes a snapshot from the simulation visualization and stores this snapshot to a file.
 * @param strName Name of the file where to store the snapshot.
 */
void RTMSimulator::takeSnapShot( QString strName)
{
    m_pViewerWidget->createSnapShot(strName);
}

/**
 * Changes the size of the node representing visual objects.
 * @param size New visual size of a node.
 */
void RTMSimulator::setCellSize(float size)
{
    m_cellSize = size;
    std::for_each(m_activeNodes.begin(), m_activeNodes.end(), SetCellSize(size));
    std::for_each(m_sweptNodes.begin(), m_sweptNodes.end(), SetCellSize(size));
}

/**
 * @returns Current visual size of the nodes.
 */
float RTMSimulator::cellSize()
{
    return m_cellSize;
}

/**
 * @returns Pointer to the camera object, which provides a view on the simulation visualization.
 */
SoCamera* RTMSimulator::camera()
{
    return m_pViewerWidget->getCamera();
}

/**
 * Moves the camera object to a specific position.
 * @param x New X position of the camera
 * @param y New Y position of the camera
 * @param z New Z position of the camera
 */
void RTMSimulator::moveCamera(float x, float y, float z)
{
    m_pViewerWidget->moveCamera(x,y,z);
    m_fUpdateCameraPosition = false;
}

/**
 * Creates a rotation for the camera based on a quaternion.
 */
void RTMSimulator::rotateCamera(float val0, float val1, float val2, float val3)
{
    m_pViewerWidget->rotateCamera(val0,val1,val2,val3);
}

/**
 * Resizes the translations in the graph by a factor.
 * @param factor Factor for resizing the original translations.
 */
void RTMSimulator::rescaleTranslations(double factor)
{
    setTranslationFactor(factor);
    std::for_each(m_activeNodes.begin(), m_activeNodes.end(), RescaleTranslation(factor));
    std::for_each(m_sweptNodes.begin(), m_sweptNodes.end(), RescaleTranslation(factor));
    std::for_each(m_triangles.begin(), m_triangles.end(), RescaleTriangle(factor));

    m_pViewerWidget->viewAll();
    recalculateNeighborDistances();
    m_fRecalculateMedianl = true;
}

/**
 * Recalculates the distance between neighbors.
 * This function should be called after loading data from an external file,
 * to rebuild the neighbor distance maps.
 */
void RTMSimulator::recalculateNeighborDistances()
{
    std::for_each(m_activeNodes.begin(), m_activeNodes.end(), RecalcNeighborDist());
    std::for_each(m_sweptNodes.begin(), m_sweptNodes.end(), RecalcNeighborDist());
}

void RTMSimulator::showInfoHelp()
{
    m_pInfoHelp->show();
}

void RTMSimulator::open_clicked()
{
    QSettings settings;
    settings.beginGroup("importexport");
    QString dir = settings.value("path").toString();
#if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
    QString file = QFileDialog::getOpenFileName(this,tr("Choose a file to import"),dir);
#else
    QString file = QFileDialog::getOpenFileName(this,tr("Choose a file to import"),dir,"RTMSimulator Models (*.rtms);;gmsh files(*.msh *.iges *.igs);;Marc files (*.mac *.marc);;Ansys files (*.db *.ansys);;Nastran files (*.nas *.nastran);;All Files (*.*)");
#endif
    if(0 != file.length())
    {
        settings.setValue("path", file);
        importFile(file);
    }

    settings.endGroup();
}

void RTMSimulator::save_clicked()
{
    QSettings settings;
    settings.beginGroup("importexport");
    QString dir = settings.value("path").toString();
    QString file = QFileDialog::getSaveFileName(this,tr("Choose destination file to export data"),dir,tr("RTMSimulator scenes (*.rtms)"));

    if(0 != file.length())
    {
        settings.setValue("path",file);
        exportToFile(file);
    }
    settings.endGroup();
}


#ifdef DEBUG
void RTMSimulator::printNodeTable()
{
    std::string fileName = "NodeTable" + QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss").toStdString() + ".txt";
    std::ofstream file(fileName.c_str());
    file << "Active Nodes" << std::endl;
    writeNodeSettingsToFile(file,&m_activeNodes);
    file << "swept Nodes" << std::endl;
    writeNodeSettingsToFile(file,&m_sweptNodes);

    std::cout << "finished writing to file" << std::endl;
}

void RTMSimulator::writeNodeSettingsToFile(std::ofstream& file, std::map<int,SimNode*>* nodes)
{
    for(std::map<int,SimNode*>::iterator it = nodes->begin(); it != nodes->end(); it++)
    {
        file << (*it).first << "\t";
        SimNode* node = (*it).second;

        std::set<SimNode*>* set_directNeighbors = node->getDirectNeighbors();
        std::vector<SimNode*>* vector_gradeNeighbors = node ->getGradeNeighbors();
        std::set<SimNode*>* set_sweptNeighbors = node->getsweptNeighbors();
        std::set<SimNode*>* set_nodesThatReferenceMe = node->getNodesThatReferenceMe();
        std::set<SimNode*>* set_adoptedDirectNeighbors = node->getAdoptedDirectNeighbors();
        std::multimap<double, SimNode*>* multimap_neighbors = node->getNeighbors();

        std::vector<int> directNeighbors;
        std::vector<int> gradeNeighbors;
        std::vector<int> sweptNeighbors;
        std::vector<int> nodesThatReferenceMe;
        std::vector<int> adoptedDirectNeighbors;
        std::vector<int> usedNeighbors;

        for(std::set<SimNode*>::iterator it = set_directNeighbors->begin(); it != set_directNeighbors->end(); it++)
        {
            directNeighbors.push_back((*it)->id());
        }
        for(std::vector<SimNode*>::iterator it = vector_gradeNeighbors->begin(); it != vector_gradeNeighbors->end(); it++)
        {
            gradeNeighbors.push_back((*it)->id());
        }
        for(std::set<SimNode*>::iterator it = set_sweptNeighbors->begin(); it != set_sweptNeighbors->end(); it++)
        {
            sweptNeighbors.push_back((*it)->id());
        }
        for(std::set<SimNode*>::iterator it = set_nodesThatReferenceMe->begin(); it != set_nodesThatReferenceMe->end(); it++)
        {
            nodesThatReferenceMe.push_back((*it)->id());
        }
        for(std::set<SimNode*>::iterator it = set_adoptedDirectNeighbors->begin(); it != set_adoptedDirectNeighbors->end(); it++)
        {
            directNeighbors.push_back((*it)->id());
        }
        for(std::multimap<double,SimNode*>::iterator it = multimap_neighbors->begin(); it != multimap_neighbors->end(); it++)
        {
            usedNeighbors.push_back((*it).second->id());
        }

        sort(directNeighbors.begin(),directNeighbors.end());
        sort(gradeNeighbors.begin(),gradeNeighbors.end());
        sort(sweptNeighbors.begin(),sweptNeighbors.end());
        sort(nodesThatReferenceMe.begin(),nodesThatReferenceMe.end());
        sort(adoptedDirectNeighbors.begin(),adoptedDirectNeighbors.end());
        sort(usedNeighbors.begin(),usedNeighbors.end());

        file << "--DirectNeighbors: NUM: " << set_directNeighbors->size() << " Nodes: ";
        std::ostream_iterator<int> ofit0(file, " ");
        copy(directNeighbors.begin(),directNeighbors.end(),ofit0);
        file << std::endl;

        file << "\t--GradeNeighbors: NUM: " << vector_gradeNeighbors->size() << " Nodes: ";
        std::ostream_iterator<int> ofit1(file, " ");
        copy(gradeNeighbors.begin(),gradeNeighbors.end(),ofit1);
        file << std::endl;

        file << "\t--sweptNeighbors: NUM: " << set_sweptNeighbors->size() << " Nodes: ";
        std::ostream_iterator<int> ofit2(file, " ");
        copy(sweptNeighbors.begin(),sweptNeighbors.end(),ofit2);
        file << std::endl;

        file << "\t--NodesThatReferenceMe: NUM: " << set_nodesThatReferenceMe->size() << " Nodes: ";
        std::ostream_iterator<int> ofit3(file, " ");
        copy(nodesThatReferenceMe.begin(),nodesThatReferenceMe.end(),ofit3);
        file << std::endl;

        file << "\t--AdoptedDirectNeighbors: NUM: " << set_adoptedDirectNeighbors->size() << " Nodes: ";
        std::ostream_iterator<int> ofit4(file, " ");
        copy(adoptedDirectNeighbors.begin(),adoptedDirectNeighbors.end(),ofit4);
        file << std::endl;
    }
}

void RTMSimulator::createTestNode()
{
    m_testNode = m_pViewerWidget->addNode(0,0,0,0);
}

void RTMSimulator::deleteTestNode()
{
    m_pViewerWidget->removeNode(m_testNode);
}

void RTMSimulator::createTestShape()
{
    m_pViewerWidget->addShape();
}

void RTMSimulator::setCellSize4()
{
    for(std::map<int,SimNode*>::iterator it = m_activeNodes.begin(); it != m_activeNodes.end(); it++)
    {
        (*it).second->setCellSize(0.2f);
    }

    for(std::map<int,SimNode*>::iterator it = m_sweptNodes.begin(); it != m_sweptNodes.end(); it++)
    {
        (*it).second->setCellSize(0.2f);
    }
}

void RTMSimulator::moveCam()
{
    m_pViewerWidget->moveCamera(0,0,10);
}

void RTMSimulator::testMultipleTriangleStrips()
{
    m_pViewerWidget->testMultipleTriangleStrips();

    TriangleSettings settings;
    settings.triangleId = 0;
    settings.Ax = 2;
    settings.Ay = 2;
    settings.Az = 2;
    settings.Bx = 3;
    settings.By = 2;
    settings.Bz = 2;
    settings.Cx = 2;
    settings.Cy = 3;
    settings.Cz = 2;
    m_pViewerWidget->addTriangle(settings);
}


#endif //_DEBUG
