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

#include "Game.h"
#include "GUI.h"
#include "Settings.h"
#include "3DSoundMng.h"
#include "3DObjElement.h"

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

//--------------------------------------------------------------------------------------
// Variables Globales
//--------------------------------------------------------------------------------------
float					g_TimeGame;        // Tiempo de juego
extern CGUI             *g_GUI;            // Instancia externa para el GUI (GUI.cpp)
extern CSettings        *g_Settings;       // Instancia para las opciones (Settings.cpp)
extern C3DSoundMng      *g_3DSoundMng;     // Instancia externa para manejar sonidos 3D (3DSoundMng.cpp)

//--------------------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------------------
CGame::CGame()
{
    m_pArena = NULL;
    m_pLPlayer = NULL;

    m_pSndWin = NULL;
    m_pSndLose = NULL;
    m_pSndTime = NULL;

    m_iType = -1;
    m_bInit = false;
}

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

//--------------------------------------------------------------------------------------
// Inicializa el juego
//--------------------------------------------------------------------------------------
bool CGame::initialize(SDataGame *dData)
{
    // Salimos si ya está inicializado
    if(m_bInit) return false;
    m_bInit = true;

    // Tomamos el tipo de juego
    m_iType = dData->m_iType;

    // Tomamos la arena
    m_pArena = new C3DObjArena;
    if(!m_pArena) return false;
    if(!m_pArena->initialize(NULL, dData->m_awArena.c_str())) return false;

    // Tomamos el jugador local
    m_pLPlayer = new CPlayerLocal;
    if(!m_pLPlayer) return false;
    if(!m_pLPlayer->initialize(dData->m_awLName.c_str(), dData->m_awLVehicle.c_str(), m_pArena)) return false;

    // Tomamos los jugadores remotos y los insertamos en el vector
    vector<basic_string<wchar_t> >::iterator itrIPs;
    vector<basic_string<wchar_t> >::iterator itrVehicles;
    vector<basic_string<wchar_t> >::iterator itrNames;
    for ( itrIPs = dData->m_aRIPs.begin(),
          itrVehicles = dData->m_aRVehicles.begin(),
          itrNames = dData->m_aRNames.begin() ;
          itrIPs != dData->m_aRIPs.end() &&
          itrVehicles != dData->m_aRVehicles.end() &&
          itrNames != dData->m_aRNames.end();
          itrIPs++, itrVehicles++, itrNames++ )
    {
        CPlayerRemote *new_remote;
        new_remote = new CPlayerRemote;
        if(!new_remote) return false;
        if(!new_remote->initialize(itrIPs->c_str(), itrNames->c_str(), itrVehicles->c_str(), m_pArena)) return false;

        m_aRPlayers.push_back(new_remote);
    }

	// Inicializamos variables
	m_pTime = m_pArena->getTimeIni();
	m_pLastTime = 0.0f;
	m_pObjetives = 0.0f;
	m_pHealthSecond = 0.0f;
	m_pLastHealth = -1.0f;
	m_pLastRockets = -1.0f;
	m_pLastObjetives = -1.0f;

	// Comienza el juego en 1.0 segundos
	g_TimeGame = -1.0f;

	// Cargamos sonidos
	m_pSndWin = g_3DSoundMng->Create(L".\\sounds\\misc\\win1.wav", NULL);
	m_pSndLose = g_3DSoundMng->Create(L".\\sounds\\misc\\lose1.wav", NULL);
	m_pSndTime = g_3DSoundMng->Create(L".\\sounds\\misc\\time1.wav", NULL);

    return true;
}

//--------------------------------------------------------------------------------------
// Renderiza los objetos del juego
//--------------------------------------------------------------------------------------
void CGame::render()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

    // Renderizamos los jugadores remotos
    for (vector<CPlayerRemote*>::iterator itr = m_aRPlayers.begin() ; itr != m_aRPlayers.end() ; itr++)
        (*itr)->render();

    // Renderizamos el jugador local
    m_pLPlayer->render();

    // Renderizamos la arena
    m_pArena->render();
}

//--------------------------------------------------------------------------------------
// Actualiza el juego
//--------------------------------------------------------------------------------------
void CGame::update()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

	// Actualizamos los marcadores
	updateInfo();

	// Actualizamos los jugadores remotos
	for (vector<CPlayerRemote*>::iterator itr = m_aRPlayers.begin() ; itr != m_aRPlayers.end() ; itr++)
		(*itr)->update();

	// Actualizamos el jugador local
	m_pLPlayer->update();

    // Actualizamos la arena
    m_pArena->update();
}

//--------------------------------------------------------------------------------------
// Para actualizar varios marcadores del GUI durante el juego
//--------------------------------------------------------------------------------------
void CGame::updateInfo()
{
	TCHAR txt[MAX_STRING+1];

	// Salimos si hemos ganado o perdido
	if (m_pLastTime == -1.0f || m_pObjetives == -1.0f) return;

	// Actualizamos el tiempo de juego
	float ElapsedTime = DXUTGetElapsedTime();
	g_TimeGame += ElapsedTime;
	if (g_TimeGame > 0.0f)
		m_pTime -= ElapsedTime;

	// Actualizamos el marcador de tiempos antes de la partida
	if (g_TimeGame < 0.0f)
	{
		// Ponemos el texto
		if (g_TimeGame < -0.66f) wsprintf(txt, L"-");
		else if (g_TimeGame < -0.33f) wsprintf(txt, L"--");
		else wsprintf(txt, L"---");
		g_GUI->setText(txt, GUI_STATIC_TITULO_2);
		g_GUI->getGUI()->GetStatic(GUI_STATIC_INFO_3a)->SetTextColor(D3DCOLOR_ARGB(150, 220, 220, 255));
		g_GUI->setText(L"Starting game ...", GUI_STATIC_INFO_3a);
		g_GUI->setText(L"Please wait", GUI_STATIC_INFO_3b);
		g_GUI->setText(L"", GUI_STATIC_INFO_3c);
		g_GUI->setText(L"", GUI_STATIC_INFO_3d);
		// Salimos
		return;
	}

	// Comprobamos si se capturó un elemento objetivo
	if(m_pArena->getObjetive()->getState() == OBJELEMENT_PRECOLLISION)
	{
		// Cambiamos el estado del elemento
		m_pArena->getObjetive()->setState(OBJELEMENT_COLLISION);
		// Aumentamos el contador de objetivos
		m_pObjetives++;
		// Aumentamos el contador de tiempo
		m_pTime += m_pArena->getTimeObj();
		// Actualizamos el objetivo en el minimapa
		g_GUI->getGUI()->GetStatic(GUI_STATIC_MAP2)->SetLocation(-100, -100);
	}

	// Comprobamos si se capturó un elemento aleatorio
	if(m_pArena->getRand()->getState() == OBJELEMENT_PRECOLLISION)
	{
		// Cambiamos el estado del elemento
		m_pArena->getRand()->setState(OBJELEMENT_COLLISION);
		// Seleccionamos una acción aleatoria
		int seleccionado = rand() % 4;
		switch(seleccionado)
		{
		case 0: // Tiempo
			m_pTime += m_pArena->getTimeObj(); break;
		case 1: // Vida
			m_pLPlayer->setHealth(min(m_pLPlayer->getHealth() + 5.0f, m_pLPlayer->getVehicle()->getMaxHealth())); break;
		case 2: // Cohete
			m_pLPlayer->setRockets(min(m_pLPlayer->getRockets() + 5.0f, m_pLPlayer->getVehicle()->getMaxCapacity())); break;
		}
	}

	// Comprobamos si se capturó un elemento vida
	if(m_pArena->getHealth()->getState() == OBJELEMENT_PRECOLLISION)
	{
		// Cambiamos el estado del elemento
		m_pArena->getHealth()->setState(OBJELEMENT_COLLISION);
		// Aumentamos el contador de vida
		m_pLPlayer->setHealth(min(m_pLPlayer->getHealth() + 5.0f, m_pLPlayer->getVehicle()->getMaxHealth()));
	}

	// Comprobamos si se capturó un elemento cohete
	if(m_pArena->getRocket()->getState() == OBJELEMENT_PRECOLLISION)
	{
		// Cambiamos el estado del elemento
		m_pArena->getRocket()->setState(OBJELEMENT_COLLISION);
		// Aumentamos el contador de cohetes
		m_pLPlayer->setRockets(min(m_pLPlayer->getRockets() + 5.0f, m_pLPlayer->getVehicle()->getMaxCapacity()));
	}

	// Actualizamos el marcador de tiempos durante la partida
	if (fabs(fabs(m_pLastTime)-fabs(m_pTime)) >= 1.0f)
	{
		// Ponemos el texto
		if (m_pTime < 10.0f) wsprintf(txt, L"0%d", (int)(m_pTime));
		else wsprintf(txt, L"%d", (int)(m_pTime));
		g_GUI->setText(txt, GUI_STATIC_TITULO_2);
		// Si hemos llegado a cero, se terminó la partida
		if (m_pTime < 0.0f)
		{
			playLose(false);
			g_GUI->setTypeGUI(GUI_TYPE_WARNING_LOST);
			m_pLastTime = -1.0f;
			return;
		}

		// Si es bajo activamos sonido de tiempo
		else if (m_pTime < 11.0f)
			playTime(false);

		// Indicamos el tiempo total
		wsprintf(txt, L"Total time: %d", (int)(g_TimeGame));
		g_GUI->setText(txt, GUI_STATIC_INFO_3d);

		// Cada 1 segundo, tratamos de poner el objetivo en pista
		if (m_pArena->getObjetive()->getState() == OBJELEMENT_FREE)
		{
			// Lo mostramos
			m_pArena->nextObjective();
			// Actualizamos el objetivo en el minimapa
			int height = (int)(g_GUI->getHeight());
			int size = (int)(m_pArena->getSize());
			D3DXVECTOR3 pos_v; m_pArena->getObjetive()->getPosition(pos_v);
			g_GUI->getGUI()->GetStatic(GUI_STATIC_MAP2)->SetLocation(size/2 - 3 + (int)(pos_v.z / 2.0f), height - 39 - size/2 + (int)(pos_v.x / 2.0f));
		}

		// Cada 5 segundos, tratamos de poner otro elemento en pista
		if (((int)(g_TimeGame)%5) == 0)
			m_pArena->nextElement();

		// Reiniciamos la vida perdida en el último segundo
		m_pHealthSecond = 0.0f;

		// Actualizamos
		m_pLastTime = m_pTime;
	}

	// Si tenemos que cambiar el mostrador de la vida
	if (m_pLastHealth != m_pLPlayer->getHealth())
	{
		// Si se perdió vida, lo acumulamos como vida perdida en el último segundo
		if (m_pLastHealth > m_pLPlayer->getHealth())
			m_pHealthSecond += m_pLastHealth - m_pLPlayer->getHealth();

		// Comprobamos que no se pierda más vida del máximo permitido por segundo
		if (m_pHealthSecond > GAME_MAX_SEC_HEALTH)
			m_pLPlayer->setHealth(m_pLastHealth);

		// Ponemos el texto
		int In = (int)(m_pLPlayer->getHealth());
		int Tot = (int)(m_pLPlayer->getVehicle()->getMaxHealth());
		TCHAR txt_aux[128];
		for (int i=0 ; i<Tot ; i++)
			if (i < In) txt_aux[i] = 'I';
			else txt_aux[i] = '.';
		txt_aux[Tot] = '\0';
		wsprintf(txt, L"Health     [%s]", txt_aux);
		g_GUI->setText(txt, GUI_STATIC_INFO_3a);

		// Si es bajo lo ponemos en rojo
		if (In <= 5) g_GUI->getGUI()->GetStatic(GUI_STATIC_INFO_3a)->SetTextColor(D3DCOLOR_ARGB(150, 255, 220 - (5-In)*20, 220 - (5-In)*20));
		else g_GUI->getGUI()->GetStatic(GUI_STATIC_INFO_3a)->SetTextColor(D3DCOLOR_ARGB(150, 220, 220, 255));

		// Si hemos llegado a cero, se terminó la partida
		if (m_pLPlayer->getHealth() <= 0.0f)
		{
			playLose(false);
			g_GUI->setTypeGUI(GUI_TYPE_WARNING_DIE);
			C3DParticles *p1 = m_pLPlayer->getVehicle()->getParticles1();
			C3DParticles *p2 = m_pLPlayer->getVehicle()->getParticles2();
			if (p1)
			{
				p1->SetLifeCycle(1.0f);
				p1->SetVelocityVar(0.8f);
				p1->SetNumToRelease(5);
				p1->SetMaxParticles(100);
				p1->SetVelocity(D3DXVECTOR3(0.0f, 4.0f, 0.0f));
			}
			if (p2)
			{
				p2->SetLifeCycle(1.0f);
				p2->SetVelocityVar(0.8f);
				p1->SetNumToRelease(5);
				p2->SetMaxParticles(100);
				p2->SetVelocity(D3DXVECTOR3(0.0f, 4.0f, 0.0f));
			}
			m_pLPlayer->getVehicle()->getPhysics()->setLAcceleration(1.0f);
			m_pLPlayer->getVehicle()->setMaxSpeed(8.0f);

			m_pLastTime = -1.0f;
			return;
		}
		// Actualizamos
		m_pLastHealth = m_pLPlayer->getHealth();
	}

	// Si tenemos que cambiar el mostrador de cohetes
	if (m_pLastRockets != m_pLPlayer->getRockets())
	{
		// Ponemos el texto
		int In = (int)(m_pLPlayer->getRockets());
		int Tot = (int)(m_pLPlayer->getVehicle()->getMaxCapacity());
		TCHAR txt_aux[128];
		for (int i=0 ; i<Tot ; i++)
			if (i < In) txt_aux[i] = 'I';
			else txt_aux[i] = '.';
		txt_aux[Tot] = '\0';
		wsprintf(txt, L"Rockets   [%s]", txt_aux);
		g_GUI->setText(txt, GUI_STATIC_INFO_3b);
		// Actualizamos
		m_pLastRockets = m_pLPlayer->getRockets();
	}

	// Si tenemos que cambiar el mostrador de objetivos
	if (m_pLastObjetives != m_pObjetives)
	{
		// Ponemos el texto
		int In = (int)(m_pObjetives);
		int Tot = (int)(m_pArena->getNumObj());
		TCHAR txt_aux[128];
		for (int i=0 ; i<Tot ; i++)
			if (i < In) txt_aux[i] = 'I';
			else txt_aux[i] = '.';
		txt_aux[Tot] = '\0';
		wsprintf(txt, L"Objetive  [%s]", txt_aux);
		g_GUI->setText(txt, GUI_STATIC_INFO_3c);
		// Si hemos completado todos los objetivos, se terminó la partida
		if (In >= Tot)
		{
			playWin(false);
			g_GUI->setTypeGUI(GUI_TYPE_WARNING_WIN);
			m_pObjetives = -1.0f;
			return;
		}
		// Actualizamos
		m_pLastObjetives = m_pObjetives;
	}

	// Actualizamos el vehículo en el minimapa
	if (m_pLPlayer->getVehicle()->getLastModified() || g_TimeGame < 1.0f)
	{
		int height = (int)(g_GUI->getHeight());
		int size = (int)(m_pArena->getSize());
		D3DXVECTOR3 pos_v; m_pLPlayer->getVehicle()->getPosition(pos_v);
		g_GUI->getGUI()->GetStatic(GUI_STATIC_MAP1)->SetLocation(size/2 - 3 + (int)(pos_v.z / 2.0f), height - 39 - size/2 + (int)(pos_v.x / 2.0f));
	}

}

//--------------------------------------------------------------------------------------
// Libera los datos del juego
//--------------------------------------------------------------------------------------
void CGame::Release()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;
    m_bInit = false;

	// Liberamos sonidos
    if(m_pSndWin)		g_3DSoundMng->Destroy(m_pSndWin);
    if(m_pSndLose)		g_3DSoundMng->Destroy(m_pSndLose);
    if(m_pSndTime)		g_3DSoundMng->Destroy(m_pSndTime);

    // Liberamos estructuras internas
    SAFE_DELETE(m_pArena);
    SAFE_DELETE(m_pLPlayer);
    for (vector<CPlayerRemote*>::iterator itr = m_aRPlayers.begin() ; itr != m_aRPlayers.end() ; itr++)
        SAFE_DELETE(*itr);
    m_aRPlayers.clear();
    m_iType = -1;
}

//--------------------------------------------------------------------------------------
// Para activar el sonido de victoria
//--------------------------------------------------------------------------------------
void CGame::playWin(bool loop, long volume, long frecuency)
{
	if(!m_pSndWin) return;

	m_pSndWin->Play(loop);
	if(volume)    m_pSndWin->setVolume(volume);
	if(frecuency) m_pSndWin->setFrecuency(frecuency);
}

//--------------------------------------------------------------------------------------
// Para activar el sonido de derrota
//--------------------------------------------------------------------------------------
void CGame::playLose(bool loop, long volume, long frecuency)
{
	if(!m_pSndLose) return;

	m_pSndLose->Play(loop);
	if(volume)    m_pSndLose->setVolume(volume);
	if(frecuency) m_pSndLose->setFrecuency(frecuency);
}

//--------------------------------------------------------------------------------------
// Para activar el sonido de tiempo
//--------------------------------------------------------------------------------------
void CGame::playTime(bool loop, long volume, long frecuency)
{
	if(!m_pSndTime) return;

	m_pSndTime->Play(loop);
	if(volume)    m_pSndTime->setVolume(volume);
	if(frecuency) m_pSndTime->setFrecuency(frecuency);
}
