//
//  SuperTuxKart - a fun racing game with go-kart
//  Copyright (C) 2009-2015 Joerg Henrichs
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 3
//  of the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

#include "irr_driver.h"

#include "../config/user_config.h"
//#include "callbacks.h"
#include "central_settings.h"
#include "glwrap.h"
#include "ls_scene_manager.h"
#include "ls_mesh_scene_node.h"
#include "../guiengine/engine.h"
//#include "graphics/graphics_restrictions.hpp"
//#include "graphics/lod_node.hpp"
//#include "graphics/post_processing.hpp"
//#include "graphics/referee.hpp"
//#include "graphics/rtts.hpp"
#include "screen_quad.h"
#include "shaders.h"
#include "camera.h"
#include "../modes/world.h"





#define MAX2(a, b) ((a) > (b) ? (a) : (b))
#define MIN2(a, b) ((a) > (b) ? (b) : (a))



void IrrDriver::renderGLSL(float dt)
{
    //Log::info("irrDriver", "render GLSL");

    //m_post_processing->begin();
/*
    //RaceGUIBase *rg = world->getRaceGUI();
    //if (rg) rg->update(dt);

    */

    for(unsigned int cam = 0; cam < Camera::getNumCameras(); cam++)
    {
        //Log::info("irrDriver", "a cam found");

        Camera * const camera = Camera::getCamera(cam);


        scene::ICameraSceneNode * const camnode = camera->getCameraSceneNode();


        //PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), (cam+1)*60,
        //                         0x00, 0x00);
        camera->activate(true);
        Log::info("irrDriver/render", "activate");
        //camera->applyMouseMovement(2, 1);






        m_scene_manager->setActiveCamera(camnode);

        const core::recti &viewport = camera->getViewport();

        //if (World::getWorld() && World::getWorld()->getTrack()->hasShadows() && !SphericalHarmonicsTextures.empty())
        //    irr_driver->getSceneManager()->setAmbientLight(SColor(0, 0, 0, 0));

        // TODO: put this outside of the rendering loop



        //computeMatrixesAndCameras(camnode, viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y);
        //uploadLightingData();

        renderScene(camnode, 0, dt, false, false);

        // Render bounding boxes


        // Save projection-view matrix for the next frame
        //camera->setPreviousPVMatrix(m_ProjViewMatrix);


    }   // for i<world->getNumKarts()


    // Use full screen size
    float tmp[2];
    tmp[0] = float(m_actual_screen_size.Width);
    tmp[1] = float(m_actual_screen_size.Height);
    //glBindBuffer(GL_UNIFORM_BUFFER, SharedObject::ViewProjectionMatrixesUBO);
    //glBufferSubData(GL_UNIFORM_BUFFER, (16 * 9) * sizeof(float), 2 * sizeof(float), tmp);

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glUseProgram(0);

    // Set the viewport back to the full screen for race gui
    m_video_driver->setViewPort(core::recti(0, 0,
        irr_driver->getActualScreenSize().Width,
        irr_driver->getActualScreenSize().Height));





    {
        //ScopedGPUTimer Timer(getGPUTimer(Q_GUI));
        //PROFILER_PUSH_CPU_MARKER("GUIEngine", 0x75, 0x75, 0x75);
        // Either render the gui, or the global elements of the race gui.
        GUIEngine::render(dt);
        //PROFILER_POP_CPU_MARKER();
    }

    // Render the profiler



#ifdef DEBUG
    drawDebugMeshes();
#endif


    //PROFILER_PUSH_CPU_MARKER("EndSccene", 0x45, 0x75, 0x45);
    m_video_driver->endScene();
    //PROFILER_POP_CPU_MARKER();

    //getPostProcessing()->update(dt);
}

void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned pointlightcount, float dt, bool hasShadow, bool forceRTT)
{
    Log::warn("irrDriver", "render Scene not working yet");

    //glBindBufferBase(GL_UNIFORM_BUFFER, 0, SharedObject::ViewProjectionMatrixesUBO);
    //glBindBufferBase(GL_UNIFORM_BUFFER, 1, SharedObject::LightingDataUBO);
    m_scene_manager->setActiveCamera(camnode);

    PrepareDrawCalls(camnode);
    /*

    PROFILER_PUSH_CPU_MARKER("- Draw Call Generation", 0xFF, 0xFF, 0xFF);

    PROFILER_POP_CPU_MARKER();
    // Shadows
    {
        // To avoid wrong culling, use the largest view possible
        m_scene_manager->setActiveCamera(m_suncam);
        if (CVS->isDefferedEnabled() &&
            CVS->isShadowEnabled() && hasShadow)
        {
            PROFILER_PUSH_CPU_MARKER("- Shadow", 0x30, 0x6F, 0x90);
            renderShadows();
            PROFILER_POP_CPU_MARKER();
            if (CVS->isGlobalIlluminationEnabled())
            {
                PROFILER_PUSH_CPU_MARKER("- RSM", 0xFF, 0x0, 0xFF);
                renderRSM();
                PROFILER_POP_CPU_MARKER();
            }
        }
        m_scene_manager->setActiveCamera(camnode);

    }
*/
    //PROFILER_PUSH_CPU_MARKER("- Solid Pass 1", 0xFF, 0x00, 0x00);
    glDepthMask(GL_TRUE);
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_BLEND);
    glEnable(GL_CULL_FACE);
    if (CVS->isDefferedEnabled() || forceRTT)
    {
        Log::info("render.cpp", "renderscene");
        //m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind();
        glClearColor(0., 0., 0., 0.);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

        renderSolidFirstPass(); //needed ???????
    }
    else
    {
       //not needed yet, because defferd always enabled (for tests)
    /*
        // We need a cleared depth buffer for some effect (eg particles depth blending)

        // Bind() modifies the viewport. In order not to affect anything else,
        // the viewport is just reset here and not removed in Bind().
        const core::recti &vp = Camera::getActiveCamera()->getViewport();
        glViewport(vp.UpperLeftCorner.X,
                   irr_driver->getActualScreenSize().Height - vp.LowerRightCorner.Y,
                   vp.LowerRightCorner.X - vp.UpperLeftCorner.X,
                   vp.LowerRightCorner.Y - vp.UpperLeftCorner.Y);
        glClear(GL_DEPTH_BUFFER_BIT);
        if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FRAMEBUFFER_SRGB_WORKING))
            glEnable(GL_FRAMEBUFFER_SRGB);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        */
    }
    //PROFILER_POP_CPU_MARKER();




    //PROFILER_PUSH_CPU_MARKER("- Solid Pass 2", 0x00, 0x00, 0xFF);
    if (CVS->isDefferedEnabled() || forceRTT)
    {
        //m_rtts->getFBO(FBO_COLORS).Bind();
        SColor clearColor(0, 150, 150, 150);
        if (World::getWorld() != NULL)
            clearColor = World::getWorld()->getClearColor();

        glClearColor(clearColor.getRed() / 255.f, clearColor.getGreen() / 255.f,
            clearColor.getBlue() / 255.f, clearColor.getAlpha() / 255.f);
        glClear(GL_COLOR_BUFFER_BIT);
        glDepthMask(GL_FALSE);
    }

    renderSolidSecondPass(); //needed ?????
    //PROFILER_POP_CPU_MARKER();
    /*
    if (getNormals())
    {
        m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind();
        renderNormalsVisualisation();
        m_rtts->getFBO(FBO_COLORS).Bind();
    }
    */



    /*
    // Render transparent
    {
        PROFILER_PUSH_CPU_MARKER("- Transparent Pass", 0xFF, 0x00, 0x00);
        ScopedGPUTimer Timer(getGPUTimer(Q_TRANSPARENT));
        renderTransparent();
        PROFILER_POP_CPU_MARKER();
    }
    */



    // Ensure that no object will be drawn after that by using invalid pass
    irr_driver->setPhase(PASS_COUNT);

}

// --------------------------------------------

void IrrDriver::renderFixed(float dt)
{

    World *world = World::getWorld(); // Never NULL.


    m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true,
                                   video::SColor(55,0,201,240));
    Log::info("IrrDriver::renderFixed", "begin scene");
    m_scene_manager->drawAll();
    Log::info("IrrDriver::renderFixed", "draw all 1");

    irr_driver->getVideoDriver()->enableMaterial2D();

    //RaceGUIBase *rg = world->getRaceGUI();
    //if (rg) rg->update(dt);


    for(unsigned int i=0; i<Camera::getNumCameras(); i++)
    {
        Camera *camera = Camera::getCamera(i);


        camera->activate();
        //rg->preRenderCallback(camera);

        m_renderpass = ~0;
        m_scene_manager->drawAll();

        Log::info("IrrDriver::renderFixed", "draw all 2");



    }   // for i<world->getNumKarts()

    // Set the viewport back to the full screen for race gui
    m_video_driver->setViewPort(core::recti(0, 0,
                                            UserConfigParams::m_width,
                                            UserConfigParams::m_height));

    GUIEngine::render(dt);




    m_video_driver->endScene();
    Log::info("IrrDriver::renderFixed", "end scene");
}


