#include "SimTriangle.h"

#include "Export.h"
#include "RTMSimulator.h"

#include <algorithm>

#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoCoordinate3.h>
#include <Inventor/nodes/SoTriangleStripSet.h>

/**
 * Creates a SimTriangle object that will be displayed in the viewer widget of the RTMSimulator.
 * @param vertexA Pointer to the first SimNode to be used as vertex for the triangle.
 * @param vertexB Pointer to the second SimNode to be used as vertex for the triangle.
 * @param vertexC Pointer to the third SimNode to be used as vertex for the triangle.
 */
SimTriangle::SimTriangle(int id, SimNode* vertexA, SimNode* vertexB, SimNode* vertexC):
        m_triangleId(id)
{
    addVertexNode(vertexA);
    addVertexNode(vertexB);
    addVertexNode(vertexC);

    double s = STATIC_STRETCH_FACTOR;
    int32_t numVerticesPerStripSet = 3;

    m_material = new SoMaterial;
    m_material->diffuseColor.setHSVValue(0.35f, 0.7f,  0.9f);
    m_material->transparency.setValue(0.0f);
    addChild(m_material);

    m_triangleCoordinates = new SoCoordinate3;
    m_triangleCoordinates->point.set1Value(0,vertexA->getX()*s, vertexA->getY()*s, vertexA->getZ()*s);
    m_triangleCoordinates->point.set1Value(1,vertexB->getX()*s, vertexB->getY()*s, vertexB->getZ()*s);
    m_triangleCoordinates->point.set1Value(2,vertexC->getX()*s, vertexC->getY()*s, vertexC->getZ()*s);

    SoTriangleStripSet *strip = new SoTriangleStripSet;
    strip->numVertices.setValues(0, 1, &numVerticesPerStripSet);
    addChild(m_triangleCoordinates);
    addChild(strip);
}

/**
 * Creates a SimTriangle object that will be displayed in the viewer widget of the RTMSimulator.
 * @param settings TriangleSettings structure that stores all the necessary information to build a SimTriangle.
 */
SimTriangle::SimTriangle(TriangleSettings& settings):
        m_triangleId(settings.triangleId)
{
    double s = 1/*STATIC_STRETCH_FACTOR*/;
    int32_t numVerticesPerStripSet = 3;

    m_material = new SoMaterial;
    m_material->diffuseColor.setHSVValue(0.35f, 0.7f,  0.9f);
    m_material->transparency.setValue(0.0f);
    addChild(m_material);

    m_triangleCoordinates = new SoCoordinate3;
    m_triangleCoordinates->point.set1Value(0,settings.Ax*s, settings.Ay*s, settings.Az*s);
    m_triangleCoordinates->point.set1Value(1,settings.Bx*s, settings.By*s, settings.Bz*s);
    m_triangleCoordinates->point.set1Value(2,settings.Cx*s, settings.Cy*s, settings.Cz*s);

    SoTriangleStripSet *strip = new SoTriangleStripSet;
    strip->numVertices.setValues(0, 1, &numVerticesPerStripSet);
    addChild(m_triangleCoordinates);
    addChild(strip);
}

/**
 * @returns ID of this SimTriangle
 */
int SimTriangle::id()
{
    return m_triangleId;
}

/**
 * This function is used when building up a new triangle.
 * It adds a new vertex to this triangle and further it adds this triangle to the simulation node's adjacent matrix.
 * @param vertexNode Simulation node to add to this triangle as a vertex.
 */
void SimTriangle::addVertexNode(SimNode* vertexNode)
{
    m_vertexNodes.insert(vertexNode);
    vertexNode->addAdjacentTriangle(this);
}

/**
 * Removes a simulation node from the vertex list of this simulation triangle.
 * This might be necessary, when a node is delete from a scene graph.
 */
void SimTriangle::removeVertexNode(SimNode* vertexNode)
{
    m_vertexNodes.erase(vertexNode);
}

/**
 * Rescales this triangle by the provided factor.
 * @param factor Factor to rescale this triangle.
 */
void SimTriangle::rescale(double factor)
{
    int numTrianglePoints = m_triangleCoordinates->point.getNum();
    for(int triangle_it = 0; triangle_it < numTrianglePoints; triangle_it++)
    {
        SbVec3f vec = *m_triangleCoordinates->point.getValues(triangle_it);
        vec = vec*(float)factor;
        m_triangleCoordinates->point.setValues(triangle_it,1,&vec);
    }
}

/**
 * Changes the transparency for this simulation triangle.
 * @param transparency New transparency for this triangle.
 */
void SimTriangle::setTransparency(float transparency)
{
    m_material->transparency.setValue(transparency);
}

/**
 * Updates the color of the triangle appropriate to the fill state.
 */
void SimTriangle::updateColor()
{
    bool hasHalfFullVertexNode = false;
    unsigned int fullVertexNodes = std::count_if(m_vertexNodes.begin(),m_vertexNodes.end(),SimNode::isFull);
    if(fullVertexNodes == 0)
    {
        hasHalfFullVertexNode = (m_vertexNodes.end() != std::find_if(m_vertexNodes.begin(),m_vertexNodes.end(),SimNode::isHalfFull));
    }


    if(fullVertexNodes == 0)
    {
        //GREEN: neither half full vertex nodes nor full vertex nodes
        m_material->diffuseColor.setHSVValue(0.35f, 0.7f,  0.9f);
    }else if(fullVertexNodes == 1 && fullVertexNodes < m_vertexNodes.size())
    {
        //LIGHT-RED: one or more, but not only full vertex nodes
        m_material->diffuseColor.setHSVValue(1.0f, 0.8f,  1.0f);
    }else if(fullVertexNodes > 1 && fullVertexNodes < m_vertexNodes.size())
    {
        //MEDIUM-RED: two or more, but not only full vertex nodes
        m_material->diffuseColor.setHSVValue(1.0f, 0.9f,  1.0f);
    }else if(fullVertexNodes == m_vertexNodes.size())
    {
        //RED: only full vertex nodes
        m_material->diffuseColor.setHSVValue(1.0f,1.0f,1.0f);
    }
}

/**
 * Resets the color of this triangle to default setting.
 */
void SimTriangle::resetColor()
{
    m_material->diffuseColor.setHSVValue(0.35f, 0.7f,  0.9f);
}

/**
 * Stores settings of this triangle in to the settings structure that is passed as a reference.
 * @param settings Reference to a settings structure, where the settings of this triangle should be written to.
 */
void SimTriangle::getTriangleSettings(TriangleSettings& settings)
{
    float Ax,Ay,Az;
    float Bx,By,Bz;
    float Cx,Cy,Cz;

    m_triangleCoordinates->point.getValues(0)->getValue(Ax, Ay, Az);
    m_triangleCoordinates->point.getValues(1)->getValue(Bx, By, Bz);
    m_triangleCoordinates->point.getValues(2)->getValue(Cx, Cy, Cz);

    settings.triangleId = m_triangleId;
    settings.Ax = Ax;
    settings.Ay = Ay;
    settings.Az = Az;
    settings.Bx = Bx;
    settings.By = By;
    settings.Bz = Bz;
    settings.Cx = Cx;
    settings.Cy = Cy;
    settings.Cz = Cz;
}

/**
 * @returns A set that contains all the vertex simulation nodes of this triangle.
 */
std::set<SimNode*>* SimTriangle::getVertexNodes()
{
    return &m_vertexNodes;
}


