/*******************************************************************************
**
** The source file for the Jahshaka glworldglcore.cpp module
** The Jahshaka Project
** Copyright (C) 2000-2005 The Jahshaka Project
** Released under the GNU General Public License
**
*******************************************************************************/

#include "glworld.h"
#include "glworldlists.h"
#include "glcore.h"
#include "jahrender.h"
#include "jahstats.h"
#include <map>
#include <jahtimer.h>
#include <qtimer.h>

void GLWorld::buildEffects(JahLayer* image_layer, bool draw_only_to_selected_effect)
{
    QImage renderimage;

    GLint current_shade_model;
    glGetIntegerv(GL_SHADE_MODEL, &current_shade_model);
    glShadeModel(GL_SMOOTH);

    executeEffectsList(image_layer, true, draw_only_to_selected_effect);

    glShadeModel(current_shade_model);    
}

void GLWorld::updatePosition( )
{
    //checks to see if the play button is on and increments frames
    if ( getAnimating() )
    {  
        updateAnimation(m_currentFrame); 
        incrementCurrentFrame(1);

        if ( m_currentFrame > maxFrame ) 
        {
            m_currentFrame = minFrame;
            updateAnimation(m_currentFrame);
            updateGL();
        }
    }
}

void GLWorld::schedulePosition( )
{
    /////////////////////////////////////////////////
    // finished the scene now Wait this many msecs before redraw
    if ( m_animation ) 
    {
        startSingleShotTimer();  
    } 
}

///////////////////////////////////////////////////////////////////
// the main opengl routine that iterates through all objects
// and paints the scene for us
void GLWorld::paintGL()
{
    check_gl();

    //stop the timer only if fps timing is enabled...    
    static double timefps; //to be replaced by stats object

    //start the timer only if timing is enabled...    
    if (stats_showStats)
    {
        coretimer->starttime();
    }

    //turn on hardware aliasing for the scene if it is enabled

    if (hwaliasing)
    {
        glEnable(GL_MULTISAMPLE_ARB);
    }

    //clear the scene and set up matrices
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    //glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);

    glPushMatrix();


    //draw black video safe area if no layer present
    //if (!select) //not sure why we ifdef'd this
    //{ 
    if ( isLayerVisible(0) )     
    {  
        renderSpace->drawLayer(getLayerList()->at(0)->thelayer, false);  
    }
    else 
    {   
        renderSpace->drawLayer(titlesafe, true); 
    }
    //}

    //we position the camera here
    renderSpace->positionObject(camera->layernodes->m_node);

    //draw the grid if its visible
    if (gridval) 
    {  
        renderSpace->drawLayer(thegrid, true); 
    }

    //set up opengl layer selection
    glInitNames(); 
    glPushName(0);                      // initialize the selection stack

    int number_of_base_layers = 1;
    drawAllLayers(number_of_base_layers);

    //if textsafe is on draw it as well
    if (textsafe->layerStatus) 
    {  
        renderSpace->drawLayer(textsafe, true);  
    }

    if (hwaliasing)
    { 
        glDisable(GL_MULTISAMPLE_ARB);
    }

    glFlush(); 

    if (stats_showStats)
    {
        coretimer->endtime();
        timefps = coretimer->returnTimeFps();

        //for now we are passing the fps over
        //what we need is a stas object that stores other data for display
        drawOverlayStats(timefps);
    }

    glPopMatrix();

    check_gl();
}

////////////////////////////////////////////////////////////////
//this routine is used to update effects before a screen redraw
void  GLWorld::updateEffects()
{
    m_sliders_have_changed = true;

    if (getActiveJahLayer()->getCategory() == ObjectCategory::EFFECT)
    {
        getActiveJahLayer()->getParent()->setEffectsSlidersHaveChanged(true);
    }

}

void GLWorld::drawOverlayStats(double fps) 
{	
    ////////////////////////////////////////////
    //parse the data sent over 
    QString selectedfps = QString("Selected FPS: %1 ").arg(jahfps);
    QString coretiming  = QString("Timing: %1 ").arg( getRedrawDelay() );
    QString corefps     = QString("Core FPS: %1").arg(fps);

    QString fulldata = selectedfps + coretiming + corefps;

    std::string outtext1 = fulldata.data();

    ////////////////////////////////////////////
    //grab the rest from the stats object

    //grab the stats object
    JahStats& jstats = JahStats::getInstance();
    std::string outtext2 = jstats.getstatsdata1()+jstats.getstatsdata2();
    std::string outtext3 = jstats.getstatsdata3();

    ////////////////////////////////////////////
    //draw the text    
    glPushMatrix(); 

    //onscreen positional data
    //needs to be relative to the format 
    //ie ntsc is different from pal from 2k!
    //currently we are relative to NTSC only
    GLfloat tx  = -340.0;
    GLfloat ty  =  260.0;
    GLfloat tz  =    0.0;

    //qfonts get rendered directly from the core
    //color of text to be drawn
    glColor3f(0.0,1.0,0.0);
    core->renderText ( tx, ty, tz, outtext1.data()  );

    glColor3f(1.0,1.0,0.0);
    core->renderText ( tx, ty+40, tz, outtext2.data()  );
    core->renderText ( tx, ty+20, tz, outtext3.data()  );

    glPopMatrix();
}

void GLWorld::initializeWorld() 
{
    //set up some default variables and objects
    polySmooth=0;
    m_active_layer = NULL;
    m_currentFrame=1;
    m_lastLayer = 0; 
    select = FALSE;  // m_lastLayer is used to unselect...

    m_animation = FALSE; 
    m_animsliderselected=FALSE;  
    Globals::setRendering(false);
    lastx = lasty = 0;  
    m_currentFrame =0;

    // set up the world first   - is this necessary? what are the defaults
	if (camera)
	{ 
		camera->layernodes->m_node->sx = camera->layernodes->m_node->sy = 
        camera->layernodes->m_node->sz = camera->layernodes->m_node->sc = 1.0;
	}

    ////////////////////////////////////////////////
    // note these three should be shared display lists 
    // and not layers

    // set up the black title safe area
    //should be called rendersafe
    titlesafe->layertype = LayerCategory::LAYER; 
    titlesafe->objtype = ObjectCategory::MAINBACK;

    titlesafe->blend = TRUE; 
    titlesafe->depth = FALSE;
    titlesafe->layerStatus = TRUE;
    titlesafe->drawtheLayer = TRUE;

    // this is the green textsafe overlay
    textsafe->layernodes->m_node->sx = 0.8f; 
    textsafe->layernodes->m_node->sy = 0.8f; //it is scaled to fit here...
    textsafe->layertype = LayerCategory::FRAME; textsafe->objtype = ObjectCategory::TITLEGRID;
    textsafe->Red=0.0; textsafe->Green=1.0; textsafe->Blue=0.0;
    textsafe->blend = FALSE; textsafe->depth = FALSE;
    textsafe->foreground = TRUE; 
    textsafe->layerStatus = FALSE;
    textsafe->drawtheLayer = TRUE;

    // this is the grid
    thegrid->layertype = LayerCategory::GRID;
    thegrid->Red=0.75; thegrid->Green=0.75; thegrid->Blue=0.75;
    thegrid->blend = TRUE; thegrid->depth = TRUE;
    thegrid->layerStatus = TRUE;

}

void GLWorld::initializeObjects() {   /* this is overridden */    }

void GLWorld::raiseCore(void)
{
	changeZoom( int( core->getZoom( ) ) );
    updateGL();
}



void 
GLWorld::initializeMeshLayer(int layer)
{
    JahLayer* jah_layer = getLayerList()->at(layer)->thelayer;

    initializeMeshLayer(jah_layer);
}

void 
GLWorld::initializeMeshLayer(JahLayer* layer)
{

    int                     i, j;
    GLfloat                 xx, yy;

    GLfloat                 x_unit_size;
    GLfloat                 y_unit_size;
    float                   image_width;
    float                   image_height;
    float					mesh_x;
    float					mesh_y;

    image_width = layer->getImageWidth();
    image_height = layer->getImageHeight();

    mesh_x  = (float)layer->JAHMESH_X_DIMENSION;
    mesh_y  = (float)layer->JAHMESH_Y_DIMENSION;

    x_unit_size = image_width  / mesh_x;
    y_unit_size = image_height / mesh_y;

    for (i = 0; i <= mesh_x; i++)
    {
        xx = (float)(i - layer->JAHMESH_X_DIMENSION_DIV_2) * x_unit_size;

        for (j = 0; j <= (mesh_y); j++)
        {
            yy = (float)(j - layer->JAHMESH_Y_DIMENSION_DIV_2) * y_unit_size;

            layer->setMeshCoord(i, j, xx, yy, 0.0);
            layer->setMeshNormal(i, j, 0.0f, 0.0f, 1.0f);
        }
    }
}

void
GLWorld::drawAllLayers(int number_of_base_layers, bool draw_only_to_selected_effect)
{
    // Sort the layers by Z value using a map
    std::map<int, LayerListEntry*> layer_map;

    for ( int i = number_of_base_layers; i < int( getLayerList()->count() ); ++i )
    {
        JahLayer* jah_layer = getJahLayer(i);
        if ( jah_layer->objtype == ObjectCategory::EFFECT)
        {
            continue;
        }

        int depth = ( int )jah_layer->layernodes->m_node->tz;
        bool collision;

        // We need to do this to keep the map from clobbering layers at the same Z value
        do 
        {
            collision = false;

            if ( layer_map.find(depth) != layer_map.end() )
            {
                depth++;
                collision = true;
            }

        } while (collision);

        layer_map[depth] = getLayerList()->at(i);
    }

    std::map<int, LayerListEntry*>::iterator layer_map_iterator = layer_map.begin();

    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);

    int left_spacing = (core->m_renderwidth - X_RESOLUTION) / 2;
    int bottom_spacing = (core->m_renderheight - Y_RESOLUTION) / 2;

    static bool composite_texture_initialized = false;

    if (!composite_texture_initialized)
    {
        int composite_texture_width;
        int composite_texture_height;

        getPowerOfTwoTextureSize(composite_texture_width, composite_texture_height, 
            X_RESOLUTION, Y_RESOLUTION);

        createEmpty2DTexture(getCompositeTextureIdPtr(), GL_RGBA, 
            composite_texture_width, composite_texture_height);

        getCompositeSizeRatioPtr()->x = float(X_RESOLUTION) / float(composite_texture_width);
        getCompositeSizeRatioPtr()->y = float(Y_RESOLUTION) / float(composite_texture_height);

        composite_texture_initialized = true;
    }

    for (; layer_map_iterator != layer_map.end(); layer_map_iterator++)
    {
        int layer_number = getLayerList()->find(layer_map_iterator->second);
        JahLayer* image_layer = getJahLayer(layer_number);

        if ( isLayerVisible(layer_number) && !image_layer->foreground)     
        {
            if ( canAcceptEffects(image_layer->objtype) )
            {
                initializeMeshLayer(layer_number);

                if ( image_layer->getFirstPass() )
                {
                    image_layer->saveMeshCoords();
                    image_layer->saveMeshNormals();
                    image_layer->setFirstPass(false);
                }
            }

            //effects buffering is now calculated here
            if ( canAcceptEffects( image_layer->objtype ) )
            {
                if (   image_layer->getEffectsSlidersHaveChanged() 
                    || getAnimationUpdated() && image_layer->getEffectsHaveKeyFrames()
                   )
                {
                    buildEffects(image_layer, draw_only_to_selected_effect);
                    image_layer->saveMeshCoords();
                    image_layer->saveMeshNormals();
                }
                else
                {
                    image_layer->restoreMeshCoords();
                    image_layer->restoreMeshNormals();
                }
            }

            bool set_position_flag = true;

            if ( image_layer->isChromaKeyLayer() )
            {
                set_position_flag = false;
                // Save the composite texture
                glBindTexture( GL_TEXTURE_2D, getCompositeTextureId() );
                glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, left_spacing,
                    bottom_spacing, X_RESOLUTION, Y_RESOLUTION);
            }

            renderSpace->drawLayer(image_layer, set_position_flag);

            //then draw the keyframes if visible
            if (image_layer->drawKeyframes)
            {
                renderSpace->drawNodes();
            }
        }

    }

    //renderSpace->makeLayerOutline();

    //glPopMatrix();

    /////////////////////////////////////////////////////////////////////////////
    //draw the foreground layers ie objects not affected by worldspace or sorting

    layer_map_iterator = layer_map.begin();

    for (; layer_map_iterator != layer_map.end(); layer_map_iterator++)
    {
        int layer_number = getLayerList()->find(layer_map_iterator->second);
        JahLayer* image_layer = getJahLayer(layer_number);

        glPushMatrix();

        if (image_layer) 
        {
            if (isLayerVisible(layer_number) && image_layer->foreground) 
            {
                bool depth_save = image_layer->depth;

                if ( layer_map.size() == 1 )
                {
                    // Not sure why this needs to be done
                    // Without it the layer can go behind the world layer
                    // FIXME
                    renderSpace->drawLayer(image_layer, true);
                }

                image_layer->depth = false;
                renderSpace->drawLayer(image_layer, true);

                if(image_layer->drawKeyframes) 
                { 
                    renderSpace->drawNodes(); 
                }

                image_layer->depth = depth_save;
            }
        }

        image_layer->setEffectsSlidersHaveChanged(false);

        glPopMatrix();
    }

    setAnimationUpdated(false);
}

