//--------------------------------------------------------------------------------------
// 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.
//--------------------------------------------------------------------------------------
// Menu.cpp
//--------------------------------------------------------------------------------------
#include "dxstdafx.h"
#include "Menu.h"

#include "3DCamera.h"

//--------------------------------------------------------------------------------------
// Espacio de nombres
//--------------------------------------------------------------------------------------
using namespace std;

//--------------------------------------------------------------------------------------
// Variables Globales
//--------------------------------------------------------------------------------------
CMenu                   *g_Menu;           // Instancia para el menú 3D
extern C3DCamera        *g_Camera;         // Instancia externa para la cámara 3D (3DCamera.cpp)
extern bool             g_bInGame;         // Para saber si estamos durante el juego (Main.cpp)
extern bool             g_bInMenu;         // Para saber si estamos durante el menú (Main.cpp)

//--------------------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------------------
CMenu::CMenu(void)
{
    // Inicializamos punteros
    m_pLight   = NULL;

    m_pScene   = NULL;
    m_pLogo    = NULL;
    m_pArena   = NULL;
    m_pVehicle = NULL;
    m_pGame    = NULL;

    // Inicializamos variables
    m_bInit = false;
    m_bLightIn = true;
    m_fTotalTime = 0;
    m_fElapsedTime = 0;
    m_iTipo = 0;
    m_iEstadoAnim = 0;
}

//--------------------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------------------
CMenu::~CMenu(void)
{
    Release();
}

//--------------------------------------------------------------------------------------
// Carga el menú ante un CreateDevice
//--------------------------------------------------------------------------------------
bool CMenu::initialize()
{
    // Salimos si ya se ha inicializado
    if(m_bInit) return false;
    m_bInit = true;

    // Inicializamos el modo del menú inicial
    return setTypeMenu(MENU_TYPE_MENU);
}

//--------------------------------------------------------------------------------------
// Renderiza el menú ante un FrameRender
//--------------------------------------------------------------------------------------
void CMenu::render()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

    // Renderizamos la luz
    m_pLight->render();

    // Renderizamos los objetos dependiendo del tipo de menú
    switch(m_iTipo)
    {
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_MENU:
        m_pScene->render();
        m_pLogo->render();
        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_SELECT_ARENA:
        m_pScene->render();
        m_pArena->render();
        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_SELECT_VEHICLE:
        m_pScene->render();
        m_pVehicle->render();
        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_PREGAME:
    case MENU_TYPE_GAME:
    case MENU_TYPE_PAUSE:
        m_pGame->render();
        break;
    }

}

//--------------------------------------------------------------------------------------
// Actualiza el menú ante un FrameMove
//--------------------------------------------------------------------------------------
void CMenu::update()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

    // Actualizamos los temporizadores
    m_fElapsedTime = DXUTGetElapsedTime();
    m_fTotalTime += m_fElapsedTime;
    if(m_fTotalTime > MENU_ANIM_SEG) m_fTotalTime = 0;

    // Actualizamos la luz
    m_pLight->update();

    // Actualizamos los objetos dependiendo del tipo de menú
    switch(m_iTipo)
    {
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_MENU:
		// Actualizamos la escena
        m_pScene->update();
        m_pLogo->update();

        // Si estabamos a oscuras situamos la cámara y encendemos suvemente las luces
        if(!m_bLightIn && m_fTotalTime > MENU_INI_CAMERA)
        {
            // Tendremos una cámara libre
            g_Camera->setType(CAMERA3D_FREE);
            m_fTotalTime = MENU_ANIM_SEG;
            m_pLight->FallIn(0.5f, true);
            m_bLightIn = true;
        }
        // Animamos la cámara en el modo demo
        if(m_bLightIn) AnimDemo();
        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_SELECT_VEHICLE:
		// Actualizamos la escena
        m_pScene->update();
        m_pVehicle->update();

        // Si estabamos a oscuras situamos la cámara y encendemos suvemente las luces
        if(!m_bLightIn && m_fTotalTime > MENU_INI_CAMERA)
        {
            // Tendremos una cámara libre
            g_Camera->setType(CAMERA3D_FREE);
            g_Camera->setPosition(D3DXVECTOR3(-12.0f, 50.0f, -60.0f));
            g_Camera->setRotation(D3DXVECTOR3(0.7f, 0.0f, 0.0f));
            m_pLight->FallIn(1.5f, true);
            m_bLightIn = true;
        }
        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_SELECT_ARENA:
		// Actualizamos la escena
        m_pScene->update();
        m_pArena->update();

        // Si estabamos a oscuras situamos la cámara y encendemos suvemente las luces
        if(!m_bLightIn && m_fTotalTime > MENU_INI_CAMERA)
        {
            // Tendremos una cámara libre
            g_Camera->setType(CAMERA3D_FREE);
            g_Camera->setPosition(D3DXVECTOR3(-12.0f, 50.0f, -60.0f));
            g_Camera->setRotation(D3DXVECTOR3(0.7f, 0.0f, 0.0f));
            m_pLight->FallIn(1.5f, true);
            m_bLightIn = true;
        }
        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_PREGAME:
        // Si estabamos a oscuras situamos la cámara y encendemos suvemente las luces
        if(!m_bLightIn && m_fTotalTime > MENU_INI_CAMERA)
        {
            // Para este menú pre-juego tenemos una cámara desde un punto fijo
            g_Camera->setType(CAMERA3D_GAME_6);
			g_Camera->setPosition(D3DXVECTOR3(120.0f, 200.0f, 120.0f));
			g_Camera->setTo(D3DXVECTOR3(0.0f, 0.0f, 0.0f));

			// Para el juego tendremos una cámara persecutoria
            g_Camera->setLastType(CAMERA3D_GAME_4);

            m_pLight->FallIn(1.5f, true);
            m_bLightIn = true;
        }
        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_GAME:
		// Actualizamos la escena
        m_pGame->update();

		// Si estabamos a oscuras situamos la cámara y encendemos suvemente las luces
		if(!m_bLightIn && m_fTotalTime > MENU_INI_CAMERA)
		{
            // Recuperamos la última cámara del jugador
			g_Camera->setType(g_Camera->getLastType());
            m_pLight->FallIn(1.5f, true);
			m_bLightIn = true;
		}

		// Ponemos la cámara de tipo persecutorio sobre la posición del vehículo del player local
        g_Camera->Follow(m_pGame->getLocalPlayer()->getVehicle());
        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_PAUSE:
        // Si estabamos a oscuras situamos la cámara y encendemos suvemente las luces
        if(!m_bLightIn && m_fTotalTime > MENU_INI_CAMERA)
        {
            // Tendremos una cámara desde punto fijo
            g_Camera->setType(CAMERA3D_GAME_6);
			g_Camera->setPosition(D3DXVECTOR3(120.0f, 200.0f, 120.0f));
			g_Camera->setTo(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
            m_pLight->FallIn(1.5f, true);
            m_bLightIn = true;
        }
        break;
    }

}

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

    // Restauramos la luz
    m_pLight->restore();
}

//--------------------------------------------------------------------------------------
// Libera objetos ante un LostDevice
//--------------------------------------------------------------------------------------
void CMenu::free()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

    // Liberamos la luz
    m_pLight->free();
}

//--------------------------------------------------------------------------------------
// Libera el menú ante un DestroyDevice
//--------------------------------------------------------------------------------------
void CMenu::Release()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;
    m_bInit = false;

    // Liberamos la luz
    SAFE_DELETE(m_pLight);

    // Liberamos los objetos
    SAFE_DELETE(m_pScene);
    SAFE_DELETE(m_pLogo);
    SAFE_DELETE(m_pVehicle);
    SAFE_DELETE(m_pArena);
    SAFE_DELETE(m_pGame);
}

//--------------------------------------------------------------------------------------
// Para comenzar el juego
//--------------------------------------------------------------------------------------
bool CMenu::setTypeMenu(int dTipo, LPCWSTR dStr, int dTipoGame)
{
    // Creamos la luz, si no estaba ya creada
    if(!m_pLight)
    {
        m_pLight = new C3DLight();
        if(!m_pLight) return false;
        if(!m_pLight->initialize()) return false;
        m_pLight->setType(D3DLIGHT_DIRECTIONAL);
        m_pLight->setDirection(D3DXVECTOR3(0.0f, 0.5f, 1.0f));
        m_pLight->setDiffuse(0.4f, 0.4f, 0.4f, 0.4f);
        m_pLight->setAmbient(0.6f, 0.6f, 0.6f, 0.6f);

        // Partimos con la luz apagada
        m_pLight->FallOff(-1.0f);
        m_bLightIn = false;
    }
    else
    {
        // Si la luz estaba creada, la apagamos suavemente
        m_pLight->FallOff(1.5f, true);
        m_bLightIn = false;
    }

    // Creamos los objetos dependiendo del tipo de menú
	g_bInGame = false;
    switch(dTipo)
    {
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_MENU:
        if(m_pScene)   m_pScene->setEnabled(true);
        if(m_pLogo)    m_pLogo->setEnabled(true);

        // Liberamos el juego, en el caso que acabe de finalizar
        SAFE_DELETE(m_pGame);

        // Creamos el entorno
        if(!m_pScene)
        {
			g_bInMenu = true;
            m_pScene = new C3DObjArena;
            if(!m_pScene) return false;
            if(!m_pScene->initialize(NULL, L".\\models\\objects\\menu.xml")) return false;
			g_bInMenu = false;
        }
        // Creamos el logo
        if(!m_pLogo)
        {
            m_pLogo = new C3DObject;
            if(!m_pLogo) return false;
            if(!m_pLogo->initialize(m_pScene, L".\\models\\objects\\logo_shift.xml")) return false;
            m_pLogo->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, 1.0f, 0.0f));
        }
        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_SELECT_ARENA:
        if(m_pArena)   m_pArena->setEnabled(true);
        if(m_pVehicle) m_pVehicle->setEnabled(false);

        // En 'dStr' tenemos el nombre del fichero de la arena seleccionada
        // si se indicó un nuevo nombre de arena, la creamos
        if(dStr)
        {
            // Hallamos la ruta del fichero de la arena
            m_sDataGame.m_awArena = MENU_ARENAS_DIR + (basic_string<wchar_t>)dStr + MENU_MODELS_EXTENSION;

            // Cargamos la arena
            SAFE_DELETE(m_pArena);
            m_pArena = new C3DObjArena;
            if(!m_pArena) return false;
            if(!m_pArena->initialize(m_pScene, m_sDataGame.m_awArena.c_str())) return false;

            // Preparamos la arena
            m_pArena->setScale(D3DXVECTOR3(0.3f, 0.3f, 0.3f));
            m_pArena->getPhysics()->setAFriction(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
            m_pArena->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, 0.7f, 0.0f));
        }
        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_SELECT_VEHICLE:
        if(m_pArena)   m_pArena->setEnabled(false);
        if(m_pVehicle) m_pVehicle->setEnabled(true);

        // En 'dStr' tenemos el nombre del fichero del vehículo seleccionado
        // si se indicó un nuevo nombre de vehículo, lo creamos
        if(dStr)
        {
            // Hallamos la ruta del fichero del vehículo, liberando anteriores estructuras
            m_sDataGame.m_awLVehicle = MENU_VEHICLES_DIR + (basic_string<wchar_t>)dStr + MENU_MODELS_EXTENSION;

            // Cargamos el vehículo
            SAFE_DELETE(m_pVehicle);
            m_pVehicle = new C3DObjVehicle;
            if(!m_pVehicle) return false;
            if(!m_pVehicle->initialize(m_pScene, m_sDataGame.m_awLVehicle.c_str(), NULL)) return false;

            // Preparamos el vehículo
            m_pVehicle->setScale(D3DXVECTOR3(0.7f, 0.7f, 0.7f));
            m_pVehicle->setWheelDist(0.0f);
            m_pVehicle->getPhysics()->setAFriction(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
            m_pVehicle->getPhysics()->setASpeed(D3DXVECTOR3(0.0f, 0.7f, 0.0f));
        }
        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_PREGAME:
        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_GAME:
        if(m_pScene)   m_pScene->setEnabled(false);
        if(m_pLogo)    m_pLogo->setEnabled(false);
        if(m_pArena)   m_pArena->setEnabled(false);
        if(m_pVehicle) m_pVehicle->setEnabled(false);

        // Para que tenga más memoria se pueden descomentar estas liberaciones
        // de anteriores estructuras no necesarias durante el juego
        // SAFE_DELETE(m_pScene);
        // SAFE_DELETE(m_pLogo);
        SAFE_DELETE(m_pArena);
        SAFE_DELETE(m_pVehicle);

        // Creamos el juego si aún no estaba creado
		g_bInGame = true;
        if(dStr)
        {
			// Eliminamos el anterior jeugo
			SAFE_DELETE(m_pGame);

            // Completamos los campos de 'm_sDataGame' que aún quedan
            // El tipo de juego lo tenemos en 'dTipoGame'
            // La arena ya éstá indicada con anterioridad
            // El path del vehículo del jugador local ya está indicado con anterioridad
            // El nombre del jugador actual lo tenemos en 'dStr'
            m_sDataGame.m_iType = dTipoGame;
            m_sDataGame.m_awLName = dStr;

            // Inicializamos los vectores de los jugadores remotos  (IP + Nombre + Path del vehículo)
            m_sDataGame.m_aRIPs.clear();
            m_sDataGame.m_aRVehicles.clear();
            m_sDataGame.m_aRNames.clear();

            // Insertamos en los vectores los datos de los jugadores remotos (IP + Nombre + Path del vehículo)
            // TODO:

            // Creamos el juego, pasando la estructura de los datos del mismo
            m_pGame = new CGame;
            if(!m_pGame) return false;
            if(!m_pGame->initialize(&m_sDataGame)) return false;
        }

        break;
	//--------------------------------------------------------------------------------------
    case MENU_TYPE_PAUSE:
        break;
    }

    // Actualizamos variables
    m_iTipo = dTipo;

    // Inicializamos el contador global
    m_iEstadoAnim = 0;
    m_fTotalTime = MENU_ANIM_SEG;

    return true;
}

//--------------------------------------------------------------------------------------
// Para animar en el modo del menú
//--------------------------------------------------------------------------------------
void CMenu::AnimDemo(void)
{
    // Antes de cambiar de animación apagamos lentamente las luces
    if(m_fTotalTime > MENU_ANIM_SEG - MENU_INI_CAMERA)
        m_pLight->FallOff(1.5f);

    // Cada cierto tiempo cambiamos el modo de visualización
    if(m_fTotalTime == 0)
    {
        // Al cambiar de animación encendemos lentamente las luces
        m_pLight->FallIn(1.5f);

        int n_estado = rand()%11;
        if(n_estado == m_iEstadoAnim) m_iEstadoAnim = (m_iEstadoAnim+1) % 11;
        else                          m_iEstadoAnim = n_estado;

        switch(m_iEstadoAnim)
        {
        case 0:
            g_Camera->setPosition(D3DXVECTOR3(10.0f, 0.0f, -15.0f));
            g_Camera->setRotation(D3DXVECTOR3(0.0f, -0.2f, 0.0f));
            break;
        case 1:
            g_Camera->setPosition(D3DXVECTOR3(-50.0f, 50.0f, -50.0f));
            g_Camera->setRotation(D3DXVECTOR3(0.15f, 0.2f, 0.0f));
            break;
        case 2:
            g_Camera->setPosition(D3DXVECTOR3(-40.0f, 40.0f, -40.0f));
            g_Camera->setRotation(D3DXVECTOR3(0.5f, 0.2f, 0.0f));
            break;
        case 3:
            g_Camera->setPosition(D3DXVECTOR3(0.0f, 0.0f, -30.0f));
            g_Camera->setRotation(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
            break;
        case 4:
            g_Camera->setPosition(D3DXVECTOR3(0.0f, 1.0f, -20.0f));
            g_Camera->setRotation(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
            break;
        case 5:
            g_Camera->setPosition(D3DXVECTOR3(0.0f, -5.0f, -35.0f));
            g_Camera->setRotation(D3DXVECTOR3(-0.2f, 0.0f, 0.0f));
            break;
        case 6:
            g_Camera->setPosition(D3DXVECTOR3(0.0f, 20.0f, -20.0f));
            g_Camera->setRotation(D3DXVECTOR3(0.5f, 0.0f, 0.0f));
            break;
        case 7:
            g_Camera->setPosition(D3DXVECTOR3(8.0f, 2.0f, -20.0f));
            g_Camera->setRotation(D3DXVECTOR3(0.0f, 0.0f, -0.3f));
            break;
        case 8:
            g_Camera->setPosition(D3DXVECTOR3(0.0f, 0.0f, -35.0f));
            g_Camera->setRotation(D3DXVECTOR3(0.0f, 0.0f, 0.3f));
            break;
        case 9:
            g_Camera->setPosition(D3DXVECTOR3(0.0f, 0.0f, -90.0f));
            g_Camera->setRotation(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
            break;
        case 10:
            g_Camera->setPosition(D3DXVECTOR3(0.0f, 50.0f, 0.0f));
            g_Camera->setRotation(D3DXVECTOR3(D3DX_PI/2.0f, 0.0f, 0.0f));
            break;
        }
    }
    // Movemos la cámara alrededor de la escena
    switch(m_iEstadoAnim)
    {
    case 0: g_Camera->addRotation(D3DXVECTOR3(0.0f, -CAMERA3D_A_SPEED * m_fElapsedTime, 0.0f)); break;
    case 1: g_Camera->addRotation(D3DXVECTOR3(0.0f, (CAMERA3D_A_SPEED/2.0f) * m_fElapsedTime, 0.0f)); break;
    case 2: g_Camera->addRotation(D3DXVECTOR3(0.0f, CAMERA3D_A_SPEED * m_fElapsedTime, 0.0f)); break;
    case 3: g_Camera->addPosition(D3DXVECTOR3(0.0f, 0.0f, (CAMERA3D_SPEED/2.0f) * m_fElapsedTime)); break;
    case 4: g_Camera->addPosition(D3DXVECTOR3(0.0f, 0.0f, CAMERA3D_SPEED * m_fElapsedTime)); break;
    case 5:
        g_Camera->addRotation(D3DXVECTOR3(-(CAMERA3D_A_SPEED/2.0f) * m_fElapsedTime, 0.0f, 0.0f));
        g_Camera->addPosition(D3DXVECTOR3(0.0f, 0.0f, CAMERA3D_SPEED * m_fElapsedTime));
        break;
    case 6:
        g_Camera->addRotation(D3DXVECTOR3(-CAMERA3D_A_SPEED * m_fElapsedTime, 0.0f, 0.0f));
        g_Camera->addPosition(D3DXVECTOR3(0.0f, -CAMERA3D_SPEED * m_fElapsedTime, 0.0f));
        break;
    case 7: break;
    case 8: g_Camera->addPosition(D3DXVECTOR3(0.0f, 0.0f, CAMERA3D_SPEED * m_fElapsedTime)); break;
    case 9: g_Camera->addPosition(D3DXVECTOR3(0.0f, 0.0f, CAMERA3D_SPEED * m_fElapsedTime)); break;
    case 10: g_Camera->addPosition(D3DXVECTOR3(0.0f, CAMERA3D_SPEED * 2 * m_fElapsedTime, 0.0f)); break;
    }

}
