
#include "ImportGmesh.h"
#include "RTMSimulator.h"
#include "Import.h"

#include <QApplication>
#include <QDesktopWidget>
#include <QString>
#include <QWidget>
#include <QHBoxLayout>
#include <QLabel>
#include <QFile>
#include <QStringList>
#include <QTextStream>
#include <QRegExp>

#include <cmath>
#include <iostream>
#include <list>

/**
 * Creates a temporary import object to read in a Gmsh file.
 * @param pFile File to read from
 * @param pSimulator Pointer to the RTMSimulator object to read into.
 */
ImportGmesh::ImportGmesh(QFile* pFile, RTMSimulator* pSimulator):
        ImportFormat(pSimulator),
        m_numberOfNodes(0),
        m_numberOfElements(0)
{
    read(pFile);
}

/**
 * Creates a temporary import object to read in a Ansys file.
 * @param strFile Fully qualified path to file to read from
 * @param pSimulator Pointer to the RTMSimulator object to read into.
 */
ImportGmesh::ImportGmesh(QString strFile, RTMSimulator* pSimulator):
        ImportFormat(pSimulator),
        m_numberOfNodes(0),
        m_numberOfElements(0)
{
    QFile* pFile = new QFile(strFile);
    read(pFile);
    pFile->close();
}

/**
 * Reads in all the necessary information from a Gmsh file.
 * @param pFile Pointer to the file to read from
 */
void ImportGmesh::read(QFile* pFile)
{
    InfoBox infoBox("reading File Information...");
    qApp->processEvents();

    infoBox.setText("importing Nodes...");
    qApp->processEvents();
    if(!importNodes(pFile))
    {
        //here we should display an error message
        infoBox.hide();
        return;
    }

    infoBox.setText("importing Connections...");
    qApp->processEvents();
    if(!importConnections(pFile))
    {
        //here we should display an error message
        infoBox.hide();
        return;
    }

    infoBox.hide();
}

/**
 * Imports all the node information from a Gmsh file.
 * @param pFile Pointer to the file to read from
 */
bool ImportGmesh::importNodes(QFile* pFile)
{
    QString line;
    while (!pFile->atEnd())
    {
        line = pFile->readLine();
        if(line.startsWith("$Nodes",Qt::CaseInsensitive))
        {
            line = pFile->readLine();
            line = pFile->readLine();
            while(!line.contains("$EndNodes",Qt::CaseInsensitive))
            {
                int nodeId = 0;
                QStringList tokens = line.split(QRegExp("\\s+"), QString::SkipEmptyParts);

                std::vector<double> coords;
                int pos = 0;
                for(QStringList::iterator it = tokens.begin(); it != tokens.end(); it++)
                {
                    bool ok;
                    QString token = (*it);

                    switch(pos)
                    {
                    case 0:
                        nodeId = token.toInt(&ok);
                        if(!ok)
                            return false;
                        break;
                    case 1:
                    case 2:
                    case 3:
                        double coord = token.toDouble(&ok);
                        if(!ok)
                            return false;
                        else
                        {
                            coords.push_back(coord);
                        }
                        break;
                    }
                    pos++;
                }

                if(coords.size() >= 1)
                {
                    while(coords.size() < 3)
                    {
                        coords.push_back(0);
                    }
                    m_pSimulator->addNode(nodeId,coords[0],coords[1],coords[2]);
                }
                line = pFile->readLine();
            }
        }
    }

    pFile->reset();
    return true;
}

/**
 * Imports all the connection information from a Gmsh file.
 * @param pFile Pointer to the file to read from
 */
bool ImportGmesh::importConnections(QFile* pFile)
{
    QString line;
    while (!pFile->atEnd())
    {
        line = pFile->readLine();
        if(line.startsWith("$Elements",Qt::CaseInsensitive))
        {
            line = pFile->readLine();
            line = pFile->readLine();
            while(!line.contains("$EndElements"))
            {
                int elementId = 0;
                int elementType = 0;
                int numberOfTags = 0;
                std::list<int> vertices;
                int pos=0;
                QStringList tokens = line.split(QRegExp("\\s+"), QString::SkipEmptyParts);
                for(QStringList::iterator it = tokens.begin(); it != tokens.end(); it++)
                {
                    bool ok;
                    QString token = (*it);

                    switch(pos){
                    case 0:
                        elementId = token.toInt(&ok);
                        if(!ok)
                            return false;
                        break;
                    case 1:
                        elementType = token.toInt(&ok);
                        if(!ok)
                            return false;
                        break;
                    case 2:
                        numberOfTags = token.toInt(&ok);
                        if(!ok)
                            return false;
                        break;
                    default:
                        int nodeNumber = token.toInt(&ok);
                        if(!ok)
                            return false;
                        if(pos > (2 + numberOfTags))
                        {
                            vertices.push_back(nodeNumber);
                        }
                        break;
                    }
                    pos++;
                }

                if( elementType == 2 || elementType == 3 )
                {
                    // elementType 2: triangle
                    // elementType 3: quadrangle
                    createConnections(&vertices);
                }
                else if( elementType == 4)
                {
                    // elementType 4: tetrahedron
                    if(vertices.size() == 4)
                    {
                        std::list<int> verticesTriangle1(vertices);
                        verticesTriangle1.pop_front();
                        createConnections(&verticesTriangle1);

                        std::list<int> verticesTriangle2(vertices);
                        verticesTriangle2.erase(++(verticesTriangle2.begin()));
                        createConnections(&verticesTriangle2);

                        std::list<int> verticesTriangle3(vertices);
                        verticesTriangle3.erase(++++(verticesTriangle3.begin()));
                        createConnections(&verticesTriangle3);

                        std::list<int> verticesTriangle4(vertices);
                        verticesTriangle4.pop_back();
                        createConnections(&verticesTriangle4);
                    }
                }
                line = pFile->readLine();
            }
        }
    }

    pFile->reset();
    return true;
}

/**
 * Creates all the connections to build up a triangle from the list of vertices.
 * @param vertices List of vertices to build up a triangle
 */
void ImportGmesh::createConnections(std::list<int>* vertices)
{
    if(vertices->size() >= 2)
    {
        for(std::list<int>::const_iterator it = vertices->begin(); it != vertices->end(); it++)
        {
            std::list<int>::const_iterator itNode2 = it;
            itNode2++;
            if(itNode2 == vertices->end())
                itNode2 = vertices->begin();

            m_pSimulator->setDirectNeighbor(*it,*itNode2);
            m_pSimulator->setDirectNeighbor(*itNode2,*it);
        }
        addShapePart(vertices);
    }
}

