//##################################################################################################
//
//   Custom Visualization Core library
//   Copyright (C) 2011-2012 Ceetron AS
//    
//   This library 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 library 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 at <<http://www.gnu.org/licenses/gpl.html>> 
//   for more details. 
//
//##################################################################################################

#include "cvfBase.h"
#include "cvfRenderStateTracker.h"
#include "cvfRenderStateSet.h"
#include "cvfRenderStateBlending.h"
#include "cvfRenderStateColorMask.h"
#include "cvfRenderStateCullFace.h"
#include "cvfRenderStateDepth.h"
#include "cvfRenderStateFrontFace.h"
#include "cvfRenderStateLine.h"
#include "cvfRenderStatePoint.h"
#include "cvfRenderStatePolygonMode.h"
#include "cvfRenderStatePolygonOffset.h"
#include "cvfRenderStateStencil.h"
#include "cvfRenderStateTextureBindings.h"
#include "cvfOpenGLContext.h"
#include "cvfOpenGLCapabilities.h"

#ifndef CVF_OPENGL_ES
#include "cvfRenderState_FF.h"
#endif

#include <memory.h>

namespace cvf {



//==================================================================================================
///
/// \class cvf::RenderStateTracker
/// \ingroup Render
///
/// 
/// 
//==================================================================================================

//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
RenderStateTracker::RenderStateTracker()
{
    memset(m_renderStateUsageTable, 0, sizeof(m_renderStateUsageTable));
    memset(m_currentRenderStates, 0, sizeof(m_currentRenderStates));

    setupDefaultRenderStates();
}


//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
void RenderStateTracker::setupDefaultRenderStates()
{
    m_defaultRenderStates[RenderState::BLENDING]            = new RenderStateBlending;
    m_defaultRenderStates[RenderState::COLOR_MASK]          = new RenderStateColorMask;
    m_defaultRenderStates[RenderState::CULL_FACE]           = new RenderStateCullFace(false);
    m_defaultRenderStates[RenderState::DEPTH]               = new RenderStateDepth;
    m_defaultRenderStates[RenderState::FRONT_FACE]          = new RenderStateFrontFace;
    m_defaultRenderStates[RenderState::LINE]                = new RenderStateLine;
    m_defaultRenderStates[RenderState::POINT]               = new RenderStatePoint;
    m_defaultRenderStates[RenderState::POLYGON_MODE]        = new RenderStatePolygonMode;
    m_defaultRenderStates[RenderState::POLYGON_OFFSET]      = new RenderStatePolygonOffset;
    m_defaultRenderStates[RenderState::STENCIL]             = new RenderStateStencil;
    m_defaultRenderStates[RenderState::TEXTURE_BINDINGS]    = new RenderStateTextureBindings;

#ifndef CVF_OPENGL_ES
    m_defaultRenderStates[RenderState::LIGHTING_FF]         = new RenderStateLighting_FF;
    m_defaultRenderStates[RenderState::MATERIAL_FF]         = new RenderStateMaterial_FF;
    m_defaultRenderStates[RenderState::NORMALIZE_FF]        = new RenderStateNormalize_FF(false);
    m_defaultRenderStates[RenderState::TEXTURE_MAPPING_FF]  = new RenderStateTextureMapping_FF;
#endif
}


//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
void RenderStateTracker::resetAndApplyDefaultRenderStates(OpenGLContext* oglContext)
{
    memset(m_renderStateUsageTable, 0, sizeof(m_renderStateUsageTable));
    memset(m_currentRenderStates, 0, sizeof(m_currentRenderStates));

    bool contextSupportsFixedFunction = oglContext->capabilities()->supportsFixedFunction();

    int numRenderStates = RenderState::COUNT;
    int i;
    for (i = 0; i < numRenderStates; i++)
    {
        RenderState* rs = m_defaultRenderStates[i].p();
        CVF_ASSERT(rs);
        if (rs->isFixedFunction())
        {
            // Apply fixed function render states ONLY if the OpenGL context supports it 
            if (contextSupportsFixedFunction)
            {
                rs->applyOpenGL(oglContext);
            }
        }
        else
        {
            rs->applyOpenGL(oglContext);
        }
    }
}


//--------------------------------------------------------------------------------------------------
/// Apply states in RenderStateSet
/// 
/// \param oglContext  The current OpenGLContext
/// \param newRSS      The new RenderStateSet that is to be applied
/// \param prevRSS     The previous RenderStateSet
//--------------------------------------------------------------------------------------------------
void RenderStateTracker::applyRenderStates(OpenGLContext* oglContext, const RenderStateSet* newRSS, const RenderStateSet* prevRSS)
{
    const int IS_DEFAULT  = 0;
    const int IN_USE      = 1;
    const int WILL_BE_SET = 2;

    // If there is no previous state set, assume all render states are at defaults
    if (!prevRSS)
    {
        memset(m_renderStateUsageTable, IS_DEFAULT, sizeof(m_renderStateUsageTable));
        memset(m_currentRenderStates, 0, sizeof(m_currentRenderStates));
    }


    uint numNewStates = newRSS ? newRSS->count() : 0;

    // Modify the usage flag for all the states that are present in the new state set.
    // These will be set later so there is no need to set them back to the defaults
    uint i;
    for (i = 0; i < numNewStates; i++)
    {
        m_renderStateUsageTable[newRSS->renderState(i)->type()] = WILL_BE_SET;
    }


    // Iterate over the previous state set
    // Apply the corresponding default state only if it won't be overwritten when we apply the new states later
    if (prevRSS)
    {
        uint numPrevStates = prevRSS->count();
        for (i = 0; i < numPrevStates; i++)
        {
            RenderState::Type stateType = prevRSS->renderState(i)->type();

            // Only need to apply the corresponding default if it is flagged as IN_USE
            // States with the WILL_BE_SET usage are handled in the final loop below
            if (m_renderStateUsageTable[stateType] == IN_USE)
            {
                const RenderState* defaultState = m_defaultRenderStates[stateType].p();
                
                defaultState->applyOpenGL(oglContext);
                m_currentRenderStates[stateType] = defaultState;
                m_renderStateUsageTable[stateType] = IS_DEFAULT;
            }
        }
    }


    // Iterate over the incoming new render states
    // Call apply function only if the new state is different from the current one 
    for (i = 0; i < numNewStates; i++)
    {
        const RenderState* newState = newRSS->renderState(i);
        CVF_ASSERT(newState);
        RenderState::Type stateType = newState->type();

        if (m_currentRenderStates[stateType] != newState)
        {
            newState->applyOpenGL(oglContext);
            m_currentRenderStates[stateType] = newState;
        }

        m_renderStateUsageTable[stateType] = IN_USE;
    }
}



} // namespace cvf

