#include "glview.h"



GLView::GLView(Scene *scene,Camera * camera,Controler *controler )
{
    this->camera = camera;
    this->scene = scene;
    this->controler = controler;
    gridSize = 5;
    gridStepSize = 1;
    isGridEnabled = false;
}

QSize GLView::minimumSizeHint() const
{
    return QSize(50, 50);
}

QSize GLView::sizeHint() const
{
    return QSize(600, 400);
}


void GLView::initializeGL ( ) {
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glEnable(GL_DEPTH_TEST);


    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    static GLfloat lightPosition[4] = { 0.0, 0.0, 4.0, 1.0 };
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);



    //Shader Setup
    initShader();
    shader->bind();
}

void GLView::initShader()
{
    QGLShader *vertex = new QGLShader(QGLShader::Vertex);
    if(!vertex->compileSourceFile(QLatin1String(":/phong.vert")))
        qCritical()<< "Vertex Shader failed"<< vertex->log();

    QGLShader *fragment = new QGLShader(QGLShader::Fragment);
    if(!fragment->compileSourceFile(QLatin1String(":/phong.frag")))
        qCritical()<< "Fragment Shader failed"<< fragment->log();

    shader = new QGLShaderProgram(this);
    shader->addShader(vertex);
    shader->addShader(fragment);
    shader->bindAttributeLocation("pickID",4);
    if(!shader->link())
        qCritical()<< "Linking  failed"<<shader->log();



    vertex = new QGLShader(QGLShader::Vertex);
    if(!vertex->compileSourceFile(QLatin1String(":/display.vert")))
        qCritical()<< "Vertex Shader 2 failed"<< vertex->log();

    fragment = new QGLShader(QGLShader::Fragment);
    if(!fragment->compileSourceFile(QLatin1String(":/display.frag")))
        qCritical()<< "Fragment Shader 2 failed"<< fragment->log();

    displayShader = new QGLShaderProgram(this);
    displayShader->addShader(vertex);
    displayShader->addShader(fragment);
    if(!displayShader->link())
        qCritical()<< "Linking  failed"<<displayShader->log();
}

void GLView::home(){
    camera->home();
    updateGL();
}



void GLView::paintGL ()
{
    bool useFBO = true;
    QGLFunctions functions = QGLFunctions(this->context());

    shader->bind();
    if(useFBO)
        functions.glBindFramebuffer(GL_FRAMEBUFFER,fbo);


    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);

    glViewport(0,0,this->width(),this->height());

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();


    shader->setUniformValue("shaded",true);
    //qDebug()<<"IsLinked"<<shader->isLinked();

    //set Projection and Camera Rotation
    camera->setupCamera(aspect);

    //draw Scene
    scene->draw(shader);

    shader->setAttributeValue(3,0);

    if(isGridEnabled){
        drawGrid();
    }



    if(isActive){
        glDisable(GL_DEPTH_TEST);
        glMatrixMode (GL_MODELVIEW);
        glLoadIdentity ();
        glMatrixMode (GL_PROJECTION);
        glLoadIdentity ();
        shader->setUniformValue("shaded",false);
        glLineWidth(10);
        GLfloat color[] = {1.0,1.0,0.0};
        glMaterialfv(GL_FRONT,GL_AMBIENT,color);
        glMaterialfv(GL_FRONT,GL_DIFFUSE,color);
        glMaterialfv(GL_FRONT,GL_SPECULAR,color);
        glMaterialf(GL_FRONT,GL_SHININESS,128);

        glBegin (GL_LINE_LOOP);
        glVertex3i (-1, -1, 0);
        glVertex3i (1, -1, 0);
        glVertex3i (1, 1, 0);
        glVertex3i (-1, 1, 0);
        glEnd ();
    }

    shader->release();
    if(useFBO){
        displayShader->bind();
        //

        functions.glBindFramebuffer(GL_FRAMEBUFFER,0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glViewport(0,0,this->width(),this->height());
        glMatrixMode (GL_MODELVIEW);
        glLoadIdentity ();
        glMatrixMode (GL_PROJECTION);
        glLoadIdentity ();

        functions.glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D,color);
        displayShader->setUniformValue("Texture",0);

        functions.glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D,picID);
        displayShader->setUniformValue("TextureID",1);

        displayShader->setUniformValue("active",scene->getActive()->getID());

        glBegin (GL_QUADS);
        glTexCoord2d(0,0);
        glVertex3i (-1, -1, 0);
        glTexCoord2d(1,0);
        glVertex3i (1, -1, 0);
        glTexCoord2d(1,1);
        glVertex3i (1, 1, 0);
        glTexCoord2d(0,1);
        glVertex3i (-1, 1, 0);
        glEnd ();



        displayShader->release();

    }

}


void GLView::drawGrid()
{
    shader->release();

    GLfloat specularColor[] = {0,0,0};
    GLfloat shininess[] = {128};
    glMaterialfv(GL_FRONT,GL_SPECULAR,specularColor);
    glMaterialfv(GL_FRONT,GL_SHININESS, shininess);
    GLfloat grey[] = {1,0.5,0.5};
    glMaterialfv(GL_FRONT,GL_DIFFUSE,grey);
    glNormal3f(0,1,0);

    glDisable(GL_LIGHTING);
    glEnable(GL_COLOR_MATERIAL);

    glLineWidth(1);
    glPolygonMode(GL_FRONT_AND_BACK,GL_LINES);
    glBegin(GL_LINES);
    float stepSize = gridStepSize;
    float x = stepSize;
    float y = stepSize;
    if (stepSize <= 0) stepSize = 1;

    for(; x < gridSize; x += stepSize){
        glVertex3f(x,0,-gridSize);
        glVertex3f(x,0,gridSize);
        glVertex3f(-x,0,-gridSize);
        glVertex3f(-x,0,gridSize);
    }
    for (; y < gridSize; y += stepSize){
        glVertex3f(-gridSize, 0, y);
        glVertex3f(gridSize,0,y);
        glVertex3f(-gridSize,0,-y);
        glVertex3f(gridSize,0,-y);
    }
    glEnd();

    glBegin(GL_LINES);
    x = stepSize;
    y = stepSize;
    if (stepSize <= 0) stepSize = 1;

    for(; x < gridSize; x += stepSize){
        glVertex3f(0,x,-gridSize);
        glVertex3f(0,x,gridSize);
        glVertex3f(0,-x,-gridSize);
        glVertex3f(0,-x,gridSize);
    }
    for (; y < gridSize; y += stepSize){
        glVertex3f(0,-gridSize,  y);
        glVertex3f(0,gridSize,y);
        glVertex3f(0,-gridSize,-y);
        glVertex3f(0,gridSize,-y);
    }
    glEnd();

    glBegin(GL_LINES);
    x = stepSize;
    y = stepSize;
    if (stepSize <= 0) stepSize = 1;

    for(; x < gridSize; x += stepSize){
        glVertex3f(x,-gridSize,0);
        glVertex3f(x,gridSize,0);
        glVertex3f(-x,-gridSize,0);
        glVertex3f(-x,gridSize,0);
    }
    for (; y < gridSize; y += stepSize){
        glVertex3f(-gridSize, y,0);
        glVertex3f(gridSize,y,0);
        glVertex3f(-gridSize,-y,0);
        glVertex3f(gridSize,-y,0);
    }
    glEnd();

    glBegin(GL_LINES);
    glVertex3f(0,0,-gridSize);
    glVertex3f(0,0,gridSize);
    glEnd();
    glBegin(GL_LINES);
    glVertex3f(-gridSize,0,0);
    glVertex3f(gridSize,0,0);
    glEnd();
    glBegin(GL_LINES);
    glVertex3f(0,-gridSize,0);
    glVertex3f(0,gridSize,0);
    glEnd();




    glPolygonMode(GL_FRONT,GL_FILL);
    glEnable(GL_LIGHTING);
    shader->bind();

}

void GLView::resizeGL(int width , int height )
{
    aspect = 1.0*width/height;

    QGLFunctions functions = QGLFunctions(this->context());

    functions.glGenFramebuffers(1, &fbo);
    functions.glBindFramebuffer(GL_FRAMEBUFFER,fbo);



    // Create the color buffer
    glGenTextures(1, &color);
    glBindTexture(GL_TEXTURE_2D, color);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);


    // Create the color buffer
    glGenTextures(1, &picID);
    glBindTexture(GL_TEXTURE_2D, picID);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);


    // Create the depth buffer
    functions.glGenRenderbuffers(1, &depth);
    functions.glBindRenderbuffer(GL_RENDERBUFFER, depth);
    functions.glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT,width,height);

    functions.glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,color,0);
    functions.glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,picID,0);
    functions.glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,depth);

    GLenum err = functions.glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if(err == GL_FRAMEBUFFER_COMPLETE){
        qDebug()<<"FBO OK";
    } else {
        qDebug()<<"FBO ERROR"<<err;
    }

    functions.glBindFramebuffer(GL_FRAMEBUFFER, 0);


}



void GLView::setAcive(bool active)
{
    this->isActive = active;
    updateGL();
}

Camera *GLView::getCamera()
{
    return this->camera;
}


void GLView::mousePressEvent(QMouseEvent *event )
{
    controler->mousePressed(this,event);
}

void GLView::mouseMoveEvent(QMouseEvent *event )
{
    controler->mouseMoveEvent(this,event);
}

void GLView::wheelEvent(QWheelEvent *event )
{
    controler->wheelEvent(this,event);
}

