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

#include <QDataStream>
#include <QFile>
#include <QDebug>


int ImportRTMS::format_version = 0;
double ImportRTMS::translation_factor = 1.0;

/**
 * This function should be called to read in a scene graph from an existing RTMS file.
 * @param file File that should be read into the scene graph
 * @param pSimulator Pointer to the RTMSimulator object, the RTMS scene should be imported into.
 */
void ImportRTMS::import(QFile* file, RTMSimulator* pSimulator)
{
    readFormatVersion(file);
    QDataStream in(file);

    readSettings(in, pSimulator);
    readActiveNodes(in, pSimulator);
    readSweptNodes(in, pSimulator);
    readNodesNeighbors(in, pSimulator); //active nodes first
    readNodesNeighbors(in, pSimulator); //swept nodes afterwards
    readTriangles(in, pSimulator);
    readTriangleVertices(in, pSimulator);
}

/**
 * Read in the RTMS file format version of the file.
 * @param file File to read from
 */
void ImportRTMS::readFormatVersion(QFile* file)
{
    file->reset();
    QString line = file->readLine();
    if(line.contains("FormatVersion",Qt::CaseInsensitive))
    {

        QString formatVersion = line.right(10);
        format_version = atoi(formatVersion.toAscii());
    }
    else
    {
        format_version = 0;
    }
}

/**
 * Read in the settings for the simulation process.
 * @param in QDataStream object to read from.
 * @param pSiulator Pointer to the RTMSimulator object, the settings should be imported into.
 */
void ImportRTMS::readSettings(QDataStream& in, RTMSimulator* pSimulator)
{
    CameraPosition cp;
    in.readRawData((char*)&cp,sizeof(CameraPosition));

    SceneSettings settings;
    if(format_version < 20080813)
    {
        in.readRawData((char*)&settings,sizeof(SceneSettings)-sizeof(double)*11);
    }
    else
    {
        in.readRawData((char*)&settings,sizeof(SceneSettings));
    }

    pSimulator->setQ(settings.q);
    pSimulator->setR(settings.r);
    pSimulator->setS(settings.s);
    pSimulator->setViscosity(settings.viscosity);
    pSimulator->setPictureTimeInterval(settings.pictureTimeInterval);
    pSimulator->setTimeOfLastSnapShot(settings.timeOfLastSnapshot);
    pSimulator->setCurrentTime(settings.currentTime);
    pSimulator->setVacuumPressure(settings.vacuumPressure);
    pSimulator->setCellSize(settings.cellSize);

    pSimulator->setShowSweptNodes(settings.showSweptNodes);
    pSimulator->setShowNeighbors(settings.markNeighbors);
    pSimulator->setShowDirectNeighbors(settings.markDirectNeighbors);
    pSimulator->setShowPermeabilityGradient(false);
    pSimulator->takePictures(settings.takePictures);

    if(format_version >= 20080813)
    {
        ImportRTMS::translation_factor = settings.translationFactor;
    }
    else
    {
        ImportRTMS::translation_factor = 1.0;
    }
    pSimulator->setTranslationFactor(translation_factor);
    pSimulator->moveCamera(cp.x,cp.y,cp.z);
    pSimulator->rotateCamera(cp.val0,cp.val1,cp.val2,cp.val3);

    pSimulator->invalidatePermeabilityGlobals();
}

/**
 * Read in active nodes from the data stream object.
 * @param in QDataStream object to read from
 * @param pSimulator Pointer to the RTMSimulator object, the active nodes should be imported into.
 */
void ImportRTMS::readActiveNodes(QDataStream& in, RTMSimulator* pSimulator)
{
    int numNodes;
    in.readRawData((char*)&numNodes,sizeof(int));
    for(int i=0; i < numNodes; i++)
    {
        NodeSettings settings;
        in.readRawData((char*)&settings,sizeof(NodeSettings));

        SimNode* node = pSimulator->addNode( settings.nodeId, settings.x, settings.y, settings.z );

        node->setInjectionPoint(settings.injectionPoint,settings.injectionPressure);
        node->setInjectionDiameter(settings.injectionDiameter);
        node->memorizePressure(settings.pressure);
        node->commitPressure();
        node->setPermeability(settings.permeability);
        node->setCavityHeight(settings.cavityHeight);
        node->setPorosity(settings.porosity);
        node->markForSweep(settings.markedForSweep);
        node->markGreen(settings.markedGreen);
        node->updateColor();
    }
}

/**
 * Read in swept nodes from the data stream object.
 * @param in QDataStream object to read from
 * @param pSimulator Pointer to the RTMSimulator object, the swept nodes should be imported into.
 */
void ImportRTMS::readSweptNodes(QDataStream& in, RTMSimulator* pSimulator)
{
    int numNodes;
    in.readRawData((char*)&numNodes,sizeof(int));
    for(int i=0; i < numNodes; i++)
    {
        NodeSettings settings;
        in.readRawData((char*)&settings,sizeof(NodeSettings));

        SimNode* node = pSimulator->addNode( settings.nodeId, settings.x, settings.y, settings.z, true);

        node->memorizePressure(settings.pressure);
        node->commitPressure();
        node->setInjectionPoint(settings.injectionPoint,settings.injectionPressure);
        node->setInjectionDiameter(settings.injectionDiameter);
        node->setPermeability(settings.permeability);
        node->setCavityHeight(settings.cavityHeight);
        node->setPorosity(settings.porosity);
        node->markForSweep(settings.markedForSweep);
        node->markGreen(settings.markedGreen);
        node->updateColor();
    }
}

/**
 * Read in neighbor information from the data stream object.
 * @param in QDataStream object to read from
 * @param pSimulator Pointer to the RTMSimulator object, the neighbor information nodes should be imported into.
 */
void ImportRTMS::readNodesNeighbors(QDataStream& in, RTMSimulator* pSimulator)
{
    int numDirectNeighborhoods;
    int numNeighborhoods;
    int numSweptNeighborhoods;
    int numAdoptedNeighborhoods;

    in.readRawData((char*)&numDirectNeighborhoods,  sizeof(int));
    in.readRawData((char*)&numNeighborhoods,        sizeof(int));
    in.readRawData((char*)&numSweptNeighborhoods, sizeof(int));
    in.readRawData((char*)&numAdoptedNeighborhoods, sizeof(int));

    //read direct neighborhood
    for(int i=0; i < numDirectNeighborhoods; i++)
    {
        Neighborhood neighborhood;
        in.readRawData((char*)&neighborhood,sizeof(Neighborhood));
        SimNode* node1 = pSimulator->getNodeById(neighborhood.node1);
        SimNode* node2 = pSimulator->getNodeById(neighborhood.node2);
        if(node1 != 0 && node2 != 0)
            node1->addDirectNeighbor(node2);
        else
        {
            qDebug() << "we should fix this!!! such nodes should not be exported at all";
        }
    }

    //read neighborhood
    for(int i=0; i < numNeighborhoods; i++)
    {
        Neighborhood neighborhood;
        in.readRawData((char*)&neighborhood,sizeof(Neighborhood));
        pSimulator->getNodeById(neighborhood.node1)->addNeighbor(pSimulator->getNodeById(neighborhood.node2));
    }

    //read swept neighborhood
    for(int i=0; i < numSweptNeighborhoods; i++)
    {
        Neighborhood neighborhood;
        in.readRawData((char*)&neighborhood,sizeof(Neighborhood));
        SimNode* node1 = pSimulator->getNodeById(neighborhood.node1);
        SimNode* node2 = pSimulator->getNodeById(neighborhood.node2);
        if(node1 != NULL && node2 != NULL)
            node1->addNeighbor(node2);
        else
            qDebug() << "this node should not be stored in the rtms file";
    }

    //read adopted neighborhood
    for(int i=0; i < numAdoptedNeighborhoods; i++)
    {
        Neighborhood neighborhood;
        in.readRawData((char*)&neighborhood,sizeof(Neighborhood));
        SimNode* node1 = pSimulator->getNodeById(neighborhood.node1);
        SimNode* node2 = pSimulator->getNodeById(neighborhood.node2);
        if(node1 != NULL && node2 != NULL)
            node1->addAdoptedDirectNeighbor(node2);
        else
            qDebug() << "this node should not be stored in the rtms file";
    }
}

/**
 * Read in triangles from the data stream object.
 * @param in QDataStream object to read from
 * @param pSimulator Pointer to the RTMSimulator object, the triangles should be imported into.
 */
void ImportRTMS::readTriangles(QDataStream& in, RTMSimulator* pSimulator)
{	
    int numTriangles;
    in.readRawData((char*)&numTriangles,sizeof(int));

    for(int i=0; i< numTriangles; i++)
    {
        TriangleSettings settings;
        in.readRawData((char*)&settings,sizeof(TriangleSettings));
        pSimulator->addTriangle(settings);
    }
}

/**
 * Read in vertex information from the data stream object.
 * @param in QDataStream object to read from
 * @param pSimulator Pointer to the RTMSimulator object, the triangle vertices should be imported into.
 */
void ImportRTMS::readTriangleVertices(QDataStream& in, RTMSimulator* pSimulator)
{
    int numTriangles;
    in.readRawData((char*)&numTriangles,sizeof(int));

    std::map<int,SimTriangle*>* triangles = pSimulator->triangles();

    for(int i=0; i< numTriangles; i++)
    {
        int triangleId;
        in.readRawData((char*)&triangleId,sizeof(int));
        int numVertexNodes;
        in.readRawData((char*)&numVertexNodes,sizeof(int));

        SimTriangle* triangle = triangles->operator [](triangleId);

        for(int i=0; i< numVertexNodes; i++)
        {
            int vertexNodeId;
            in.readRawData((char*)&vertexNodeId,sizeof(int));
            SimNode* node = pSimulator->getNodeById(vertexNodeId);
            if(node != NULL)
                triangle->addVertexNode(node);
            else
                qDebug() << "this node should not be stored in the rtms file";
        }
    }
}
