/*
 *  testTriLevelInterx.cpp
 *  TriInterx
 *
 *  Created by Chema on 8/9/10.
 *  Copyright 2010 __MyCompanyName__. All rights reserved.
 *
 */

#include <sys/time.h>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>

#include "GLInclude.h"
#include "vec.h"
#include "matrix.h"
#include "basicas.h"
#include "font.h"
#include "draw.h"
#include "resourcemap.h"
#include "icosahedron.h"
#include "bunny.h"
#include "bunnyh.h"
//#include "bunnysh.h"
#include "triangle.h"
#include "sweepLine.h"
#include "sweepTris.h"
#include "testVSweep.h"



using namespace std;
using namespace Draw;

struct UEdge { // unique edges (indices)
    UInt2 i_;
    int setpos_;

    UEdge (unsigned a, unsigned b)
    : i_(a<b?a:b,a<b?b:a)
    , setpos_(-1)
    {;}
    bool operator<(const UEdge& ref) const {
        return lessLexicoOrder(i_,ref.i_);
    }
};

typedef std::set<UEdge> UEdges;

struct Object {
    DReal3 p_;
    unsigned vxbase_;
    unsigned fcbase_;
    unsigned egbase_;

    std::vector<DReal3> vx_;
    std::vector<UInt3> fc_;
    UEdges ueg_;

    std::vector<DReal3> normals_;
    std::vector<Color3> colors_;

    Object()
        :p_(DReal3(0.))
        ,vxbase_(0)
        ,fcbase_(0)
        {;}
};


namespace VSweep {

    std::vector<string> console;

    int width,height;
    Font font;

    std::vector<DReal3> tvx;
    std::vector<DReal3> tnormals;
    std::vector<Color3> tcolors;

    std::vector<Edge> edges;
    std::vector<Triangle> tris;
    std::vector<Segment> segs;
    std::vector<Object> objs;


    ST sweeptris;
    int debugSteps=0;

    int rotateX=0;
    int rotateY=0;
    dreal rotModelX = 0;
    dreal level = 0.1;
    dreal offY= 0;
    dreal scale=0.5;
    bool multiple = true;
    Int2 deltaMousePos;
    Int2 oldMousePos;
    DReal3 modelpos(0.5,0.5,0.5);
    DReal3 lightpos(0.7,0.8,10.0);
    bool showTris=false;

    struct timeval start, end, idleold, idlenew;
    float lapse;
    float average0,average1,average2;

    void initModel(Object& obj, float data[][3], unsigned indices[][3], unsigned vsize, unsigned fsize,
                   const Color3& color,real scale) {

        obj.vx_.reserve(vsize);
        for (unsigned i=0;i<vsize;i++) {
            obj.vx_.push_back(scale*DReal3(data[i][0],data[i][1],data[i][2]));
        }
        obj.vx_.reserve(fsize);
        for (unsigned i=0;i<fsize;++i) {
            obj.fc_.push_back(UInt3(indices[i][0],indices[i][1],indices[i][2]));
        }
        obj.normals_.reserve(fsize);
        obj.colors_.reserve(fsize);
        for (unsigned i = 0; i < fsize; i++) {
            unsigned ia=indices[i][0];
            unsigned ib=indices[i][1];
            unsigned ic=indices[i][2];

            DReal3 a = obj.vx_[ia];
            DReal3 b = obj.vx_[ib];
            DReal3 c = obj.vx_[ic];
            DReal3 normal = normalize( crossProd( b-a, c-a) );

            obj.normals_.push_back(normal);
            obj.colors_.push_back(color);
        }
        for (unsigned i = 0; i < fsize; i++) {
            unsigned ia=indices[i][0];
            unsigned ib=indices[i][1];
            unsigned ic=indices[i][2];

            obj.ueg_.insert(UEdge(ia,ib));
            obj.ueg_.insert(UEdge(ib,ic));
            obj.ueg_.insert(UEdge(ic,ia));
        }
        UEdges::iterator itr = obj.ueg_.begin();
        unsigned cc=0;
        while(itr!=obj.ueg_.end()) {
            (*const_cast<UEdge*>(&*itr)).setpos_=cc++;
            ++itr;
        }
        itr = obj.ueg_.begin();
        while(itr!=obj.ueg_.end()) {
            cout<<(*itr).i_<<(*itr).setpos_<<endl;
            ++itr;
        }
    }

    void bindModel (Object& obj, std::vector<Edge>& edges, std::vector<Triangle>& tris, std::vector<Segment>& segs) {
        UEdges::iterator itr = obj.ueg_.begin();
        while(itr!=obj.ueg_.end()) {
            unsigned index = obj.egbase_+(*itr).setpos_;
            unsigned v0 = obj.vxbase_+(*itr).i_[0];
            unsigned v1 = obj.vxbase_+(*itr).i_[1];
            edges[index].init(&tvx[v0],&tvx[v1]);
            ++itr;
        }

        for (unsigned i=0;i<obj.fc_.size();i++) {
            unsigned ia=obj.fc_[i][0];
            unsigned ib=obj.fc_[i][1];
            unsigned ic=obj.fc_[i][2];

            UEdges::iterator abitr = obj.ueg_.find(UEdge(ia,ib));
            UEdges::iterator bcitr = obj.ueg_.find(UEdge(ib,ic));
            UEdges::iterator caitr = obj.ueg_.find(UEdge(ic,ia));

            unsigned eab= obj.egbase_+(*abitr).setpos_;
            unsigned ebc= obj.egbase_+(*bcitr).setpos_;
            unsigned eca= obj.egbase_+(*caitr).setpos_;

            tris[obj.fcbase_+i].init(
                    &segs[obj.fcbase_+i],
                    &tvx[obj.vxbase_+ia],&tvx[obj.vxbase_+ib],&tvx[obj.vxbase_+ic],
                    &edges[eab], &edges[ebc], &edges[eca],
                    &tnormals[obj.fcbase_+i], &tcolors[obj.fcbase_+i]);
            segs[obj.fcbase_+i].tri_ = &tris[obj.fcbase_+i];
            segs[obj.fcbase_+i].index_ = obj.fcbase_+i;
        }
    }

    void transformCoords() {
        rotModelX += lapse*0.001;
        DMatrix4 rmx = Rotation(rotModelX, DReal3(0,1,0));
//        DMatrix4 rmx = Matrix4Identity<dreal>();

        for (unsigned i=0;i<objs.size();++i) {
            SetTranslation(rmx,objs[i].p_+DReal3(0,offY,0));
            for (unsigned j=0;j<objs[i].vx_.size();++j) {
                DReal3 tx = Matrix4Affine (rmx, objs[i].vx_[j]);
                tvx[objs[i].vxbase_+j] = tx;
            }
            for (unsigned j=0;j<objs[i].fc_.size();++j) {
                const DReal3& normal = tnormals[objs[i].fcbase_+j] = Matrix4Rotate(rmx,objs[i].normals_[j]);
                const Color3& color = objs[i].colors_[j];
                dreal d = -dotProd(normalize(objs[i].p_-lightpos),normal);
                d = max(0.3, d>0?d:-d-0.5) ;
                tcolors[objs[i].fcbase_+j] = Color3(color[0]*d,color[1]*d,color[2]*d);

            }
        }
        for (unsigned i=0;i<tris.size();++i) {
            tris[i].reset(sweeptris.at_);
        }
        for (unsigned i=0;i<edges.size();++i) {
            edges[i].reset(sweeptris.aet_);
        }
        for (unsigned i=0;i<segs.size();++i) {
            segs[i].reset(sweeptris.sweepline_.events_);
        }
    }

    void draw(void)
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        setOrthographicCubeProjection(1.f);

        glPushMatrix();
        glLoadIdentity();

        glRotatef(rotateX,1,0,0);
        glRotatef(-rotateY,0,1,0);

        glScalef(scale,scale,scale);
        glTranslatef(-0.5,-0.5,-0.5);

        drawCube(1.);
        drawAxis(0.01);

        glColor3ub(0,0,255);


        int lines = height/4;
        char txt[128];
        sprintf(txt,"%dx%d tris %d", width, height, static_cast<int>(tris.size()));
        console.push_back(txt);
        console.push_back("");

        float total = 0;

        if (showTris) {
            //const size_t size = tris.size();
            glBegin(GL_LINES);
            for (unsigned i=0;i<tris.size();i++) {
                const Color3& rgb =*tris[i].rgb_;
                const DReal3& t = *(tris[i].vx_[2]);
                const DReal3& m = *(tris[i].vx_[1]);
                const DReal3& b = *(tris[i].vx_[0]);
                const DReal3& n = *(tris[i].normal_);
                glColor3ubv(&rgb[0]);
                drawTri(t,m,b);
//                glColor3ub(0,255,0);
//                glVertex3dv(&t[0]);
//                glVertex3dv(&(t+0.01*n)[0]);
            }
            glEnd();
        }

//        sprintf(txt,"ST:");
//        console.push_back(txt);
        gettimeofday(&start, 0);

        sweeptris.init(tris, console);

        gettimeofday(&end, 0);
        lapse = (end.tv_sec - start.tv_sec)* (1e03f) + (end.tv_usec - start.tv_usec)*(1e-03f);
        total+=lapse;
        sprintf(txt,"init: %3.3f",lapse);
        console.push_back(txt);

        gettimeofday(&start, 0);

        sweeptris.run(lines,debugSteps,font,console);

        gettimeofday(&end, 0);
        lapse = (end.tv_sec - start.tv_sec)* (1e03f) + (end.tv_usec - start.tv_usec)*(1e-03f);
        total+=lapse;
        sprintf(txt,"run: %3.3f",lapse);
        console.push_back(txt);

        sprintf(txt,"total: %3.3f",lapse);
        console.push_back(txt);
        console.push_back("");
        sprintf(txt,"fps:%3.3f", 1000./total);
        console.push_back(txt);

        glPopMatrix();

        glColor3ub(220,220,220);

        // text overlay
        setOrthographicProjection(width,height);

        glPushMatrix();
        glLoadIdentity();

        glScalef(1,-1,1);

        //font.preDrawString();
        real lineFeed = 0;
        for (unsigned i=0;i<console.size();i++) {
        //    font.drawString(console[i],0,lineFeed-=font.lineHeight());
        }
        //font.posDrawString();

        glPopMatrix();

        resetPerspectiveProjection();

        glutSwapBuffers();
    }


    void idle(void) {
        console.clear();

        gettimeofday(&idlenew, 0);
        lapse = (idlenew.tv_sec - idleold.tv_sec)* (1e03f) + (idlenew.tv_usec - idleold.tv_usec)*(1e-03f);
        idleold = idlenew;

        transformCoords();

        glutPostRedisplay();
    }


    void keyboard(unsigned char key, int x, int y) {
        switch (key) {
            case 27:  /*  Escape Key  */
                exit(0);
                break;
            case 'x':
                rotateX++;
                break;
            case 'X':
                rotateX--;
                break;
            case 'y':
                rotateY++;
                break;
            case 'Y':
                rotateY--;
                break;
            case '+':
                for(unsigned i=0;i<objs.size();i++) {
                    objs[i].p_[1]+=0.01;
                }

//                offY+=0.01;
                break;
            case '-':
                for(unsigned i=0;i<objs.size();i++) {
                    objs[i].p_[1]-=0.01;
                }
//                offY-=0.01;
                break;
            case 'm':
                multiple =!multiple;
                break;
            case 'c':
                init(width,height);
                break;
            case 's':
                showTris=!showTris;
                break;
            case 'a':
                scale+=0.1;
                break;
            case 'A':
                scale-=0.1;
                break;
            case 'r':
                rotModelX+=0.1;
                break;
            default:
                break;
        }
    }

    void processMouseActiveMotion(int x, int y) {
        Int2 p(x,y);
        deltaMousePos = p - oldMousePos;
        rotateX += deltaMousePos[1];
        rotateY -= deltaMousePos[0];
        oldMousePos = p;
    }

    void processMousePassiveMotion(int x, int y) {
        Int2 p(x,y);
        deltaMousePos = p - oldMousePos;
        oldMousePos = p;
    }

    void init(unsigned w, unsigned h) {
        ResourceMap resource;
        resource.addPath("Fonts","");
        std::string fontPath = resource.getPath("Fonts")+"monaco11";
        //font.load(fontPath.c_str());

        //console.reserve(height/font.lineHeight());

        width=w;height=h;

        sweeptris.clear();
        tris.clear();
        tvx.clear();
        tnormals.clear();
        tcolors.clear();
        objs.clear();

        glEnable(GL_DEPTH_TEST);
        glutIdleFunc (idle);
        glutDisplayFunc (draw);
        glutKeyboardFunc (keyboard);

        //adding here the mouse processing callbacks
        glutMotionFunc(processMouseActiveMotion);
        glutPassiveMotionFunc(processMousePassiveMotion);


        Color3 colors [] = {
            Color3(255,0,0),
            Color3(0,255,0),
            Color3(0,0,255),
            Color3(255,255,0),
            Color3(0,255,255),
            Color3(255,0,255),
            Color3(255,255,255),
        };

        int n = 2;

//        for (unsigned i=0;i<n;i++) {
//            addModel(bunny_vertex, bunny_indices,bunny_vertex_size,bunny_indices_size, /*DReal3(0,-.5,0)*/0.5*DReal3(-0.5+randnorm<dreal>(),-0.5+randnorm<dreal>(),-0.5+randnorm<dreal>()),colors[(i)%7],(7.5/n));
//        }

        for (unsigned i=0;i<n;i++) {
             size_t size;
//            size = objs.size();
//            objs.push_back(Object());
//            objs[size].p_ = DReal3(randnorm<dreal>(),randnorm<dreal>(),randnorm<dreal>());
//            initModel(objs[objs.size()-1], icosa_vertex, icosa_indices,icosa_vertex_size,icosa_indices_size, colors[(i+5)%7],(7./n));
            size = objs.size();
            objs.push_back(Object());
//            objs[size].p_ =0.8*DReal3(0.1+randnorm<dreal>(),0.1+randnorm<dreal>(),randnorm<dreal>());
            //objs[size].p_ =0.5*DReal3(randnorm<dreal>(),randnorm<dreal>(),randnorm<dreal>());
            objs[size].p_=DReal3(0.5,0.,0.5)+0.1*DReal3(randnorm<dreal>(),randnorm<dreal>(),randnorm<dreal>());
//            initModel(objs[objs.size()-1], bunny_vertex, bunny_indices,bunny_vertex_size,bunny_indices_size, colors[(i)%7],5/*(1./n)*/);
            initModel(objs[objs.size()-1], bunnyh_vertex, bunnyh_indices,bunnyh_vertex_size,bunnyh_indices_size, colors[(i)%7],5/*(1./n)*/);
        }

        unsigned fbase = 0;
        unsigned vbase = 0;
        unsigned ebase = 0;
        for (unsigned i=0;i<objs.size();i++) {
            objs[i].vxbase_ = vbase;
            objs[i].fcbase_ = fbase;
            objs[i].egbase_ = ebase;
            vbase+=objs[i].vx_.size();
            fbase+=objs[i].normals_.size();
            ebase+=objs[i].ueg_.size();
        }
        tvx.resize(vbase);
        tnormals.resize(fbase);
        tcolors.resize(fbase);
        edges.resize(ebase);
        tris.resize(fbase);
        segs.resize(fbase);

        for (unsigned i=0;i<objs.size();i++) {
            bindModel(objs[i],edges,tris,segs);
        }
//
//        tris.push_back( Triangle(DReal3(-0.2,-0.2+offY,0.0), DReal3(0.2,0.0+offY,-0.1),DReal3(0.15,0.1+offY,0.0), DReal3(0,0,1), Color3(255,0,0) ) );
//        tris.push_back( Triangle(DReal3(-0.15,-0.2+offY,0.2), DReal3(0.0,-0.15+offY,0.0),DReal3(-0.1,0.1+offY,-0.1),DReal3(0,0,1), Color3(0,255,0) ) );
//        tris.push_back( Triangle(DReal3(-0.2,0.1+offY,0.2), DReal3(0.2,-0.1+offY,-0.2), DReal3(-0.1,0.2+offY,0.1), DReal3(0,0,1),Color3(0,0,255) ) );

        gettimeofday(&idleold, 0);
        transformCoords();
    }

} // namespace

