/*
 * =====================================================================================
 *
 *       Filename:  viewer.cpp
 *
 *    Description:  View multi-z-buffer views
 *
 *        Version:  1.0
 *        Created:  23/02/2011 08:31:52
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  John Ryland (jryland), jryland@xiaofrog.com
 *        Company:  InvertedLogic
 *
 * =====================================================================================
 */

#include <viewer.h>
#include <QMatrix4x4>


Viewer::Viewer(QWidget *parent) : QWidget(parent)
{
    for (int f = 0; f < 6; f++)
    {
        QImage img(QString("side_%1_.png").arg(f));
        unsigned int *data = (unsigned int *)img.bits();
        for (int i = 0; i < 256*256; i++)
        {
            //if ( data[i] & 0x00ffffff )
            {
                face[f][i] = data[i] & 0xff;
            }
            //else
            {
                //face[f][i] = 0xff;
            }
        }
    }
    angle = 0;

    startTimer(1);
}


Viewer::~Viewer()
{
}


void Viewer::timerEvent(QTimerEvent *event)
{
    repaint();
}


#include <time.h>


float calcFPS()
{
    static unsigned lastTick = clock();
    unsigned currTick = clock();
    unsigned tickDiff=currTick-lastTick;
    lastTick = currTick;
    return (float)(CLOCKS_PER_SEC) / (float)(tickDiff);   // Inverse
}


void Viewer::paintEvent(QPaintEvent *pe)
{
    QPainter p(this);

    angle += 15;

    QImage output( 256, 256, QImage::Format_ARGB32 );
    unsigned int *data = (unsigned int *)output.bits();
/*
    int sideX[6];
    int sideY[6];
    int sideZ[6];
    int sideDX[6];
    int sideDY[6];
    int sideDZ[6];
    int sideIncX[6];
    int sideIncY[6];
    int sideIncZ[6];
    
    sideX[0] = 128;
    sideY[0] = 0;
    sideZ[0] = -128;
    sideDX[0] = -1;
    sideDY[0] = 0;
    sideDZ[0] = 1;
    sideIncX[0] = 1;
    sideIncY[0] = 0;
    sideIncZ[0] = 1;
    // y = y
    
    sideX[1] = 128;
    sideZ[1] = -128;
    sideDX[1] = -1;
    sideDZ[1] = 1;
    sideIncX[1] = 1;
    sideIncZ[1] = 1;
    
    sideX[2] = 128;
    sideZ[2] = -128;
    sideDX[2] = -1;
    sideDZ[2] = 1;
    sideIncX[2] = 1;
    sideIncZ[2] = 1;
    
    sideX[3] = 128;
    sideZ[3] = -128;
    sideDX[3] = -1;
    sideDZ[3] = 1;
    sideIncX[3] = 1;
    sideIncZ[3] = 1;
    
    sideX[4] = 128;
    sideZ[4] = -128;
    sideDX[4] = -1;
    sideDZ[4] = 1;
    sideIncX[4] = 1;
    sideIncZ[4] = 1;
    
    sideX[5] = 128;
    sideZ[5] = -128;
    sideDX[5] = -1;
    sideDZ[5] = 1;
    sideIncX[5] = 1;
    sideIncZ[5] = 1;
*/

    // At the moment this is orthographic, but shouldn't be hard to modify that
    QMatrix4x4 invViewSpace;
    invViewSpace.translate( 128, 128, 128 );
    invViewSpace.rotate( angle, 0, 1, 0 );
    invViewSpace.rotate( angle + 30, 1, 0, 0 );
    invViewSpace.rotate( angle - 30, 0, 0, 1 );
    invViewSpace.translate( -128, -128, -128 );

    for (int y = 0; y < 256; y++)
    {
        QVector3D pntA(0, y, -10);
        pntA = invViewSpace.map(pntA);
        QVector3D pntB(256, y, -10);
        pntB = invViewSpace.map(pntB);
        QVector3D dxpnt1 = (pntB - pntA) / 256.0f;

        QVector3D pntC(0, y, 256);
        pntC = invViewSpace.map(pntC);
        QVector3D pntD(256, y, 256);
        pntD = invViewSpace.map(pntD);
        QVector3D dxpnt2 = (pntD - pntC) / 256.0f;

        int ipntA[3];
        int ipntC[3];
        short int idPntA[3];
        short int idPntC[3];

        const float scaleUp = 256.0f;//65536.0f;
        const int iScaleUp = 256;//65536;
        const int shiftDown = 8;//16;

        ipntA[0] = pntA.x() * scaleUp;
        ipntA[1] = pntA.y() * scaleUp;
        ipntA[2] = pntA.z() * scaleUp;
        ipntC[0] = pntC.x() * scaleUp;
        ipntC[1] = pntC.y() * scaleUp;
        ipntC[2] = pntC.z() * scaleUp;
        idPntA[0] = dxpnt1.x() * scaleUp;
        idPntA[1] = dxpnt1.y() * scaleUp;
        idPntA[2] = dxpnt1.z() * scaleUp;
        idPntC[0] = dxpnt2.x() * scaleUp;
        idPntC[1] = dxpnt2.y() * scaleUp;
        idPntC[2] = dxpnt2.z() * scaleUp;

        for (int x = 0; x < 256; x++)
        {
            data[y*256+x] = 0xff000000;
            bool inside = false;
/*
            QVector3D pnt = pntA;//(x, y, 0);
            pntA += dxpnt1;
            QVector3D pnt2 = pntC;//(x, y, 0);
            pntC += dxpnt2;
*/
            int ipnt[3];
            int ipnt2[3];
            short int idpnt[3];
            for (int i = 0; i < 3; i++)
            {
                ipnt[i] = ipntA[i];
                ipnt2[i] = ipntC[i];
                ipntA[i] += idPntA[i];
                ipntC[i] += idPntC[i];
                idpnt[i] = (ipnt2[i] - ipnt[i]) / 256;
            }

/*
            QVector3D pnt(x, y, 0);
            pnt = invViewSpace.map(pnt);
            QVector3D pnt2(x, y, 256);
            pnt2 = invViewSpace.map(pnt2);
*/

/*
            QVector3D dpnt = (pnt2 - pnt) / 256.0f;
            
            int px = pnt.x();
            int py = pnt.y();
            int pz = pnt.z();
*/
            int px, py, pz;

            int c = -10;

            for (int j = 0; j < 3; j++)
            {
                int cnt = 1;
                if ( ipnt[j] < 0 )
                {
                    if ( idpnt[j] > 0 ) {
                        cnt = -ipnt[j] / idpnt[j];

                    }
                }
                else if ( ipnt[j] > 256*iScaleUp )
                {
                    if ( idpnt[j] < 0 ) {
                        cnt = (ipnt[j] - 256*iScaleUp) / -idpnt[j];
                    }
                }
                for (int i = 0; i < 3; i++)
                    ipnt[i] += (cnt - 1) * idpnt[i];
                c += cnt - 1;
            }

            int iterations = 512;

            for (int j = 0; j < 3; j++)
            {
                if ( idpnt[j] > 0 )
                {
                    int its = (256*iScaleUp - ipnt[j]) / idpnt[j];
                    if ( its < 512 )
                    {
                        iterations = its;
                    }
                }
                else if ( idpnt[j] < 0 )
                {
                    int its = ipnt[j] / -idpnt[j];
                    if ( its < 512 )
                    {
                        iterations = its;
                    }
                }
            }

            //for (int z = -10; z <= 256; z++)
            for (int z = 0; z <= iterations; z++)
            {
            /*
                pnt += dpnt;

                px = pnt.x();
                py = pnt.y();
                pz = pnt.z();
           */
           

                px = ipnt[0] >> shiftDown;// / 65536;
                py = ipnt[1] >> shiftDown;// / 65536;
                pz = ipnt[2] >> shiftDown;/// 65536;
                        
                        
                while ( z <= iterations && py <= face[3][pz * 256 + px] )
                {
                    for (int i = 0; i < 3; i++)
                        ipnt[i] += idpnt[i];
                    px = ipnt[0] >> shiftDown;// / 65536;
                    py = ipnt[1] >> shiftDown;// / 65536;
                    pz = ipnt[2] >> shiftDown;/// 65536;
                    z++;
                }
                while ( z <= iterations && pz <= face[2][(256 - py) * 256 + px] )
                {
                    for (int i = 0; i < 3; i++)
                        ipnt[i] += idpnt[i];
                    px = ipnt[0] >> shiftDown;// / 65536;
                    py = ipnt[1] >> shiftDown;// / 65536;
                    pz = ipnt[2] >> shiftDown;/// 65536;
                    z++;
                }
                while ( z <= iterations && px <= face[0][(256 - py) * 256 + (256 - pz)] )
                {
                    for (int i = 0; i < 3; i++)
                        ipnt[i] += idpnt[i];
                    px = ipnt[0] >> shiftDown;// / 65536;
                    py = ipnt[1] >> shiftDown;// / 65536;
                    pz = ipnt[2] >> shiftDown;/// 65536;
                    z++;
                }

                while ( z <= iterations && (256 - py) <= face[1][(256 - pz) * 256 + px] )
                {
                    for (int i = 0; i < 3; i++)
                        ipnt[i] += idpnt[i];
                    px = ipnt[0] >> shiftDown;// / 65536;
                    py = ipnt[1] >> shiftDown;// / 65536;
                    pz = ipnt[2] >> shiftDown;/// 65536;
                    z++;
                }

                while ( z <= iterations && (256 - px) <= face[4][(256 - py) * 256 + pz] )
                {
                    for (int i = 0; i < 3; i++)
                        ipnt[i] += idpnt[i];
                    px = ipnt[0] >> shiftDown;// / 65536;
                    py = ipnt[1] >> shiftDown;// / 65536;
                    pz = ipnt[2] >> shiftDown;/// 65536;
                    z++;
                }

                while ( z <= iterations && (256 - pz) <= face[5][(256 - py) * 256 + (256 - px)] )
                {
                    for (int i = 0; i < 3; i++)
                        ipnt[i] += idpnt[i];
                    px = ipnt[0] >> shiftDown;// / 65536;
                    py = ipnt[1] >> shiftDown;// / 65536;
                    pz = ipnt[2] >> shiftDown;/// 65536;
                    z++;
                }

/*
                if ( px > 0 && px < 256 && 
                     py > 0 && py < 256 && 
                     pz > 0 && pz < 256 )
                     */
                {
                    // inside = true;

                    int i3 = (256 - px);
                    int i4 = (256 - pz);
                    int i5 = (256 - py);
                    int j1 = i5 * 256;
                    int j2 = i4 * 256;
                    int j3 = pz * 256;

                    
                    // (65536+256) - ((ipnt[1] >> 8) & ~0xFF) - (ipnt[2] >> 16);


                    if (
                           ( px > face[0][j1 + i4] )
                        && ( i5 > face[1][j2 + px] )
                        && ( pz > face[2][j1 + px] )
                        && ( py > face[3][j3 + px] )
                        && ( i3 > face[4][j1 + pz] )
                        && ( i4 > face[5][j1 + i3] ) 
                       )
                    {
                        //int c = z;//128;
                        int col = z + c;//128;
                        data[(y<<8)+x] = 0xff000000 | (col << 16) | (col << 8) | col;
                        break;
                    }
                }
                /*
                else
                {
                    if (inside)
                    {
                        continue;
                    }
                }
                */

                for (int i = 0; i < 3; i++)
                    ipnt[i] += idpnt[i];
            }
        }
    }

/*
    for (int i = 0; i < 256*256; i++)
    {
        unsigned char z = face[0][i];
        data[i] = 0xff000000 | (z << 16) | (z << 8) | z;
    }
*/

    p.drawImage(0, 0, output);
    
    p.setPen( Qt::green );
    p.drawText( 10, 30, QString("fps: %1").arg(calcFPS()) );
}


void Viewer::mouseMoveEvent(QMouseEvent *me)
{
}


void Viewer::mouseClickEvent(QMouseEvent *me)
{
}


