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

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

//--------------------------------------------------------------------------------------
// Variables Globales
//--------------------------------------------------------------------------------------
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)
float					g_fFactor;		   // Factor de fricción del suelo de la arena

//--------------------------------------------------------------------------------------
// Funciones auxiliares
//--------------------------------------------------------------------------------------
// Función para obtener un entero aleatorio entre un mínimo y un máximo
inline int getRandom_i (int Min, int Max)
{
	return Min + (rand() % (Max - Min + 1));
}

//--------------------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------------------
C3DObjArena::C3DObjArena()
{
    m_awName = NULL;
    m_awDescription = NULL;
	m_awRankFile = NULL;
	m_awRankTxt = NULL;

	m_pSky = NULL;
    m_pObjetive = NULL;
    m_pRand = NULL;
    m_pHealth = NULL;
    m_pRocket = NULL;

	m_pPositions.clear();
	m_pPosObjective = -1;
	m_pPosElement = -1;

	m_pRanking.clear();
}

//--------------------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------------------
C3DObjArena::~C3DObjArena()
{
    // Eliminamos los elementos específicos de la arena
    SAFE_DELETE_ARRAY(m_awName);
    SAFE_DELETE_ARRAY(m_awDescription);
	SAFE_DELETE_ARRAY(m_awRankFile);
	SAFE_DELETE_ARRAY(m_awRankTxt);

	// El cielo se eliminará en el destructor de la clase padre C3DObject

	// Eliminamos los elementos de la arena
    SAFE_DELETE(m_pObjetive);
    SAFE_DELETE(m_pRand);
    SAFE_DELETE(m_pHealth);
    SAFE_DELETE(m_pRocket);

	// Eliminamos la lista de ubicaciones
	m_pPositions.clear();

	// Eliminamos la lista de rankings
	m_pRanking.clear();
}

//--------------------------------------------------------------------------------------
// Inicializa la arena
//--------------------------------------------------------------------------------------
bool C3DObjArena::initialize(C3DObject *dParent, LPCWSTR ObjectPath)
{
    // Función de la clase padre
    if(!C3DObject::initialize(dParent, ObjectPath))
        return false;

    // Se usará una nueva instancia de CXMLIO
    CXMLIO *myXMLIO = new CXMLIO;
    if(!myXMLIO) return false;

    // Tomamos los elementos específicos de la arena
    if(myXMLIO->LoadFile(ObjectPath))
    {
        WCHAR value[MAX_STRING+1];

        // Nombre (atributo 'name')
        if(myXMLIO->GetAttribute(L"name", value)) m_awName = _wcsdup(value);

        // Descripción (atributo 'description')
        if(myXMLIO->GetAttribute(L"description", value)) m_awDescription = _wcsdup(value);

        // Descripción (atributo 'ranking')
        if(myXMLIO->GetAttribute(L"ranking", value)) m_awRankFile = _wcsdup(value);

		// Factor de fricción (atributo 'f_factor')
        g_fFactor = 1.0f;
        if(myXMLIO->GetAttribute(L"f_factor", value))  g_fFactor = (float)_wtof(value);

		// Número de objetivos a capturar (atributo 'num_obj')
		m_pNumObj = 15.0f;
        if(myXMLIO->GetAttribute(L"num_obj", value))  m_pNumObj = (float)_wtof(value);

		// Tiempo inicial para la arena (atributo 'time_ini')
		m_pTimeIni = 30.0f;
        if(myXMLIO->GetAttribute(L"time_ini", value))  m_pTimeIni = (float)_wtof(value);

		// Tiempo por objetivo capturado (atributo 'time_obj')
		m_pTimeObj = 5.0f;
        if(myXMLIO->GetAttribute(L"time_obj", value))  m_pTimeObj = (float)_wtof(value);

		// Tamaño de la arena (atributo 'size')
		m_pSize = 60.0f;
        if(myXMLIO->GetAttribute(L"size", value))  m_pSize = (float)_wtof(value);

		// Tomamos el puntero del cielo (nodo 'sky')
		// Si no estamos en modo juego o menu no lo cargamos
		if(	(g_bInGame || g_bInMenu) &&
			myXMLIO->MoveSubElement(L"sky"))
        {
            // Se carga la configuración del objeto hijo
            if(!m_pSky) m_pSky = new C3DObject;
            if(!m_pSky) return false;
            if(!m_pSky->initialize(this, myXMLIO)) return false;

            // El objeto se almacena como hijo del actual
            if(!m_paObjets) m_paObjets = new vector<C3DObject*>;
            if(!m_paObjets) return false;
            m_paObjets->push_back(m_pSky);

            myXMLIO->MoveParent();
        }

		// Cargamos la lista de ubicaciones
		// Si no estamos en modo juego no lo cargamos
		if(g_bInGame)
		{
			for(long i=0 ; myXMLIO->MoveSubElement(L"elements", i) ; i++)
			{
				D3DXVECTOR3 pos (0.0f, 1.0f, 0.0f);
				if(myXMLIO->GetAttribute(L"x", value)) pos.x = (float)_wtof(value);
				if(myXMLIO->GetAttribute(L"y", value)) pos.y = (float)_wtof(value);
				if(myXMLIO->GetAttribute(L"z", value)) pos.z = (float)_wtof(value);
				m_pPositions.push_back(pos);

				myXMLIO->MoveParent();
			}
		}
    }

    // Liberamos el parseador de XML
    SAFE_DELETE(myXMLIO);

	// Cargamos la lista de rankings
	if (m_awRankFile) loadRanking();

	// Si estamos en modo juego cargamos los elementos de la arena
	if(g_bInGame)
	{
		// Cargamos el elemento objetivo (archivo 'obj_obj.xml') y lo desactivamos
        if(!m_pObjetive) m_pObjetive = new C3DObjElement;
        if(!m_pObjetive) return false;
        if(!m_pObjetive->initialize(this, L".\\models\\objects\\obj_obj.xml", OBJELEMENT_OBJECTIVE)) return false;
		m_pObjetive->setEnabled(false);

		// Cargamos el elemento interrogación (archivo 'obj_rand.xml') y lo desactivamos
        if(!m_pRand) m_pRand = new C3DObjElement;
        if(!m_pRand) return false;
        if(!m_pRand->initialize(this, L".\\models\\objects\\obj_rand.xml", OBJELEMENT_RAND)) return false;
		m_pRand->setEnabled(false);

		// Cargamos el elemento de vida (archivo 'obj_health.xml') y lo desactivamos
        if(!m_pHealth) m_pHealth = new C3DObjElement;
        if(!m_pHealth) return false;
        if(!m_pHealth->initialize(this, L".\\models\\objects\\obj_health.xml", OBJELEMENT_HEALTH)) return false;
		m_pHealth->setEnabled(false);

		// Cargamos el elemento cohete (archivo 'obj_rocket.xml') y lo desactivamos
        if(!m_pRocket) m_pRocket = new C3DObjElement;
        if(!m_pRocket) return false;
        if(!m_pRocket->initialize(this, L".\\models\\objects\\obj_rocket.xml", OBJELEMENT_ROCKET)) return false;
		m_pRocket->setEnabled(false);
	}

    return true;
}

//--------------------------------------------------------------------------------------
// Actualiza la arena
//--------------------------------------------------------------------------------------
void C3DObjArena::update()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

	// Actualizamos los elementos de la arena
	if (m_pObjetive) m_pObjetive->update();
    if (m_pRand) m_pRand->update();
    if (m_pHealth) m_pHealth->update();
    if (m_pRocket) m_pRocket->update();

    // Función de la clase padre
    C3DObject::update();
}

//--------------------------------------------------------------------------------------
// Renderiza la arena
//--------------------------------------------------------------------------------------
void C3DObjArena::render()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

	// Renderizamos los elementos de la arena
	if (m_pObjetive) m_pObjetive->render();
    if (m_pRand) m_pRand->render();
    if (m_pHealth) m_pHealth->render();
    if (m_pRocket) m_pRocket->render();

    // Función de la clase padre
    C3DObject::render();
}

//--------------------------------------------------------------------------------------
// Carga el fichero de rankings
//--------------------------------------------------------------------------------------
bool C3DObjArena::loadRanking(void)
{
    // Se usará una nueva instancia de CXMLIO
    CXMLIO *myXMLIO = new CXMLIO;
    if(!myXMLIO) return false;

	// Cargamos el fichero de rankings
    if(myXMLIO->LoadFile(m_awRankFile))
    {
        WCHAR value[MAX_STRING+1];

		// Tomamos cada elemento del ranking
		for(long i=0 ; myXMLIO->MoveSubElement(L"rank", i) ; i++)
		{
			Rank R;
			if(myXMLIO->GetAttribute(L"player", value)) wcscpy(R.m_Player, value);
			if(myXMLIO->GetAttribute(L"vehicle", value)) wcscpy(R.m_Vehicle, value);
			if(myXMLIO->GetAttribute(L"time", value)) R.m_Time = (float)_wtof(value);
			m_pRanking.push_back(R);
			myXMLIO->MoveParent();
			if (i >= OBJARENA_NUM_RANK) break;
		}

		// Ordenamos el ranking
		m_pRanking.sort();
    }

    // Liberamos el parseador de XML
    SAFE_DELETE(myXMLIO);

	return true;
}

//--------------------------------------------------------------------------------------
// Guarda el fichero de rankings
//--------------------------------------------------------------------------------------
bool C3DObjArena::saveRanking(void)
{
    // Se usará una nueva instancia de CXMLIO
    CXMLIO *myXMLIO = new CXMLIO;
    if(!myXMLIO) return false;

	// Creamos el fichero de rankings
    if(myXMLIO->CreateFile(m_awRankFile, L"ranking") && myXMLIO->LoadFile(m_awRankFile))
    {
        WCHAR value[MAX_STRING+1];

		// Ordenamos el ranking
		m_pRanking.sort();

        // Ranking
        myXMLIO->MoveRoot();

		long i = 0;
		for(list<Rank>::iterator itr = m_pRanking.begin() ; itr != m_pRanking.end() ; itr++)
		{
			myXMLIO->SetSubAttribute(L"rank", L"player", itr->m_Player, i);
			myXMLIO->SetSubAttribute(L"rank", L"vehicle", itr->m_Vehicle, i);
			_itow((int)(itr->m_Time), value, 10);
			myXMLIO->SetSubAttribute(L"rank", L"time", value, i);
			i++;
			if (i >= OBJARENA_NUM_RANK) break;
		}

		// Creamos el fichero
        myXMLIO->SaveFile(m_awRankFile);
    }

    // Liberamos el parseador de XML
    SAFE_DELETE(myXMLIO);

	return true;
}

//--------------------------------------------------------------------------------------
// Añade un elemento al ranking
//--------------------------------------------------------------------------------------
void C3DObjArena::addRanking(Rank &R)
{
	// Lo insertamos
	m_pRanking.push_back(R);
	// Ordenamos el ranking
	m_pRanking.sort();
	// Guardamos el fichero
	saveRanking();
}

//--------------------------------------------------------------------------------------
// Función observadora del texto de ranking
//--------------------------------------------------------------------------------------
LPWSTR C3DObjArena::getRankTxt(void)
{
	SAFE_DELETE_ARRAY(m_awRankTxt);
	m_awRankTxt = new WCHAR [OBJARENA_MAX_RANKTXT+1];
	assert(m_awRankTxt!=NULL);

	long i = 0;
	WCHAR value[OBJARENA_MAX_RANKTXT+1];
	_snwprintf(m_awRankTxt, OBJARENA_MAX_RANKTXT, L"BEST TIMES...");
	for(list<Rank>::iterator itr = m_pRanking.begin() ; itr != m_pRanking.end() ; itr++)
	{
		_snwprintf(value, OBJARENA_MAX_RANKTXT, L"%s\n  %s with %s car in %d secs", m_awRankTxt, itr->m_Player, itr->m_Vehicle, (int)(itr->m_Time));
		_snwprintf(m_awRankTxt, OBJARENA_MAX_RANKTXT, L"%s", value);
		i++;
		if (i >= OBJARENA_NUM_RANK) break;
	}

	return m_awRankTxt;
}

//--------------------------------------------------------------------------------------
// Toma la siguiente posición para el objetivo
//--------------------------------------------------------------------------------------
void C3DObjArena::getPosObjective(D3DXVECTOR3 &Vec)
{
	// Tomamos un valor aleatorio
	int pos = getRandom_i(0, (int)(m_pPositions.size()-1));
	// Si es como el del objetivo, lo incrementamos
	if (pos == m_pPosObjective) pos = (pos+1) % (int)(m_pPositions.size());
	// Si es como el de los elementos, lo incrementamos
	if (pos == m_pPosElement) pos = (pos+1) % (int)(m_pPositions.size());
	// Actualizamos el del objetivo
	m_pPosObjective = pos;
	// Asignamos la posición
	Vec = m_pPositions[pos];
}

//--------------------------------------------------------------------------------------
// Toma la siguiente posición para elementos
//--------------------------------------------------------------------------------------
void C3DObjArena::getPosElement(D3DXVECTOR3 &Vec)
{
	// Tomamos un valor aleatorio
	int pos = getRandom_i(0, (int)(m_pPositions.size()-1));
	// Si es como el del objetivo, lo incrementamos
	if (pos == m_pPosObjective) pos = (pos+1) % (int)(m_pPositions.size());
	// Si es como el de los elementos, lo incrementamos
	if (pos == m_pPosElement) pos = (pos+1) % (int)(m_pPositions.size());
	// Actualizamos el del objetivo
	m_pPosElement = pos;
	// Asignamos la posición
	Vec = m_pPositions[pos];
}

//--------------------------------------------------------------------------------------
// Situa el siguiente objetivo
//--------------------------------------------------------------------------------------
void C3DObjArena::nextObjective(void)
{
	// Sólo si no está activado
	if (m_pObjetive->getState() == OBJELEMENT_FREE)
		m_pObjetive->setEnabled(true);
}

//--------------------------------------------------------------------------------------
// Situa el siguiente elemento
//--------------------------------------------------------------------------------------
void C3DObjArena::nextElement(void)
{
	// Sólo si no hay otro elemento activado
	if (m_pRand->getState() == OBJELEMENT_FREE &&
		m_pHealth->getState() == OBJELEMENT_FREE &&
		m_pRocket->getState() == OBJELEMENT_FREE)
	{
		switch(rand() % 4)
		{
		case 0: m_pRand->setEnabled(true); break; // Aleatorio
		case 1: m_pHealth->setEnabled(true); break; // Vida
		case 2: m_pRocket->setEnabled(true); break; // Cohete
		}
	}
}
