#include "BasicGlCamera.h"
#include <math.h>
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include "src/tools.h"

//Coordinates of an hexagon of radius 1 and center 0
GLfloat hexcoords[6][2] = {{-1./sqrt(3.),  1},
                           { 1./sqrt(3.),  1},
                           { 2./sqrt(3.),  0},
                           { 1./sqrt(3.), -1},
                           {-1./sqrt(3.), -1},
                           {-2./sqrt(3.),  0}};



    BasicGlCamera::BasicGlCamera(QWidget* cParent) : QGLWidget(cParent)
    {
        setFormat(QGLFormat(QGL::DoubleBuffer));// | QGL::DepthBuffer));
        hexRadius = 0.015f;
        hexTolerance = hexRadius/100.0f;
        viewSize = 1.0f;
        calculatePixelsCoords();

        ifstream fin2("MasterList-v3.txt");
        if (!fin2.is_open())
        {
            cout << "Error: file \"MasterList-v3\" missing. aborting." << endl;
            exit(-1);
        }
        int l = 0;
        string buf;
        while (getline(fin2, buf, '\n'))
        {
            buf = Tools::Trim(buf);
            if (buf[0]=='#')
                continue;

            unsigned int softid, hardid, dummy;

            stringstream str(buf);

            str >> softid;
            str >> dummy;
            str >> hardid;

            if (softid>=1440)
                continue;

            hardwareMapping[softid] = hardid;
            softwareMapping[hardid] = softid;

            l++;
        }
//        GLfloat tempPixelsCoords[MAX_NUM_PIXELS][3];
//        for (int i=0;i<1440;i++)
//            for (int j=0;j<3;j++)
//                tempPixelsCoords[hardwareMapping[i]][j] = pixelsCoords[i][j];
//        for (int i=0;i<1440;i++)
//            for (int j=0;j<3;j++)
//                pixelsCoords[i][j] = tempPixelsCoords[i][j];
        buildVerticesList();
       ifstream fin1("Trigger-Patches.txt");
       if (!fin1.is_open())
       {
           cout << "Error: file \"Trigger-Patches.txt\" missing. Aborting." << endl;
           exit(-1);
       }
       l=0;
        while (getline(fin1, buf, '\n'))
        {
            buf = Tools::Trim(buf);
            if (buf[0]=='#')
                continue;

            stringstream str(buf);
            for (int i=0; i<9; i++)
            {
                unsigned int n;
                str >> n;

                if (n>=1440)
                    continue;

                patches[l][i] = hardwareMapping[n];
            }
            l++;
        }

        for (int i=0;i<1440;i++)
            updateNeighbors(i);

        buildPatchesIndices();

        ss[0] = 0;    ss[1] = 0.25f; ss[2] = 0.5f; ss[3] = 0.75f; ss[4] = 1.0f;
        rr[0] = 0.15; rr[1] = 0;     rr[2] = 0;    rr[3] = 1.0f;  rr[4] = 0.85f;
        gg[0] = 0.15; gg[1] = 0;     gg[2] = 1;    gg[3] = 0;     gg[4] = 0.85f;
        bb[0] = 0.15; bb[1] = 1;     bb[2] = 0;    bb[3] = 0;     bb[4] = 0.85f;

        fPixelStride = 1;
        fcSlice = 0;
        fData.resize(1440);
        for (int i=0;i<ACTUAL_NUM_PIXELS;i++)
            fData[i] = (double)i;///1.44;//(double)(i)/(double)(ACTUAL_NUM_PIXELS);
    }
    BasicGlCamera::~BasicGlCamera()
    {
    }

    void BasicGlCamera::initializeGL()
    {
        qglClearColor(QColor(25,25,38));
        glShadeModel(GL_FLAT);
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_CULL_FACE);
    }
    void BasicGlCamera::resizeGL(int cWidth, int cHeight)
    {
        glViewport(0, 0, cWidth, cHeight);
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity();
         GLfloat windowRatio = (float)cWidth/(float)cHeight;
         if (windowRatio < 1)
         {
             windowRatio = 1.0f/windowRatio;
             gluOrtho2D(-viewSize, viewSize, -viewSize*windowRatio, viewSize*windowRatio);
             pixelSize = 2*viewSize/(float)cWidth;
             shownSizex = 2*viewSize;
             shownSizey = 2*viewSize*windowRatio;
         }
         else
         {
             gluOrtho2D(-viewSize*windowRatio, viewSize*windowRatio, -viewSize, viewSize);
             pixelSize = 2*viewSize/(float)cHeight;
             shownSizex = 2*viewSize*windowRatio;
             shownSizey = 2*viewSize;
         }
         glMatrixMode(GL_MODELVIEW);
    }
    void BasicGlCamera::paintGL()
    {
         glClear(GL_COLOR_BUFFER_BIT);
         glLoadIdentity();

         glTranslatef(0,-0.44,0);
         glScalef(1.5, 1.5, 1.5);

         drawCamera(true);

         drawPatches();
    }
    void BasicGlCamera::mousePressEvent(QMouseEvent *cEvent)
    {

    }
    void BasicGlCamera::mouseMoveEvent(QMouseEvent *cEvent)
    {

    }
    void BasicGlCamera::mouseDoubleClickEvent(QMouseEvent *cEvent)
    {

    }
    void BasicGlCamera::drawCamera(bool alsoWire)
    {
        cout << "Super PaintGL" << endl;
        glColor3f(0.5,0.5,0.5);
        glLineWidth(1.0);
        float color;

        for (int i=0;i<ACTUAL_NUM_PIXELS;i++)
        {
            color = float(fData[i*fPixelStride+fcSlice]);// + ]eventData[nRoi*i + whichSlice]+(VALUES_SPAN/2))/(float)(VALUES_SPAN-1);
            int index = 0;
            while (ss[index] < color)
                index++;
            index--;
            float weight0 = (color-ss[index]) / (ss[index+1]-ss[index]);
            float weight1 = 1.0f-weight0;
            pixelsColor[i][0] = weight1*rr[index] + weight0*rr[index+1];
            pixelsColor[i][1] = weight1*gg[index] + weight0*gg[index+1];
            pixelsColor[i][2] = weight1*bb[index] + weight0*bb[index+1];
        }

        for (int i=0;i<ACTUAL_NUM_PIXELS;i++)
        {
 //           if (i == 690 ||
 //               i == 70)
 //               continue;
            glColor3fv(pixelsColor[i]);
            glLoadName(i);

        drawHexagon(i,true);

        }
        if (!alsoWire)
            return;
        glColor3f(0.0f,0.0f,0.0f);
        for (int i=0;i<ACTUAL_NUM_PIXELS;i++)
        {
//            if (i == 690 ||
//                i == 70)
//                continue;
            drawHexagon(i, false);
        }
    }
    void BasicGlCamera::drawPatches()
    {
//        cout << "*************************DRAWING PATCHES**************************" << endl;
//        for (int i=0;i<NTMARK;i++)
//        {
//            cout << ".....................patch " << i << endl;
//            for (unsigned int j=0;patchesIndices[i].size();j++)
//            {
//                cout << patchesIndices[i][j].first << " and " << patchesIndices[i][j].second << endl;
//            }
//        }
        glLineWidth(2.0f);
        float backupRadius = hexRadius;
        hexRadius *= 0.95;
        glColor3f(0.5f, 0.5f, 0.3f);
        glBegin(GL_LINES);
        for (int i=0;i<NTMARK;i++)
        {
            for (unsigned int j=0;j<patchesIndices[i].size();j++)
            {
                glVertex2fv(verticesList[patchesIndices[i][j].first]);
                glVertex2fv(verticesList[patchesIndices[i][j].second]);
            }
        }
        glEnd();
        hexRadius = backupRadius;
    }
    int BasicGlCamera::PixelAtPosition(const QPoint &cPos)
    {
        const int MaxSize = 512;
        GLuint buffer[MaxSize];
        GLint viewport[4];

        makeCurrent();

        glGetIntegerv(GL_VIEWPORT, viewport);
        glSelectBuffer(MaxSize, buffer);
        glRenderMode(GL_SELECT);

        glInitNames();
        glPushName(0);

        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity();
        GLfloat windowRatio = GLfloat(width()) / GLfloat(height());
        gluPickMatrix(GLdouble(cPos.x()), GLdouble(viewport[3] - cPos.y()),
                1.0, 1.0, viewport);

        if (windowRatio < 1)
         {
             windowRatio = 1.0f/windowRatio;
             gluOrtho2D(-viewSize, viewSize, -viewSize*windowRatio, viewSize*windowRatio);
         }
         else
         {
             gluOrtho2D(-viewSize*windowRatio, viewSize*windowRatio, -viewSize, viewSize);
         }

        glMatrixMode(GL_MODELVIEW);
        drawCamera(false);
        glMatrixMode(GL_PROJECTION);
        glPopMatrix();

        //for some reason that I cannot understand, the push/pop matrix doesn't do the trick here... bizarre
        //ok, so re-do the resizeGL thing.
        resizeGL(width(), height());

        if (!glRenderMode(GL_RENDER))
            return -1;

        return buffer[3];
    }
    void BasicGlCamera::drawHexagon(int index, bool solid)
    {
        if (solid)
            glBegin(GL_POLYGON);
        else
            glBegin(GL_LINE_LOOP);

        glVertex2fv(verticesList[verticesIndices[index][0]]);
        glVertex2fv(verticesList[verticesIndices[index][1]]);
        glVertex2fv(verticesList[verticesIndices[index][2]]);
        glVertex2fv(verticesList[verticesIndices[index][3]]);
        glVertex2fv(verticesList[verticesIndices[index][4]]);
        glVertex2fv(verticesList[verticesIndices[index][5]]);
        if (solid)
            glVertex2fv(verticesList[verticesIndices[index][0]]);

        glEnd();
    }

    void BasicGlCamera::updateNeighbors(int currentPixel)
    {
        float squaredDistance = 0;
        for (int i=0;i<currentPixel;i++)
        {
            squaredDistance = (pixelsCoords[i][0] - pixelsCoords[currentPixel][0])*
                              (pixelsCoords[i][0] - pixelsCoords[currentPixel][0]) +
                              (pixelsCoords[i][1] - pixelsCoords[currentPixel][1])*
                              (pixelsCoords[i][1] - pixelsCoords[currentPixel][1]);
            if (squaredDistance < 4*hexRadius*hexRadius*(1.0f+hexTolerance))//neighbor !
            {//ok, but which one ?
                if (fabs(pixelsCoords[i][0] - pixelsCoords[currentPixel][0]) < hexTolerance &&
                    pixelsCoords[i][1] < pixelsCoords[currentPixel][1]){//top
                    neighbors[i][0] = currentPixel;
                    neighbors[currentPixel][3] = i;
                    continue;}
                if (fabs(pixelsCoords[i][0] - pixelsCoords[currentPixel][0]) < hexTolerance &&
                    pixelsCoords[i][1] > pixelsCoords[currentPixel][1]){//bottom
                    neighbors[i][3] = currentPixel;
                    neighbors[currentPixel][0] = i;
                    continue;}
                if (pixelsCoords[i][0] > pixelsCoords[currentPixel][0] &&
                    pixelsCoords[i][1] > pixelsCoords[currentPixel][1]){//top right
                    neighbors[i][4] = currentPixel;
                    neighbors[currentPixel][1] = i;
                    continue;}
                if (pixelsCoords[i][0] > pixelsCoords[currentPixel][0] &&
                    pixelsCoords[i][1] < pixelsCoords[currentPixel][1]){//bottom right
                    neighbors[i][5] = currentPixel;
                    neighbors[currentPixel][2] = i;
                    continue;}
                if (pixelsCoords[i][0] < pixelsCoords[currentPixel][0] &&
                    pixelsCoords[i][1] > pixelsCoords[currentPixel][1]){//top left
                    neighbors[i][2] = currentPixel;
                    neighbors[currentPixel][5] = i;
                    continue;}
                if (pixelsCoords[i][0] < pixelsCoords[currentPixel][0] &&
                    pixelsCoords[i][1] < pixelsCoords[currentPixel][1]){//bottom left
                    neighbors[i][1] = currentPixel;
                    neighbors[currentPixel][4] = i;
                    continue;}
            }
        }
    }
    void BasicGlCamera::skipPixels(int start, int howMany)
    {
        for (int i=start;i<MAX_NUM_PIXELS-howMany;i++)
        {
            pixelsCoords[i][0] = pixelsCoords[i+howMany][0];
            pixelsCoords[i][1] = pixelsCoords[i+howMany][1];
        }
    }
    void BasicGlCamera::calculatePixelsCoords()
    {
        pixelsCoords[0][0] = 0;
        pixelsCoords[0][1] = 0.3;
        pixelsCoords[0][2] = 0;
        pixelsCoords[1][0] = 0;
        pixelsCoords[1][1] = 0.3+2*hexRadius;
        pixelsCoords[1][2] = 0;
        neighbors[0][0] = 1;
        neighbors[1][3] = 0;
        //from which side of the previous hexagon are we coming from ?
        int fromSide = 3;
        //to which side are we heading to ?
        int toSide = 0;
        for (int i=2;i<MAX_NUM_PIXELS;i++)
        {
            toSide = fromSide-1;
            if (toSide < 0)
                toSide =5;
            while (neighbors[i-1][toSide] >= 0)
            {
                toSide--;
                if (toSide < 0)
                    toSide = 5;
            }
            fromSide = toSide + 3;
            if (fromSide > 5)
                fromSide -= 6;
            //ok. now we now in which direction we're heading
            pixelsCoords[i][0] = pixelsCoords[i-1][0];
            pixelsCoords[i][1] = pixelsCoords[i-1][1];
            pixelsCoords[i][2] = pixelsCoords[i-1][2];
            switch (toSide)
            {
            case 0:
                pixelsCoords[i][1] += 2*hexRadius;
            break;
            case 1:
                pixelsCoords[i][0] += (2*hexRadius)*sin(M_PI/3.0);
                pixelsCoords[i][1] += (2*hexRadius)*cos(M_PI/3.0);
            break;
            case 2:
                pixelsCoords[i][0] += (2*hexRadius)*sin(M_PI/3.0);
                pixelsCoords[i][1] -= (2*hexRadius)*cos(M_PI/3.0);
            break;
            case 3:
                pixelsCoords[i][1] -= 2*hexRadius;
            break;
            case 4:
                pixelsCoords[i][0] -= (2*hexRadius)*sin(M_PI/3.0);
                pixelsCoords[i][1] -= (2*hexRadius)*cos(M_PI/3.0);
            break;
            case 5:
                pixelsCoords[i][0] -= (2*hexRadius)*sin(M_PI/3.0);
                pixelsCoords[i][1] += (2*hexRadius)*cos(M_PI/3.0);
            break;
            };

            updateNeighbors(i);
        }
        //Ok. So now we've circled around all the way to MAX_NUM_PIXELS
        //do the required shifts so that it matches the fact camera up to ACTUAL_NUM_PIXELS pixels
        skipPixels(1200, 1);
        skipPixels(1218, 3);
        skipPixels(1236, 1);
        skipPixels(1256, 1);
        skipPixels(1274, 3);
        skipPixels(1292, 3);
        skipPixels(1309, 6);
        skipPixels(1323, 7);
        skipPixels(1337, 6);
        skipPixels(1354, 6);
        skipPixels(1368, 7);
        skipPixels(1382, 9);
        skipPixels(1394, 12);
        skipPixels(1402, 15);
        skipPixels(1410, 12);
        skipPixels(1422, 12);
        skipPixels(1430, 15);
    }
    void BasicGlCamera::buildVerticesList()
    {
        numVertices = 0;
         GLfloat cVertex[2];
         for (int i=0;i<NPIX;i++)
         {
             for (int j=0;j<6;j++)
             {
                 for (int k=0;k<2;k++)
                     cVertex[k] = hexcoords[j][k]*hexRadius + pixelsCoords[i][k];
                 bool found = false;
                 for (int k=0;k<numVertices;k++)
                 {
                     if ((cVertex[0] - verticesList[k][0])*
                         (cVertex[0] - verticesList[k][0]) +
                         (cVertex[1] - verticesList[k][1])*
                         (cVertex[1] - verticesList[k][1]) < hexTolerance*hexTolerance)
                         {
                             found = true;
                             break;
                         }
                 }
                 if (!found)
                 {
                     for (int k=0;k<2;k++)
                         verticesList[numVertices][k] = cVertex[k];
                     numVertices++;
                 }
             }
         }
         for (int i=0;i<NPIX;i++)
         {
             for (int j=0;j<6;j++)
             {
                 for (int k=0;k<2;k++)
                     cVertex[k] = hexcoords[j][k]*hexRadius + pixelsCoords[i][k];
                 for (int k=0;k<numVertices;k++)
                 {
                     if ((cVertex[0] - verticesList[k][0])*
                          (cVertex[0] - verticesList[k][0]) +
                          (cVertex[1] - verticesList[k][1])*
                          (cVertex[1] - verticesList[k][1]) < hexTolerance*hexTolerance)
                          {
                             verticesIndices[i][j] = k;
                             break;
                          }
                 }
             }
         }
    }
    void BasicGlCamera::buildPatchesIndices()
    {
        vector<edge>::iterator it;
        bool erased = false;
//        patchesIndices.resize(NTMARK);
        for (int i=0;i<NTMARK;i++)//for all patches
        {
            patchesIndices[i].clear();
            for (int j=0;j<9;j++)//for all cells of the current patch
            {
                for (int k=0;k<6;k++)//for all sides of the current cell
                {
                    int first = k-1;
                    int second = k;
                    if (first < 0)
                        first = 5;
                    erased = false;
                    for (it=(patchesIndices[i]).begin(); it != (patchesIndices[i]).end(); it++)//check if this side is here already or not
                    {
                        if (((*it).first == verticesIndices[softwareMapping[patches[i][j]]][first] &&
                             (*it).second == verticesIndices[softwareMapping[patches[i][j]]][second]) ||
                            ((*it).first == verticesIndices[softwareMapping[patches[i][j]]][second] &&
                             (*it).second == verticesIndices[softwareMapping[patches[i][j]]][first]))
                        {
                            patchesIndices[i].erase(it);
                            erased = true;
                            break;
                        }
                    }
                    if (!erased)
                    {
                        edge temp;
                        temp.first = verticesIndices[softwareMapping[patches[i][j]]][first];
                        temp.second = verticesIndices[softwareMapping[patches[i][j]]][second];
                        patchesIndices[i].push_back(temp);
                    }
                }
            }
        }
//        for (int i=0;i<NTMARK;i++)
//        {
//            cout << ".....................patch " << i << " size: " << patchesIndices[i].size() << endl;
//            for (unsigned int j=0;j<patchesIndices[i].size();j++)
//            {
//               if (patchesIndices[i][j].first < 0 || patchesIndices[i][j].first > 3013)
//                cout << patchesIndices[i][j].first << " and " << patchesIndices[i][j].second << " and " << j << endl;
//            }
//        }
    }

