//------------------------------------------------------------------------------
//  Copyright 2007-2014 by Jyh-Ming Lien and George Mason University
//  See the file "LICENSE" for more information
//------------------------------------------------------------------------------

#include "model.h"
#include "ModelGraph.h"

#ifdef WIN32
#pragma warning(disable:4244)
#endif

//
// build the model from a file
// the file will contain an obj model
// once obj file is read, vertices and facets will be built
// other information associated with facets and vertices,
// as well as edges will be build also using model graph
//

bool model::build(const string & name)
{
    //if(pts!=NULL) return false; //built

    {//build/copy model
        objReader reader(name);
        if( !reader.Read() )
            return false;
        objModel& data=reader.getModel();

        if (data.textcoords.empty())
            cerr << "! Warning: Model does not have texture coordinates. Texturing will not work." << endl;

        //allocate memory
        v_size=data.pts.size();
        t_size=data.polys.size();
        vertices=new vertex[v_size];   //
        tris=new triangle[t_size];     //
        assert(vertices&&tris);        //make sure enough memory

        //copy vertices
        for(uint i=0;i<v_size;i++){
            vertices[i].p.set(&data.pts[i].x);
            vertices[i].n.set(&data.pts[i].nx);
            vertices[i].bk_p=vertices[i].p;  //backup for modification
        }

        //copy triangles
        int tid=0;
        for(list<polygon>::iterator i=data.polys.begin();i!=data.polys.end();i++)
        {
            list<int>& ids=i->pts;
            //check if triangle
            if(ids.size()!=3){
                cerr<<"! Error: polygon "<<tid<<" is not a triangle."<<endl;
                return false;
            }
            int vid=0;
            //list<int>::iterator t = i->textures.begin();
            for (list<int>::iterator j = ids.begin(); j != ids.end(); j++, vid++)
            {
                tris[tid].v[vid]=*j;
                //copy texture coordinates. Note: it's better to keep another list of texture coords 
                //instead of copying textcoords to each triangle....but...
                //if (t != i->textures.end()) 
                //{
                //	tris[tid].texcoord[vid] = Vector2d(&data.textcoords[*t].x); //texture coordinates
                //	t++;
                //}
                vertices[*j].m_f.push_back(tid);
            }
            tid++;
        }
    }//end build/copy model


    {//build model graph and collect informations
        CModelGraph G;
        G.doInit(this);

        //create edges
        e_size=G.getEdgeSize();
        CModelEdge * ptrE=G.getEdges();
        edges=new edge[e_size];
        assert(edges);
        for(uint i=0;i<e_size;i++,ptrE=ptrE->getNext()){
            int v1=edges[i].vid[0]=ptrE->getStartPt();
            int v2=edges[i].vid[1]=ptrE->getEndPt();

            const vector<int>&tmp_fid=ptrE->getFacets();
            edges[i].fid.insert(edges[i].fid.end(),tmp_fid.begin(),tmp_fid.end());

            if( tmp_fid.size()<2 ){ //check if boundary edge
                edges[i].fid.push_back(tmp_fid.back());
                edges[i].type='b'; //bd
            }

            //compute parallel vector
            edges[i].v=edges[i].bk_v=ptrE->getV();
            
            //compute edge normals
            edges[i].in_n[0]=ptrE->getInNormal(0); 

            if (edges[i].type == 'b')
                edges[i].in_n[1] = edges[i].in_n[0];
            else
                edges[i].in_n[1] = ptrE->getInNormal(1); 
            
            //backup edge normals
            edges[i].bk_in_n[0] = edges[i].in_n[0];
            edges[i].bk_in_n[1] = edges[i].in_n[1];
            
            vertices[v1].m_e.push_back(i);
            vertices[v2].m_e.push_back(i);
        }//end i
        
        //facets
        vector<CModelFacet>& facets=G.getFacets();
        for(uint i=0;i<t_size;i++)
        {
            tris[i].n=tris[i].bk_n=facets[i].n;
            for(int j=0;j<3;j++){
                tris[i].e[j]=facets[i].m_Edge[j]->getID();
            }//end j
        }//end i

        //edge type
        ptrE=G.getEdges();
        for(uint i=0;i<e_size;i++,ptrE=ptrE->getNext())
        {
            if(edges[i].type=='b') continue; //already know
            Vector3d& n1=tris[edges[i].fid[0]].n;
            Vector3d& n2=tris[edges[i].fid[1]].n;
            //
            double d=n1*n2;
            if(fabs(1-d)<SMALLNUMBER) 
                edges[i].type='p'; //plane
            else{
                Vector3d vec=(n1%n2).normalize();
                if(vec*edges[i].v>0) edges[i].type='c'; //convex
                else edges[i].type='r'; //reflex
            }
        }

        //vertex type
        typedef list<uint>::iterator IT;
        for(uint i=0;i<v_size;i++)
        {
            vertex& v=vertices[i];

            Vector3d evec;
            for(IT ie=v.m_e.begin();ie!=v.m_e.end();ie++){
                edge& e=edges[*ie];
                Vector3d vec=e.v;
                if(e.vid[1]==i) vec=-vec;
                evec=evec+vec;
            }//end ir

            Vector3d fvec;
            for(IT f=v.m_f.begin();f!=v.m_f.end();f++){
                triangle& t=tris[*f];
                fvec=fvec+t.n;
            }//end ir

            if(evec*fvec>0) v.concave=true;
        }//end i
    }

    return true;
}

//
//
//
// Transformation operations
// 
// Rotation
// Negation
// Reverse (turn inside out)
//

void model::transform(const Vector3d& T, const Matrix3x3& M, double S)
{
    Vector3d tmp;

    //move vertices
    for (uint i = 0; i<v_size; i++)
    {
        tmp.set(vertices[i].bk_p.get());
        vertices[i].p = M*(tmp*S)+T;
        vertices[i].n = (M*vertices[i].n).normalize(); //we should have a back up value for this.
    }

    //move edges
    for (uint i = 0; i<e_size; i++){
        edges[i].in_n[0] = (M*edges[i].bk_in_n[0]).normalize();
        edges[i].in_n[1] = (M*edges[i].bk_in_n[1]).normalize();
        edges[i].v = (M*edges[i].bk_v).normalize();
    }

    //move facets
    for (uint i = 0; i<t_size; i++)
        tris[i].n = (M*tris[i].bk_n).normalize();
}


void model::rotate(const Matrix2x2& M)
{
    Vector2d tmp;

    //rotate vertices
    for(uint i=0;i<v_size;i++){
        tmp.set(vertices[i].bk_p[0],vertices[i].bk_p[1]);
        tmp=M*tmp;
        vertices[i].p.set(tmp[0],tmp[1]);
    }
    
    //rotate edges
    for(uint i=0;i<e_size;i++){
        for(int j=0;j<2;j++){
            tmp.set(edges[i].bk_in_n[j][0],edges[i].bk_in_n[j][1]);
            tmp=M*tmp;
            edges[i].in_n[j].set(tmp[0],tmp[1]);
        }
        
        tmp.set(edges[i].v[0],edges[i].v[1]);
        tmp=M*tmp;
        edges[i].v.set(tmp[0],tmp[1]);
    }

    //rotate facets
    for(uint i=0;i<t_size;i++){
        tmp.set(tris[i].n[0],tris[i].n[1]);
        tmp=M*tmp;
        tris[i].n.set(tmp[0],tmp[1]);
    }
}




void model::rotate(const Matrix3x3& M)
{
    Vector3d tmp;

    //rotate vertices
    for(uint i=0;i<v_size;i++){
        tmp.set(vertices[i].bk_p.get());
        vertices[i].p=M*tmp;
    }
    
    //rotate edges
    for(uint i=0;i<e_size;i++){
        edges[i].in_n[0]=(M*edges[i].bk_in_n[0]).normalize();
        edges[i].in_n[1]=(M*edges[i].bk_in_n[1]).normalize();
        edges[i].v=(M*edges[i].bk_v).normalize();
    }
    //rotate facets
    for(uint i=0;i<t_size;i++)
        tris[i].n=(M*tris[i].bk_n).normalize();
}


void model::scale(double s)
{
    //rotate vertices
    Point3d tmp;
    for(uint i=0;i<v_size;i++){
        tmp.set(vertices[i].bk_p.get());
        vertices[i].p.set(tmp[0]*s,tmp[1]*s,tmp[2]*s);
        vertices[i].bk_p.set(tmp[0]*s,tmp[1]*s,tmp[2]*s);
    }
}

void model::negate()
{

    for(uint i=0;i<v_size;i++){
        Point3d& pt=vertices[i].p;
        pt.set(-pt[0],-pt[1],-pt[2]);

        Point3d& pt2=vertices[i].bk_p;
        pt2.set(-pt2[0],-pt2[1],-pt2[2]);
    }

    for(uint i=0;i<t_size;i++){
        tris[i].n=-tris[i].n;
        tris[i].bk_n=-tris[i].bk_n;
        swap(tris[i].v[1],tris[i].v[2]);
        //swap(tris[i].e[1],tris[i].e[2]);
    }

    for(uint i=0;i<e_size;i++){
        edge& e=edges[i];
        e.v=-e.v;
        e.in_n[0]=-e.in_n[0];
        e.in_n[1]=-e.in_n[1];
        e.bk_v=-e.bk_v;
        e.bk_in_n[0]=-e.bk_in_n[0];
        e.bk_in_n[1]=-e.bk_in_n[1];
    }
}


void model::reverse()
{
    for(uint i=0;i<t_size;i++){
        tris[i].n=-tris[i].n;
        tris[i].bk_n=-tris[i].bk_n;
        swap(tris[i].v[1],tris[i].v[2]);
    }

    for(uint i=0;i<e_size;i++){
        edge& e=edges[i];
        if(e.type=='r') e.type='c';
        else if(e.type=='c') e.type='r';
    }
}

