
#include <QApplication>
#include <QDebug>
#include <QMouseEvent>
#include "QtTileGameEngine.h"
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

    // FONT STRING FOR GIMP
/*
 !"#$% '()*+,-./
0123456789:;<=>?
@ABCDEFGHIJKLMNO
PQRSTUVWXYZ[\]^_
`abcdefghijklmno
pqrstuvwxyz{|}~
*/



const char *audioSamples[ AUDIO_SAMPLE_COUNT ] = {
    "click.wav",
    "change.wav",                   // milton
    "change_completed.wav",         // milton
    "destroy.wav",                      // milton
    "fanfare.wav",                  // by milton
    "game_over.wav",                    // by milton
    "illegal_move.wav",                 // milton
    "next_level.wav",               // by milton @ freesound
    "exp_bonus.wav",                 // by mattwasser
    "xbonus.wav",                 // by Christianjinnyzoe
};


#define AUDIO_SAMPLE_CLICK 0
#define AUDIO_SAMPLE_CHANGE 1
#define AUDIO_SAMPLE_CHANGE_COMPLETED 2
#define AUDIO_SAMPLE_DESTROY 3
#define AUDIO_LEVEL_COMPLETED 4
#define AUDIO_GAME_OVER 5
#define AUDIO_ILLEGAL_MOVE 6
#define AUDIO_NEXT_LEVEL 7
#define AUDIO_SAMPLE_EXP_BONUS 8
#define AUDIO_SAMPLE_X_BONUS 9



const int vboStride = 2 * sizeof(GLfloat);
const GLfloat vboVertices[] = {
        -1, -1,
        1, -1,
        1, 1,
        -1, 1,
};

float matrixProjection[16]=
{
#ifdef Q_WS_HARMATTAN
    0, 2/65536.0f, 0, 0,
    2/(1.5f*65536), 0, 0, 0,
    0, 0, 1, 0,
    -1, -1, 0, 1,
#else
   2/65536.0f, 0, 0, 0,
   //0, -2/(640.0f/360.0f*65536.0f), 0, 0,
   0, -2/(1.5f*65536.0f), 0, 0,
   0, 0, 1, 0,
   -1, 1, 0, 1,
#endif
};




const char *glslPixel =
"uniform sampler2D texture;"
"varying mediump vec2 uv;"
"varying mediump float alpha;"
"void main(void)"
"{"
"gl_FragColor = texture2D(texture,uv)*vec4(1.0,1.0,1.0,alpha);"
"}";


const char *glslVertex =
"uniform mat4 m_projection;"
"uniform vec4 size_tscale;"
"uniform vec4 tpos_position;"
"uniform vec2 angle_alpha;"
"attribute vec4 vert;"
"varying vec2 uv;"
"varying float alpha;"
"void main(void) { "
"float a=angle_alpha.x*(3.1415*2.0/65536.0); "
"vec2 rot = vec2(vert.x*cos(a) + vert.y*sin(a), -vert.x*sin(a) + vert.y*cos(a)); "
"gl_Position = m_projection*vec4(tpos_position.zw+rot.xy*size_tscale.xy,0.0,1.0);"
"uv = (vert.xy+vec2(1.0,1.0))*vec2(0.5,0.5) * size_tscale.zw + tpos_position.xy; alpha = angle_alpha.y; }";




QtTileGameEngine::QtTileGameEngine( QWidget *parent ) : GE::GameWindow( parent ) {
    theGame = 0;
};


QtTileGameEngine::~QtTileGameEngine() {
    if (theGame) delete theGame;

}


void QtTileGameEngine::update(const float fFrameDelta ) {
    float ft = fFrameDelta;
    if (ft>0.3f) ft = 0.3f;
    if (ft<0.00001f) ft = 0.00001f;
    if (theGame)
        theGame->run( (int)(ft*65536.0f) );
};

void QtTileGameEngine::onRender() {

    if (theGame)
        theGame->draw();

    //renderTile( 1000,1000,40000,40000, 0, 0, 0, 0 );
};

int QtTileGameEngine::onCreate() {
    previousMode=-1;

    // load textures
    for(int i=0;i<TexEndOfList;i++) {
        load((TextureID)i);
    }
    //buyMoreHack=0;


    pixelShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(pixelShader, 1, (const char**)&glslPixel, NULL);
    //glShaderSource(pixelShader, 1, (const char**)&pszFragShader, NULL);//glslPixel
    glCompileShader(pixelShader);

    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, (const char**)&glslVertex, NULL);
    glCompileShader(vertexShader);

    program = glCreateProgram();

    // Attach the fragment and vertex shaders to it
    glAttachShader(program, pixelShader);
    glAttachShader(program, vertexShader);

    glBindAttribLocation(program, 0, "vert");
    //glBindAttribLocation(program, 1, "uv");

    glLinkProgram(program);
    glUseProgram(program);

    glUniform1i(glGetUniformLocation(program, "texture"), 0);
    glUniformMatrix4fv(glGetUniformLocation(program, "m_projection"), 1, GL_FALSE, matrixProjection);

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glBufferData(GL_ARRAY_BUFFER, 4 * vboStride, vboVertices, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, vboStride, 0);

    GLint bLinked;
    glGetShaderiv(pixelShader, GL_COMPILE_STATUS, &bLinked);
    qDebug() << "PixelShader compiled : "<< bLinked;

    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &bLinked);
    qDebug() << "VertexShader compiled : "<< bLinked;

    glGetProgramiv(program, GL_LINK_STATUS, &bLinked);
    qDebug() << "Program linked : "<< bLinked;


    angleAlphaLocation = glGetUniformLocation(program, "angle_alpha");
    sizeTScaleLocation = glGetUniformLocation(program, "size_tscale");
    tposPositionLocation = glGetUniformLocation(program, "tpos_position");


//printf("program %d\n", bLinked);


    // load samples
    for (int f=0; f<AUDIO_SAMPLE_COUNT; f++) {
        QString str = ":/" + QString(audioSamples[f]);
        m_audioBuffers[f] = GE::CAudioBuffer::loadWav( str );
        qDebug() << "Loading audio sample (" << str << "): " << m_audioBuffers[f];
    };

    theGame = createTileGame( this,-2500,0,65536+5000, 65536*3/2+2500 );

    getMixer().setAbsoluteVolume(0.5f);

    return 1;
};

void QtTileGameEngine::onDestroy() {

    for (int f=0; f<AUDIO_SAMPLE_COUNT; f++) {
        qDebug() << "Releasing audiosample: " << m_audioBuffers[f];
        if (m_audioBuffers[f]) delete m_audioBuffers[f];
        m_audioBuffers[f] = 0;
    };

    if (theGame) {
        delete theGame;
        theGame = 0;
    };


};


GameTexture *QtTileGameEngine::bind(TextureID id)
{
        static TextureID prevId=(TextureID)-1;
        static GameTexture *t;

        if(id==prevId) return t;

        prevId=(TextureID)-1;
        t=&gameTextures[0];
        while(1)
        {
                if(t->id==TexEndOfList) return NULL;
                if(t->id==id)
                {
                        glBindTexture(GL_TEXTURE_2D, t->glId);
                        prevId=id;
                        return t;
                }
                t++;
        }
        return NULL;
}

GLuint QtTileGameEngine::loadGLTexture( const char *fn ) {
    QString filename = QString( fn );
    QImage image = QImage( filename );
    if (image.width() < 1 || image.height() < 1 ) return 0;

    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    GLuint* pTexData = new GLuint[image.width() * image.height()];
    GLuint* sdata = (GLuint*)image.bits();
    GLuint* tdata = pTexData;

    for (int y=0; y<image.height(); y++)
        for (int x=0; x<image.width(); x++) {
            //*tdata = *sdata;
            *tdata = ((*sdata&255) << 16) | (((*sdata>>8)&255) << 8)
                    | (((*sdata>>16)&255) << 0) | (((*sdata>>24)&255) << 24);
            sdata++;
            tdata++;
    };

    qDebug() << "texture : " << image.width() << " " << image.height();
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, pTexData);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST );
    glGenerateMipmap( GL_TEXTURE_2D );
    delete [] pTexData;
    return texture;
};



void QtTileGameEngine::load(TextureID id)
{
        GameTexture *t=&gameTextures[0];
        while(1)
        {
                if(t->id==TexEndOfList) return;
                if(t->id==id)
                {
                        glBindTexture(GL_TEXTURE_2D, t->id);
                        char s[256];
                        /*
                        sprintf(s, "%s.pvr", t->name);
                        t->glId=loadTexture(s);
                        */


                        sprintf(s, ":/%s.png", t->name);
                        t->glId=loadGLTexture(s);
                        qDebug() << "Loading texture:" << s << " succes( "<< t->glId << ")";


                        if(!t->tilesX) t->tilesX=1;
                        if(!t->tilesY) t->tilesY=1;

//	glUniform4f(glGetUniformLocation(program, "tscale"), 1.0f/gt->tilesX, 1.0f/gt->tilesY, 0, 0);
//	glUniform4f(glGetUniformLocation(program, "tpos"), (t%gt->tilesX)/(float)gt->tilesX, (t/gt->tilesX%gt->tilesY)/(float)gt->tilesY, 0, 0);
                        for(int i=0;i<16;i++) t->matrix[i]=0;
                        t->matrix[0]=1.0f/t->tilesX;
                        t->matrix[4*1+1]=1.0f/t->tilesY;
                        t->matrix[4*2+2]=1.0f;
                        t->matrix[4*3+3]=1.0f;

                        //t->matrix[4*3+0]=(id%t->tilesX)/(float)t->tilesX;
                        //t->matrix[4*3+1]=(id/t->tilesX%t->tilesY)/(float)t->tilesY;

                        return;
                }
                t++;
        }
}



void QtTileGameEngine::mousePressEvent( QMouseEvent *event ) {

#ifdef Q_WS_HARMATTAN
    int y = (event->x()<<16) / width();
    int x = 65536-(event->y()<<16) / height();
    y = y*3/2;
    qDebug() << "MousePress" << x << ":" <<y;
#else
    int x = (event->x()<<16) / width();
    int y = (event->y()<<16) / height();
    y = y*3/2;
#endif

    if (theGame) theGame->click(x,y, eMOUSEEVENT_BUTTONDOWN );
};

void QtTileGameEngine::mouseReleaseEvent( QMouseEvent *event ) {
#ifdef Q_WS_HARMATTAN
    int y = (event->x()<<16) / width();
    int x = 65536-(event->y()<<16) / height();
    y = y*3/2;
#else
    int x = (event->x()<<16) / width();
    int y = (event->y()<<16) / height();
    y = y*3/2;
#endif


    if (theGame) theGame->click(x,y, eMOUSEEVENT_BUTTONUP );

};

void QtTileGameEngine::mouseMoveEvent( QMouseEvent *event ) {
#ifdef Q_WS_HARMATTAN
    int y = (event->x()<<16) / width();
    int x = 65536- (event->y()<<16) / height();
    y = y*3/2;
#else
    int x = (event->x()<<16) / width();
    int y = (event->y()<<16) / height();
    y = y*3/2;
#endif


    if (theGame) theGame->click(x,y, eMOUSEEVENT_MOUSEDRAG);
};

void QtTileGameEngine::keyPressEvent( QKeyEvent *event ) {

};

void QtTileGameEngine::onPause() {

    theGame->pause();
};

void QtTileGameEngine::onResume() {
    theGame->resume();
};

void QtTileGameEngine::setSize( int w, int h ) {

    glViewport(0,0, w,h );
}


void QtTileGameEngine::renderBackground( unsigned int index )
{

        glClearColor(0,0,0, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glDisable( GL_DEPTH_TEST );
        glEnable(GL_BLEND);

}

void QtTileGameEngine::effectNotify( eTILEGAME_EFFECT effect, unsigned int arg1, unsigned int arg2 )
{
//	printf("effectNotify %x\n", effect);
        switch(effect)
        {

        /*
 eCLICK=1,
 eCHANGING,
 eCHANGE_COMPLETED,
 eEFFECT_LEVELCOMPLETED,
 eEFFECT_NEWLEVEL,
 eEFFECT_GAMEOVER,
 eILLEGAL_MOVE,
 eEFFECT_DESTROYING,
 eEFFECT_DESTROYING_BONUS,
 eEFFECT_MENU,
 eEFFECT_BUYMORE,
 eEFFECT_EXIT,

 */

        case eEFFECT_EXIT:
            qApp->exit(0);
            break;

        case eEFFECT_DESTROYING:
        case eEFFECT_DESTROYING_BONUS:
            {
             if (audioEnabled()) {
                 m_audioBuffers[ AUDIO_SAMPLE_DESTROY ]->playWithMixer( getMixer() );
                  if (arg1>0) {
                       GE::CAudioBufferPlayInstance*i = m_audioBuffers[ AUDIO_SAMPLE_EXP_BONUS ]->playWithMixer( getMixer() );
                        i->setSpeed( 1.0f + (float)arg1/12.0f );
                 }

                }
            }
            break;

        case eEFFECT_GAMEOVER:
            if (audioEnabled()) {
                m_audioBuffers[ AUDIO_GAME_OVER ]->playWithMixer( getMixer() );
            }
            break;

        case eEFFECT_NEWLEVEL:
            if (audioEnabled()) {
                m_audioBuffers[ AUDIO_NEXT_LEVEL ]->playWithMixer( getMixer() );
            }
            break;


        case eEFFECT_LEVELCOMPLETED:
            if (audioEnabled()) {
                m_audioBuffers[ AUDIO_LEVEL_COMPLETED ]->playWithMixer( getMixer() );
            }
            break;

        case eEFFECT_XBONUS:
            if (audioEnabled()) {
                m_audioBuffers[ AUDIO_SAMPLE_X_BONUS ]->playWithMixer( getMixer() );
            }
            break;

        case eEFFECT_BLOCK_BEGIN_FINISHED:
            if (audioEnabled()) {
                 GE::CAudioBufferPlayInstance *i =  m_audioBuffers[ AUDIO_SAMPLE_CHANGE ]->playWithMixer( getMixer() );
                 i->setLeftVolume( 0.1f );
                 i->setRightVolume( 0.1f );
             }
             break;

        case eEFFECT_BLOCK_VANISH_STARTED:
            if (audioEnabled()) {
                 GE::CAudioBufferPlayInstance *i =  m_audioBuffers[ AUDIO_SAMPLE_CHANGE_COMPLETED ]->playWithMixer( getMixer() );
                 i->setLeftVolume( 0.1f );
                 i->setRightVolume( 0.1f );
             }
             break;

        case eCHANGING:
           if (audioEnabled()) {
                GE::CAudioBufferPlayInstance *i = m_audioBuffers[ AUDIO_SAMPLE_CHANGE ]->playWithMixer( getMixer() );
                i->setSpeed(0.8 + (float)(rand() & 255) /255.0f * 0.4f );
            }
            break;

        case eCHANGE_COMPLETED:
            if (audioEnabled()) {
                GE::CAudioBufferPlayInstance *i = m_audioBuffers[ AUDIO_SAMPLE_CHANGE_COMPLETED ]->playWithMixer( getMixer() );
                i->setSpeed(0.8 + (float)(rand() & 255) /255.0f * 0.4f );
            }

            break;

        case eILLEGAL_MOVE:
            if (audioEnabled()) {
                GE::CAudioBufferPlayInstance *i = m_audioBuffers[ AUDIO_ILLEGAL_MOVE ]->playWithMixer( getMixer() );
                i->setSpeed(0.9 + (float)(rand() & 255) /255.0f * 0.2f );
            }
            break;

            /*
                // disabled,... clicks are annoying when scores are changed.
        case eEFFECT_SCORECHANGED:
            if (audioEnabled()) {
                m_audioBuffers[ AUDIO_SAMPLE_CLICK ]->playWithMixer( getMixer() )->setSpeed(4.0f);
            }
            break;
            */


        case eCLICK:
        case eEFFECT_MENU:
            if (audioEnabled()) {
                m_audioBuffers[ AUDIO_SAMPLE_CLICK ]->playWithMixer( getMixer() );
            }
            break;
            /*
        case eEFFECT_BUYMORE:
                //buyMoreHack=1;
                exit(0);
                break;
                */
        default:
                break;
        }
}


void QtTileGameEngine::renderTile( int x, int y,
                                int w, int h,
                                int angle,
                                int mode,
                                unsigned int tileIndex, unsigned int arg )
{
        TextureID texture=(TextureID)((tileIndex>>16)&0xff);
        //if(texture!=TexBackground) return;
        GameTexture *gt=bind(texture);
        if(!gt) return;


        if(mode!=previousMode)
        {
                if(mode==0)
                        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                else
                        glBlendFunc(GL_SRC_ALPHA, GL_ONE);

                previousMode=mode;
        }

        w>>=1;
        h>>=1;



        int t=(tileIndex&65535);

        glUniform2f(angleAlphaLocation, angle,1-((tileIndex>>24)&0xff)/255.0f);
        glUniform4f(sizeTScaleLocation, w, h, 1.0f/gt->tilesX, 1.0f/gt->tilesY);
        glUniform4f(tposPositionLocation,
                    (t%gt->tilesX)/(float)gt->tilesX, (t/gt->tilesX%gt->tilesY)/(float)gt->tilesY,
                    x+w, y+h );
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}


void QtTileGameEngine::renderForeground( unsigned int index )
{
        return;
}


int QtTileGameEngine::run( int fixedFrameTime16Bit )
{
        return 1;
}




