//--------------------------------------------------------------------------------------
// 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_pLPlayer1 = NULL;
    m_pLPlayer2 = 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 los jugadores dependiendo del tipo de juego
	switch(m_iType)
	{
	case GAME_1PLAYER:
		m_pLPlayer1 = new CPlayerLocal;
		if(!m_pLPlayer1) return false;
		if(!m_pLPlayer1->initialize(dData->m_awLName.c_str(), dData->m_awLVehicle1.c_str(), m_pArena, PLAYERLOCAL_1)) return false;
		break;

	case GAME_2PLAYER_FIGHT:
	case GAME_2PLAYER_OBJS:
		m_pLPlayer1 = new CPlayerLocal;
		if(!m_pLPlayer1) return false;
		if(!m_pLPlayer1->initialize(dData->m_awLName.c_str(), dData->m_awLVehicle1.c_str(), m_pArena, PLAYERLOCAL_1)) return false;
		m_pLPlayer2 = new CPlayerLocal;
		if(!m_pLPlayer2) return false;
		if(!m_pLPlayer2->initialize(dData->m_awLName.c_str(), dData->m_awLVehicle2.c_str(), m_pArena, PLAYERLOCAL_2)) return false;
		break;
	}

	// Inicializamos variables
	m_pTime = m_pArena->getTimeIni();
	m_pLastTime = 0.0f;
	m_bChangeTime = false;

	// Comienza el juego en GAME_TIME_INI_GAME segundos
	g_TimeGame = GAME_TIME_INI_GAME;

	// 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 dependiendo del tipo de juego
	switch(m_iType)
	{
	case GAME_1PLAYER:
		m_pLPlayer1->render();
		break;
	case GAME_2PLAYER_FIGHT:
	case GAME_2PLAYER_OBJS:
		m_pLPlayer1->render();
		m_pLPlayer2->render();
		break;
	}

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

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

	// Actualizamos los jugadores dependiendo del tipo de juego
	switch (m_iType)
	{
	case GAME_1PLAYER:
		m_pLPlayer1->update();
		break;
	case GAME_2PLAYER_FIGHT:
	case GAME_2PLAYER_OBJS:
		m_pLPlayer1->update();
		m_pLPlayer2->update();
		break;
	}

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

	// Actualizamos el tiempo
	if (!updateTime()) return;

	// Actualizamos los marcadores dependiendo del tipo de juego
	switch (m_iType)
	{
	case GAME_1PLAYER:
		updateInfo(1);
		break;
	case GAME_2PLAYER_FIGHT:
	case GAME_2PLAYER_OBJS:
		updateInfo(1);
		updateInfo(2);
		break;
	}

}

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

	// Tomamos los elementos según el jugador
	CPlayer *player;
	int GuiHealth;
	int GuiRockets;
	int GuiMines;
	int GuiObjectives;
	int GuiPlayer;
	switch(num_player)
	{
	case 1:
		player = m_pLPlayer1;
		GuiHealth = GUI_STATIC_P1_HEALTH;
		GuiRockets = GUI_STATIC_P1_ROCKETS;
		GuiMines = GUI_STATIC_P1_MINES;
		GuiObjectives = GUI_STATIC_P1_OBJECTIVES;
		GuiPlayer = GUI_STATIC_MAP_P1;
		break;
	case 2:
		player = m_pLPlayer2;
		GuiHealth = GUI_STATIC_P2_HEALTH;
		GuiRockets = GUI_STATIC_P2_ROCKETS;
		GuiMines = GUI_STATIC_P2_MINES;
		GuiObjectives = GUI_STATIC_P2_OBJECTIVES;
		GuiPlayer = GUI_STATIC_MAP_P2;
		break;
	}

	// Reiniciamos la vida perdida en el último segundo
	if (m_bChangeTime)
		player->resetHealthSecond();

	// Si tenemos que cambiar el mostrador de la vida
	if (player->changedHealth())
	{
		// Ponemos el texto
		int In = (int)(player->getHealth());
		int Tot = (int)(player->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, GuiHealth);

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

		// Si hemos llegado a cero, se terminó la partida
		if (player->getHealth() <= 0.0f)
		{
			playLose(false);
			g_GUI->setTypeGUI(GUI_TYPE_WARNING_DIE);
			C3DParticles *p1 = player->getVehicle()->getParticles1();
			C3DParticles *p2 = player->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));
			}
			player->getVehicle()->getPhysics()->setLAcceleration(1.0f);
			player->getVehicle()->setMaxSpeed(8.0f);

			m_pLastTime = -1.0f;
			return;
		}
	}

	// Si tenemos que cambiar el mostrador de cohetes
	if (player->changedRockets())
	{
		// Ponemos el texto
		int In = (int)(player->getRockets());
		int Tot = (int)(player->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, GuiRockets);
	}

	// Si tenemos que cambiar el mostrador de minas
	if (player->changedMines())
	{
		// Ponemos el texto
		int In = (int)(player->getMines());
		int Tot = (int)(player->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"Mines      [%s]", txt_aux);
		g_GUI->setText(txt, GuiMines);
	}

	// Si tenemos que cambiar el mostrador de objetivos
	if (player->changedObjectives() && m_iType != GAME_2PLAYER_FIGHT)
	{
		// Ponemos el texto
		int In = (int)(player->getObjectives());
		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, GuiObjectives);
		// Aumentamos el contador de tiempo
		m_pTime += m_pArena->getTimeObj();
		// Actualizamos el objetivo en el minimapa
		g_GUI->getGUI()->GetStatic(GUI_STATIC_MAP_OBJ)->SetLocation(-100, -100);
		// Si hemos completado todos los objetivos, se terminó la partida
		if (In >= Tot)
		{
			playWin(false);
			g_GUI->setTypeGUI(GUI_TYPE_WARNING_WIN);
			player->setObjectives(-1.0f);
			return;
		}
	}

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

}

//--------------------------------------------------------------------------------------
// Para actualizar el tiempo durante el juego
//--------------------------------------------------------------------------------------
bool CGame::updateTime(void)
{
	TCHAR txt[MAX_STRING+1];

	// Salimos si ha pasado el tiempo o alguien ha ganado
	if ((m_pLastTime == -1.0f) ||
		(m_pLPlayer1 && m_pLPlayer1->getObjectives() == -1.0f) ||
		(m_pLPlayer2 && m_pLPlayer2->getObjectives() == -1.0f)) return false;

	// La primera vez reiniciamos los marcadores
	if (g_TimeGame == GAME_TIME_INI_GAME)
	{
		// Marcadores del jugador 1
		g_GUI->getGUI()->GetStatic(GUI_STATIC_P1_HEALTH)->SetTextColor(D3DCOLOR_ARGB(150, 220, 220, 255));
		g_GUI->setText(L"Starting the game ...", GUI_STATIC_P1_HEALTH);
		g_GUI->setText(L"Please wait", GUI_STATIC_P1_ROCKETS);
		g_GUI->setText(L"", GUI_STATIC_P1_MINES);
		g_GUI->setText(L"", GUI_STATIC_P1_OBJECTIVES);
		// Marcadores del jugador 2
		g_GUI->getGUI()->GetStatic(GUI_STATIC_P2_HEALTH)->SetTextColor(D3DCOLOR_ARGB(150, 220, 220, 255));
		g_GUI->setText(L"", GUI_STATIC_P2_HEALTH);
		g_GUI->setText(L"", GUI_STATIC_P2_ROCKETS);
		g_GUI->setText(L"", GUI_STATIC_P2_MINES);
		g_GUI->setText(L"", GUI_STATIC_P2_OBJECTIVES);
		// Marcador de tiempo total
		g_GUI->setText(L"", GUI_STATIC_TOTAL_TIME);
	}

	// Actualizamos el tiempo de juego
	float ElapsedTime = DXUTGetElapsedTime();
	g_TimeGame += ElapsedTime;
	if (g_TimeGame > 0.0f)
	{
		if (m_iType != GAME_2PLAYER_FIGHT)
			m_pTime -= ElapsedTime;
		else
			m_pTime += ElapsedTime;
	}

	// Actualizamos el marcador de tiempos antes de la partida
	if (g_TimeGame < 0.0f)
	{
		// Ponemos el texto
		if (g_TimeGame < (2.0f*GAME_TIME_INI_GAME/3.0f)) wsprintf(txt, L"-");
		else if (g_TimeGame < (1.0f*GAME_TIME_INI_GAME/3.0f)) wsprintf(txt, L"--");
		else wsprintf(txt, L"---");
		g_GUI->setText(txt, GUI_STATIC_TITULO_2);

		// Retornamos falso
		return false;
	}

	// Actualizamos el marcador de tiempos durante la partida
	m_bChangeTime = false;
	if (fabs(fabs(m_pLastTime)-fabs(m_pTime)) >= 1.0f)
	{
		// Si es un tipo de partida dónde influye el tiempo
		if (m_iType != GAME_2PLAYER_FIGHT)
		{
			// 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;
				// Retornamos falso
				return false;
			}

			// Si es bajo activamos sonido de tiempo
			if (m_pTime < GAME_LOW_TIME)
				playTime(false);
		}
		else g_GUI->setText(L"", GUI_STATIC_TITULO_2);

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

		// Cada GAME_TIME_OBJ segundo, tratamos de poner el objetivo en pista
		if (((int)(g_TimeGame) % GAME_TIME_OBJ) == 0 && m_iType != GAME_2PLAYER_FIGHT)
		{
			// 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_MAP_OBJ)->SetLocation(size/2 - 3 + (int)(pos_v.z / 2.0f), height - 39 - size/2 + (int)(pos_v.x / 2.0f));
		}

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

		// Actualizamos el tiempo anterior
		m_pLastTime = m_pTime;

		// Marcamos que hubo cambio de tiempo
		m_bChangeTime = true;
	}

	// Retornamos cierto
	return true;
}

//--------------------------------------------------------------------------------------
// 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_pLPlayer1);
    SAFE_DELETE(m_pLPlayer2);
    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);
}
