//--------------------------------------------------------------------------------------
// Copyright (C) 2008 http://shift.delblanco.es
//--------------------------------------------------------------------------------------
// SHIFT Videogame 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 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.
//--------------------------------------------------------------------------------------
// 3DLight.cpp
//--------------------------------------------------------------------------------------
#include "dxstdafx.h"
#include "3DLight.h"
#include "DXUT.h"

//--------------------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------------------
C3DLight::C3DLight()
{
    m_bInit = false;
    m_pDevice = NULL;

    m_bFallOff = false;
    m_bFallIn = false;
    m_bFallAtomic = false;
    m_bModified = false;
}

//--------------------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------------------
C3DLight::~C3DLight()
{
    Release();
}

//--------------------------------------------------------------------------------------
// Carga la luz ante un CreateDevice
//--------------------------------------------------------------------------------------
bool C3DLight::initialize()
{
    // Salimos si ya se ha inicializado
    if(m_bInit) return false;
    m_bInit = true;

    // Tomamos el dispositivo
    m_pDevice = DXUTGetD3DDevice();
    assert(m_pDevice != NULL);
    m_pDevice->AddRef();

    // Inicializamos los parametros basicos por defecto
    ZeroMemory( &m_Light, sizeof(m_Light) );
    setType(D3DLIGHT_DIRECTIONAL);
    setDirection(D3DXVECTOR3(0.0f, -1.0f, 0.0f));
    setDiffuse(1.0f, 1.0f, 1.0f, 1.0f);
    return true ;
}

//--------------------------------------------------------------------------------------
// Actualiza la luz ante un FrameMove
//--------------------------------------------------------------------------------------
void C3DLight::update()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

    // Calculamos si estamos apagando las luces lentamente
    if(m_bFallOff)
    {
        float Off = DXUTGetElapsedTime() * m_FallSpd;
        if(m_Light.Diffuse.a > 0)  {m_Light.Diffuse.a -= Off; m_bModified = true;}
        if(m_Light.Diffuse.b > 0)  {m_Light.Diffuse.b -= Off; m_bModified = true;}
        if(m_Light.Diffuse.g > 0)  {m_Light.Diffuse.g -= Off; m_bModified = true;}
        if(m_Light.Diffuse.r > 0)  {m_Light.Diffuse.r -= Off; m_bModified = true;}

        if(m_Light.Specular.a > 0) {m_Light.Specular.a -= Off; m_bModified = true;}
        if(m_Light.Specular.b > 0) {m_Light.Specular.b -= Off; m_bModified = true;}
        if(m_Light.Specular.g > 0) {m_Light.Specular.g -= Off; m_bModified = true;}
        if(m_Light.Specular.r > 0) {m_Light.Specular.r -= Off; m_bModified = true;}

        if(m_Light.Ambient.a > 0)  {m_Light.Ambient.a -= Off; m_bModified = true;}
        if(m_Light.Ambient.b > 0)  {m_Light.Ambient.b -= Off; m_bModified = true;}
        if(m_Light.Ambient.g > 0)  {m_Light.Ambient.g -= Off; m_bModified = true;}
        if(m_Light.Ambient.r > 0)  {m_Light.Ambient.r -= Off; m_bModified = true;}

        if(!m_bModified)
        {
            m_bFallOff = false;
            m_bFallAtomic = false;
        }
    }

    // Calculamos si estamos encendiendo las luces lentamente
    else if(m_bFallIn)
    {
        float In = DXUTGetElapsedTime() * m_FallSpd;
        if(m_Light.Diffuse.a < m_oldDiffuse.a)   {m_Light.Diffuse.a += In; m_bModified = true;}
        if(m_Light.Diffuse.b < m_oldDiffuse.b)   {m_Light.Diffuse.b += In; m_bModified = true;}
        if(m_Light.Diffuse.g < m_oldDiffuse.g)   {m_Light.Diffuse.g += In; m_bModified = true;}
        if(m_Light.Diffuse.r < m_oldDiffuse.r)   {m_Light.Diffuse.r += In; m_bModified = true;}

        if(m_Light.Specular.a < m_oldSpecular.a) {m_Light.Specular.a += In; m_bModified = true;}
        if(m_Light.Specular.b < m_oldSpecular.b) {m_Light.Specular.b += In; m_bModified = true;}
        if(m_Light.Specular.g < m_oldSpecular.g) {m_Light.Specular.g += In; m_bModified = true;}
        if(m_Light.Specular.r < m_oldSpecular.r) {m_Light.Specular.r += In; m_bModified = true;}

        if(m_Light.Ambient.a < m_oldAmbient.a)   {m_Light.Ambient.a += In; m_bModified = true;}
        if(m_Light.Ambient.b < m_oldAmbient.b)   {m_Light.Ambient.b += In; m_bModified = true;}
        if(m_Light.Ambient.g < m_oldAmbient.g)   {m_Light.Ambient.g += In; m_bModified = true;}
        if(m_Light.Ambient.r < m_oldAmbient.r)   {m_Light.Ambient.r += In; m_bModified = true;}

        if(!m_bModified)
        {
            m_bFallIn = false;
            m_bFallAtomic = false;
            m_Light.Diffuse = m_oldDiffuse;
            m_Light.Specular = m_oldSpecular;
            m_Light.Ambient = m_oldAmbient;
        }
    }

    // Recalculamos las matrices si hubo modificaciones que influyan en ellas
    if(m_bModified) restore();
}

//--------------------------------------------------------------------------------------
// Restaura la luz ante un ResetDevice
//--------------------------------------------------------------------------------------
void C3DLight::restore()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

    m_pDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
    m_pDevice->SetLight(0, &m_Light);
    m_pDevice->LightEnable(0, true);

    // Desactivamos el flag, puesto que ya se recalcularon las modificaciones
    m_bModified = false;
}

//--------------------------------------------------------------------------------------
// Libera la luz ante un DestroyDevice
//--------------------------------------------------------------------------------------
void C3DLight::Release()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;
    m_bInit = false;

    // Liberamos el dispositivo
    SAFE_RELEASE(m_pDevice);
}

//--------------------------------------------------------------------------------------
// Para apagar las luces lentamente
//--------------------------------------------------------------------------------------
void C3DLight::FallOff(float Spd, bool Atomic)
{
    // Si queremos apagarla de golpe
    if(Spd < 0.0f)
    {
        m_bFallOff = false;
        m_oldDiffuse = m_Light.Diffuse;
        m_oldSpecular = m_Light.Specular;
        m_oldAmbient = m_Light.Ambient;
        D3DCOLORVALUE final;
        final.a = 0.0f;
        final.r = 0.0f;
        final.g = 0.0f;
        final.b = 0.0f;
        m_Light.Diffuse = final;
        m_Light.Specular = final;
        m_Light.Ambient = final;
        return;
    }

    // Si ya está en ello o hay otro falloff atómico salimos
    if(m_bFallOff || (m_bFallAtomic && !Atomic)) return;

    // Tomamos los datos y comenzamos el proceso
    m_FallSpd = Spd;
    m_bFallAtomic = Atomic;
    m_bFallIn = false;
    m_bFallOff = true;
}

//--------------------------------------------------------------------------------------
// Para encender las luces lentamente
//--------------------------------------------------------------------------------------
void C3DLight::FallIn(float Spd, bool Atomic)
{
    // Si queremos encenderla de golpe
    if(Spd < 0.0f)
    {
        m_bFallIn = false;
        m_Light.Diffuse = m_oldDiffuse;
        m_Light.Specular = m_oldSpecular;
        m_Light.Ambient = m_oldAmbient;
        return;
    }

    // Si ya está en ello o hay otro falloff atómico salimos salimos
    if(m_bFallIn || (m_bFallAtomic && !Atomic)) return;

    // Tomamos los datos y comenzamos el proceso
    m_FallSpd = Spd;
    m_bFallAtomic = Atomic;
    m_bFallOff = false;
    m_bFallIn = true;
}
