/*
Triangle.cpp
Written by Matthew Fisher

ComplexMesh is a complex (edge-based) mesh structure.  Triangle represents a triangle in a ComplexMesh.
*/

HalfEdge& Triangle::GetHalfEdge(const FullEdge &FullE) const
{
    for(UINT EdgeIndex = 0; EdgeIndex < 3; EdgeIndex++)
    {
        if(GetFullEdge(EdgeIndex) == FullE)
        {
            return *(_HalfEdges[EdgeIndex]);
        }
    }
    SignalError("Triangle::GetHalfEdge edge not found.");
    return HalfEdge::Invalid;
}

Vertex& Triangle::GetOtherVertex(const FullEdge &FullE) const
{
    for(UINT VertexIndex = 0; VertexIndex < 3; VertexIndex++)
    {
        Vertex &CurVertex = *(_Vertices[VertexIndex]);
        if(CurVertex != FullE.GetVertex(0) && CurVertex != FullE.GetVertex(1))
        {
            return CurVertex;
        }
    }
    SignalError("Triangle::GetOtherVertex vertex not found.");
    return Vertex::Invalid;
}

bool Triangle::ContainsEdge(const FullEdge &FullE) const
{
    for(UINT i = 0; i < 3; i++)
    {
        if(GetFullEdge(i) == FullE)
        {
            return true;
        }
    }
    return false;
}

bool Triangle::ContainsVertex(const Vertex &Vtx) const
{
    for(UINT VertexIndex = 0; VertexIndex < 3; VertexIndex++)
    {
        if(GetVertex(VertexIndex) == Vtx)
        {
            return true;
        }
    }
    return false;
}

bool Triangle::BordersTriangle(const Triangle &T) const
{
    for(UINT TriangleIndex = 0; TriangleIndex < 3; TriangleIndex++)
    {
        if(GetNeighboringTriangle(TriangleIndex) == T)
        {
            return true;
        }
    }
    return false;
}

FullEdge& Triangle::GetOtherEdge(const Vertex &Vtx) const
{
    for(UINT EdgeIndex = 0; EdgeIndex < 3; EdgeIndex++)
    {
        FullEdge &CurFullEdge = _HalfEdges[EdgeIndex]->AcrossEdge();
        if(!CurFullEdge.ContainsVertex(Vtx))
        {
            return CurFullEdge;
        }
    }
    SignalError("Triangle::GetOtherEdge duplicity error.");
    return FullEdge::Invalid;
}

FullEdge& Triangle::GetSharedEdge(const Triangle &T) const
{
    for(UINT EdgeIndex = 0; EdgeIndex < 3; EdgeIndex++)
    {
        FullEdge &CurFullEdge = _HalfEdges[EdgeIndex]->AcrossEdge();
        if(CurFullEdge.GetOtherTriangle(*this) == T)
        {
            return CurFullEdge;
        }
    }
    SignalError("Triangle::GetSharedEdge fatal error.");
    return FullEdge::Invalid;
}

FullEdge& Triangle::GetSharedEdge(const Vertex &V1, const Vertex &V2) const
{
    for(UINT EdgeIndex = 0; EdgeIndex < 3; EdgeIndex++)
    {
        FullEdge &CurFullEdge = _HalfEdges[EdgeIndex]->AcrossEdge();
        if(CurFullEdge.ContainsVertex(V1) && CurFullEdge.ContainsVertex(V2))
        {
            return CurFullEdge;
        }
    }
    SignalError("Triangle::GetSharedEdge fatal error.");
    return FullEdge::Invalid;
}

float Triangle::Area() const
{
    Vec3f V1 = GetVertex(1).Pos() - GetVertex(0).Pos();
    Vec3f V2 = GetVertex(2).Pos() - GetVertex(0).Pos();
    return 0.5f * Vec3f::Cross(V1, V2).Length();
}

Vec3f Triangle::Center() const
{
    return (GetVertex(0).Pos() + GetVertex(1).Pos() + GetVertex(2).Pos()) / 3.0f;
}

bool Triangle::IsBoundary() const
{
    return (GetFullEdge(0).Boundary() || 
            GetFullEdge(1).Boundary() || 
            GetFullEdge(2).Boundary());
}

bool Triangle::OrientedWith(const FullEdge &FullE) const
{
    UINT Index0 = GetVertexLocalIndex(FullE.GetVertex(0));
    UINT Index1 = GetVertexLocalIndex(FullE.GetVertex(1));
    return (Index1 == (Index0 + 1) % 3);
}

Vec3f Triangle::Normal() const
{
    Vec3f V1 = _Vertices[1]->Pos() - _Vertices[0]->Pos();
    Vec3f V2 = _Vertices[2]->Pos() - _Vertices[0]->Pos();
    return Vec3f::Normalize(Vec3f::Cross(V1, V2));
}

UINT Triangle::GetVertexLocalIndex(const Vertex &VCompare) const
{
    for(UINT i = 0; i < 3; i++)
    {
        if(GetVertex(i) == VCompare)
        {
            return i;
        }
    }
    SignalError("Triangle::GetVertexIndex Fatal Error.");
    return UINT(-1);
}

double Triangle::GetAngle(const Vertex &Vtx) const
{
    UINT BaseIndex = GetVertexLocalIndex(Vtx);
    Vec3f E1 = _Vertices[(BaseIndex + 1) % 3]->Pos() - Vtx.Pos();
    Vec3f E2 = _Vertices[(BaseIndex + 2) % 3]->Pos() - Vtx.Pos();
    return Vec3f::AngleBetween(E1, E2);
}

double Triangle::GetCotangent(const Vertex &Vtx) const
{
    UINT BaseIndex = GetVertexLocalIndex(Vtx);
    Vec3f E1 = _Vertices[(BaseIndex + 1) % 3]->Pos() - Vtx.Pos();
    Vec3f E2 = _Vertices[(BaseIndex + 2) % 3]->Pos() - Vtx.Pos();
    double Denominator = Vec3f::Dot(Vec3f::Cross(E1, E2), Normal());
    //Assert(Denominator != 0.0f && Denominator == Denominator, "invalid denominator");
    if(Denominator == 0.0f)
    {
        return 0.0f;
    }
    else
    {
        return Vec3f::Dot(E1, E2) / Denominator;
    }
}

float Triangle::ThetaFromTangent(const Vec3f &V) const
{
    Vec3f MyNormal = Normal(), Tangent1, Tangent2;
    Vec3f::CompleteOrthonormalBasis(MyNormal, Tangent1, Tangent2);
    float Theta = Vec3f::AngleBetween(V, Tangent1);
    if(Vec3f::Dot(Vec3f::Cross(Tangent1, V), MyNormal) > 0.0f)
    {
        return Theta;
    }
    else
    {
        return -Theta;
    }
}

Vec3f Triangle::TangentFromTheta(float Theta) const
{
    Vec3f MyNormal = Normal(), Tangent1, Tangent2;
    Vec3f::CompleteOrthonormalBasis(MyNormal, Tangent1, Tangent2);
    Matrix4 Rotation = Matrix4::Rotation(MyNormal, Theta);
    return Rotation.TransformNormal(Tangent1);
}

Vec3f Triangle::EvaluateVFieldGradient(const Vector<double> &OneForm, float v, float w) const
{
    Matrix4 Transform = Matrix4::Face(Normal(), Vec3f::eZ) * Matrix4::Translation(-Center());
    Vec3f V[3];
    for(UINT VertexIndex = 0; VertexIndex < 3; VertexIndex++)
    {
        V[VertexIndex] = Transform.TransformPoint(_Vertices[VertexIndex]->Pos());
    }

    float a = float(OneForm[_HalfEdges[0]->AcrossEdge().Index()]);
    float b = float(OneForm[_HalfEdges[1]->AcrossEdge().Index()]);
    float c = float(OneForm[_HalfEdges[2]->AcrossEdge().Index()]);
    if(!OrientedWith(_HalfEdges[0]->AcrossEdge()))
    {
        a = -a;
    }
    if(!OrientedWith(_HalfEdges[1]->AcrossEdge()))
    {
        b = -b;
    }
    if(!OrientedWith(_HalfEdges[2]->AcrossEdge()))
    {
        c = -c;
    }

    float NumX = V[0].y * a + V[1].y * b + V[2].y * c;
    float DenomX = (V[1].x*V[0].y - V[2].x*V[0].y - V[0].x*V[1].y + V[2].x*V[1].y + V[0].x*V[2].y - V[1].x*V[2].y);

    float NumY = V[0].x * a + V[1].x * b + V[2].x * c;
    float DenomY = (V[2].x*(V[0].y - V[1].y) + V[0].x*(V[1].y - V[2].y) + V[1].x*(-V[0].y + V[2].y));

    Vec3f Gradient = Vec3f::Origin;
    if(DenomX != 0.0f)
    {
        Gradient.x = NumX / DenomX;
    }
    if(DenomY != 0.0f)
    {
        Gradient.y = NumY / DenomY;
    }
    return Transform.Inverse().TransformNormal(Gradient);
}

Vec3f Triangle::EvaluateVField(const Vector<double> &OneForm, float v, float w) const
{
    Matrix4 Rotation = Matrix4::Rotation(Normal(), Math::PIf / 2.0f);
    Vec3f ab = _Vertices[1]->Pos() - _Vertices[0]->Pos();
    Vec3f ac = _Vertices[2]->Pos() - _Vertices[0]->Pos();

    ab = Rotation.TransformNormal(ab);
    ac = Rotation.TransformNormal(ac);
    
    float a = float(OneForm[_HalfEdges[0]->AcrossEdge().Index()]);
    float b = float(OneForm[_HalfEdges[1]->AcrossEdge().Index()]);
    float c = float(OneForm[_HalfEdges[2]->AcrossEdge().Index()]);
    if(!OrientedWith(_HalfEdges[0]->AcrossEdge()))
    {
        a = -a;
    }
    if(!OrientedWith(_HalfEdges[1]->AcrossEdge()))
    {
        b = -b;
    }
    if(!OrientedWith(_HalfEdges[2]->AcrossEdge()))
    {
        c = -c;
    }

    float abc = a + b + c;

    return ((v * abc - b) * ab + (w * abc - c) * ac) / (2.0f * Area());
}

/*float Triangle::GetAngle(Vertex *VBase)
{
    int VBaseIndex, VEdgeIndex[2];
    if(V[0] == VBase) VBaseIndex = 0;
    if(V[1] == VBase) VBaseIndex = 1;
    if(V[2] == VBase) VBaseIndex = 2;
    Vec3f Edge1, Edge2;
    Triplet(VBaseIndex, VEdgeIndex);

    float a = E[VEdgeIndex[0]]
    //Edge1 = V[VEdgeIndex[0]]->Pos - V[VBaseIndex]->Pos; Edge1.Normalize();
    //Edge2 = V[VEdgeIndex[1]]->Pos - V[VBaseIndex]->Pos; Edge2.Normalize();
    return acosf(Vec3f::Dot(Edge1,Edge2));
}*/