/*
* Copyright (c) 2011 Nokia Corporation.
*/

#include <math.h>
#include "cmirrorhouse.h"
#include <QDebug>

CMirrorHouse::CMirrorHouse()
{
    m_transMap = 0;
    m_selectedTransformPower = 0.0f;
    m_selectedTransformSize = 0.0f;
    m_currentTransformPower = 0.0f;
    m_currentTransformSize = 0.0f;
    m_hquality = true;

    m_sourceData = 0;
    m_sourceWidth = 0;
    m_sourceHeight = 0;

    m_targetData = 0;
    m_targetWidth = 0;
    m_targetHeight = 0;
}


CMirrorHouse::~CMirrorHouse() {
    recreateTransformMap( 0,0 );    // release
};

void CMirrorHouse::setSource(unsigned int *data, int w, int h, int pitch) {
    if (m_sourceWidth != w || m_sourceHeight != h)
        m_currentTransform = eNONE;     // must be recreated
    m_sourceData = data;
    m_sourceWidth = w;
    m_sourceHeight = h;
    m_sourcePitch = pitch;
};


void CMirrorHouse::setTarget(unsigned int *data, int w, int h, int pitch) {
    if (m_targetWidth != w || m_targetHeight != h)
        recreateTransformMap(0,0);              // release, must be updated.
    m_targetData = data;
    m_targetWidth = w;
    m_targetHeight = h;
    m_targetPitch = pitch;
};


void CMirrorHouse::recreateTransform( eMIRRORTRANSFORM trans, float power, float size ) {

    //qDebug() << "recreateTransform";
    float ftemp,a;
    float fx,fy,fz;
    float nx,ny,nz;

    int xinc = ((m_sourceWidth)<<14) / m_targetWidth;
    int yinc = ((m_sourceHeight)<<14) / m_targetHeight;

    int maxx = ((m_sourceWidth-1)<<14)-1;
    int maxy = ((m_sourceHeight-1)<<14)-1;

    float pixelMul = (float)(m_sourceWidth + (float)m_sourceHeight) * 2000.0f / m_selectedTransformSize;




    int *t = m_transMap;
    int sy = 0;
    for (int y=0; y<m_targetHeight; y++) {
        int sx = 0;
        for (int x=0; x<m_targetWidth; x++) {


            switch (trans) {
                default:
                case eNONE:
                    fx = 0.0f;
                    fy = 0.0f;
                break;

                case eDITHER1:
                    fx = -1.0f + (float)(rand()&255)/128.0f;
                    fy = -1.0f + (float)(rand()&255)/128.0f;
                    fx*=power;
                    fy*=power;
                    break;
                case eTILE1:
                    fx = ((float)x / (float)m_targetWidth);
                    fy = ((float)y / (float)m_targetHeight);
                    fx*=size;
                    fy*=size;
                    fx -= floor(fx);
                    fy -= floor(fy);
                    fx = (fx-0.5f) * 2.0f;
                    fy = (fy-0.5f) * 2.0f;
                    fx*=power;
                    fy*=power;

                    break;
                case eSPIKE:
                    fx = ((float)x / (float)m_targetWidth - 0.5f)*2.0f;
                    fy = ((float)y / (float)m_targetHeight - 0.5f)*2.0f;
                    ftemp = sqrtf( fx*fx+fy*fy );
                    fx/=ftemp;
                    fy/=ftemp;
                    ftemp = (1.0f - ftemp);
                    if (ftemp<0.0f) ftemp = 0.0f;
                    ftemp*=power;
                    fx*=ftemp;
                    fy*=ftemp;

                    break;


                case eRIPPLE:
                    fx = ((float)x / (float)m_targetWidth - 0.5f)*2.0f;
                    fy = ((float)y / (float)m_targetHeight - 0.5f)*2.0f;
                    ftemp = sqrtf( fx*fx+fy*fy );
                    fx /= ftemp;
                    fy /= ftemp;
                    fx = fx*sinf( ftemp * size * 3.14159f*2.0f ) * power;
                    fy = fy*sinf( ftemp * size * 3.14159f*2.0f ) * power;

                    break;

                case eSPIRAL:
                    //fx = (sinf( (float)x/(float)m_targetWidth *3.14159f * 2.0f * m_selectedTransformSize))*power;
                    //fy = (sinf( (float)y/(float)m_targetHeight * 3.14159f * 2.0f * m_selectedTransformSize))*power;
                    fx = ((float)x / (float)m_targetWidth - 0.5f)*2.0f;
                    fy = ((float)y / (float)m_targetHeight - 0.5f)*2.0f;
                    ftemp = sqrtf( fx*fx+fy*fy );
                    a = atan2(fy,fx);
                    a += (sqrtf(2.0f)-ftemp)*20.0f*power;
                    fx = -fx/4+sinf( a )*ftemp;
                    fy = -fy/4+cosf( a )*ftemp;
                    fx *= size;
                    fy *= size;
                    break;


                case eBUBBLES:
                    fx = ((float)x / (float)m_targetWidth - 0.5f)*2.0f;
                    fy = ((float)y / (float)m_targetHeight - 0.5f)*2.0f;
                    ftemp = sqrtf( fx*fx+fy*fy );
                    ftemp*=ftemp*ftemp;
                    ftemp = 1.0f - ftemp;
                    if (ftemp<0.0f) ftemp = 0.0f;

                    fx = (sinf( (float)x/(float)m_targetWidth *3.14159f * 2.0f * m_selectedTransformSize))*power;
                    fy = (sinf( (float)y/(float)m_targetHeight * 3.14159f * 2.0f * m_selectedTransformSize))*power;
                    break;

                case eINVBUBBLES:
                    fx = ((float)x / (float)m_targetWidth - 0.5f)*2.0f;
                    fy = ((float)y / (float)m_targetHeight - 0.5f)*2.0f;
                    /*
                    ftemp = (sqrtf( fx*fx+fy*fy ) / sqrtf( 2.0f ));
                    */
                    ftemp = sqrtf( fx*fx+fy*fy );
                    ftemp*=ftemp*ftemp;
                    ftemp = 1.0f - ftemp;
                    if (ftemp<0.0f) ftemp = 0.0f;

                    fx = (cosf( (float)x/(float)m_targetWidth *3.14159f * 2.0f * m_selectedTransformSize))*power;
                    fy = (cosf( (float)y/(float)m_targetHeight * 3.14159f * 2.0f * m_selectedTransformSize))*power;
                    fx*=ftemp;
                    fy*=ftemp;



                break;

                case eVERTICAL_WAVE:
                    fx = 0.0f;
                    fy = (sinf( (float)y/(float)m_targetHeight * 3.14159f * 2.0f * m_selectedTransformSize))*power;
                    break;

                case eHORIZONTAL_WAVE:
                    fx = (sinf( (float)x/(float)m_targetWidth * 3.14159f * 2.0f * m_selectedTransformSize))*power;
                    fy = 0.0f;
                    break;
            };





            /*
            if (fabsf( fx ) < fabsf(fy ) )
                fz = sqrtf( 1.0f - fx*fx );
            else fz = sqrtf( 1.0f - fy*fy );
            */


            fz = sqrtf( 3.0f-(fx*fx+fy*fy) );


            // calculate reflectionmul
            ftemp = sqrtf( fx*fx+fy*fy+fz*fz );
            nx = fx/ftemp;
            ny = fy/ftemp;
            nz = fz/ftemp;

            ftemp = nx*-0.57f+ny*-0.57f+nz*0.57f;
            //ftemp = nx+ny;
            //ftemp = nx*0.77f+ny*0.77f;
            //ftemp = nx+ny;






            ftemp = ftemp-0.5f;
            ftemp *= 2.0f;
            if (ftemp<0.0f) ftemp = 0.0f;
            ftemp = ftemp*ftemp*ftemp;
            if (ftemp>1.0f) ftemp = 1.0f;


            t[2] = (int)(ftemp*100.0f);


                // place the transform coordinate into the array
            t[0] = sx+(int)(fx*pixelMul);
            t[1] = sy+(int)(fy*pixelMul);

            if (t[0]<0) t[0] = 0;
            if (t[1]<0) t[1] = 0;
            if (t[0]>maxx) t[0] = maxx;
            if (t[1]>maxy) t[1] = maxy;
            sx+=xinc;
            t+=3;
        }
        sy += yinc;
    }

    m_currentTransform = trans;
    m_currentTransformPower = power;
    m_currentTransformSize = size;
};


void CMirrorHouse::recreateTransformMap( int w, int h ) {
    //qDebug() << "recreateTransformMAP";
    if (m_transMap) delete [] m_transMap;
    m_currentTransform = eNONE;
    m_currentTransformPower = 0.0f;
    if (w<1 || h<1) return;
    m_transMap = new int[w*h*3];
};


void CMirrorHouse::processLine( unsigned int *t, unsigned int *t_target, int *srcCoords ) {
    while (t!=t_target) {
        *t = m_sourceData[ (srcCoords[1]>>14)*m_sourcePitch + (srcCoords[0]>>14)];
        t++;
        srcCoords+=3;
    }
}


void CMirrorHouse::processLine_hq( unsigned int *t, unsigned int *t_target, int *srcCoords ) {
    int x,y;
    unsigned int temp, mask;
    unsigned int *pos;

    while (t!=t_target) {
        x = srcCoords[0];
        y = srcCoords[1];
        pos = m_sourceData + m_sourcePitch * (y>>14) + (x>>14);
        x = ((x&16383)>>7);
        y = ((y&16383)>>7);
        temp = 128-x;
        *t =   (
                (((( (((((pos[0]&0x00FF00FF)*(temp)) + ((pos[1]&0x00FF00FF)*(x)) ) >> 7)&0x00FF00FF) * (128-y))  +
                   ( (((((pos[m_sourcePitch]&0x00FF00FF)*(temp)) + ((pos[m_sourcePitch]&0x00FF00FF)*(x)) ) >> 7)&0x00FF00FF) * y)) >> 7) & 0x00FF00FF) |
                ((((( ((((((pos[0]>>8)&0x00FF00FF)*(temp)) + (((pos[1]>>8)&0x00FF00FF)*(x)) ) >> 7)&0x00FF00FF) * (128-y))  +
                    ( ((((((pos[m_sourcePitch]>>8)&0x00FF00FF)*(temp)) + (((pos[m_sourcePitch+1]>>8)&0x00FF00FF)*(x)) ) >> 7)&0x00FF00FF) * y)) >> 7) & 0x00FF00FF) << 8));

        if (srcCoords[2]>0) {
            temp = ((*t&0xFEFEFEFE)>>1)+(((srcCoords[2]|(srcCoords[2]<<8)|(srcCoords[2]<<16))&0xFEFEFEFE)>>1);
            mask = (temp&0x80808080);
            temp |= (mask-(mask>>7));
            *t = ((temp&0x7F7F7F7F)<<1);
        };


        t++;
        srcCoords+=3;
    }
}




bool CMirrorHouse::process() {
        // Don't continue if source or target is not set
    if (!m_sourceData || !m_targetData) return false;

    if (m_transMap==0) {
        recreateTransformMap( m_targetWidth, m_targetHeight );
    };

        // The transform needzs to be (re)created
    if (m_currentTransform != m_selectedTransform ||
        m_currentTransformPower!=m_selectedTransformPower ||
        m_currentTransformSize!=m_selectedTransformSize)
        recreateTransform( m_selectedTransform,
                          m_selectedTransformPower,
                          m_selectedTransformSize );


    for (int y=0; y<m_targetHeight; y++) {
        if (m_hquality==false)
            processLine(m_targetData+m_targetPitch*y,
                        m_targetData+m_targetPitch*y+m_targetWidth,
                        m_transMap + m_targetWidth*y*3 );
        else
            processLine_hq(m_targetData+m_targetPitch*y,
                        m_targetData+m_targetPitch*y+m_targetWidth,
                        m_transMap + m_targetWidth*y*3 );
    };

    return true;
};
