/*********************************************************************//**
*	Deklarace zbrani a prostredky pro jejich vytvoreni
*	Databaze zbrani spolecne s definicemi jednotlivych zbrani.
*	Kazda zbran obsahuje sadu funkci, ktere se volaji pri urcite udalosti:
*		- strelba, stav klidu, nabijeni apod.
*	Vsechny zbrane obsahuji nekolik zakladnich promennych, ktere indikuji
*	napriklad aktualni identifikacni cislo animace, model zbrane, potrebu
*	naboju, pocet naboju, maximalni pocet naboju, jmeno zbrane, moznost
*	pouzivani zbane (jeji vlastneni) atd..Tyto atributy jsou definovany
*	ve spolecne nadtride BasicWeapon.
*
*	author: Michal Jirous
*	date: 27.11.2008
*	file: weapons.cpp
**********************************************************************/

#include <map>

#include "weapons.h"
#include "mathematic.h"
#include "player.h"
#include "globaltime.h"
#include "game.h"
#include "attack_info.h"

#include "sys_controller.h"
#include "damage_types.h"
#include "bullet_tracks.h"

using namespace std;
using namespace modelLib;

//databaze ukazatelu na funkce, ktere vytvareni jednotlive zbrane, index pole je identifikacni cislo zbrane
weapon_creator_t weapon_creating_field[WEAPONS_COUNT] = {NULL};	

char* g_weapon_names[WEAPONS_COUNT] = {0};	//databaze jmen zbrani, index pole je identifikacni cislo zbrane

/**	@param weapon_name Jmeno zbrane.
*	@return Identifikacni cislo zbrane.
*/
unsigned int getWeaponId( std::string weapon_name )
{
	for( int i = 0; i <  WEAPONS_COUNT; i++ )
	{
		if( g_weapon_names[i] && strcmp( weapon_name.c_str(), g_weapon_names[i] ) == 0 )
			return i;	
	}
	return NO_WEAPON;
}

/** @param weapon_name Jmeno zbrane.
*	@param id Identifikacni ciso zbrane.
*/
void registerWeaponName( const char* weapon_name, unsigned int id )
{
	if( g_weapon_names[id%WEAPONS_COUNT] == NULL )
	{
		size_t size = strlen( weapon_name );	//zjistime delku textu
		g_weapon_names[id%WEAPONS_COUNT] = new char[size+1];	//vytvorime si prislusne misto v pameti
		strcpy( g_weapon_names[id%WEAPONS_COUNT], weapon_name );	//zkopirujeme text
	}
}


/** @param weapon_name Jmeno zbrane, kterou chceme vytvorit.
*	@return Novy objekt zbrane.
*/
BasicWeapon* createWeapon( std::string weapon_name )
{
	return createWeapon( getWeaponId( weapon_name ) );
}


/** @param weapon_type Identifikacni cislo zbrane.
*	@return novy objekt zbrane.
*/
BasicWeapon* createWeapon( unsigned int weapon_type )
{
	if( !isInRange( weapon_type, 0u, (unsigned int)(WEAPONS_COUNT-1) ) )	//testujeme, zda identifikacni cislo existuje
		return NULL;

	if( weapon_creating_field[weapon_type] )	//zjistujeme, zda pro toto id existuje vytvareci funkce
		return (weapon_creating_field[weapon_type])( );	//pokud ano, tak zbran vytvorime
		
	return NULL;
}

/** @param weapon_type Identifikacni cislo zbrane.
*	@param fnc Funkce, ktera vytvari objekt zbrane s zadanym identifikacnim cislem.
*/
void addNewWeaponCreator( unsigned int weapon_type, weapon_creator_t fnc )
{
	if( !isInRange( weapon_type, 0u, (unsigned int)(WEAPONS_COUNT-1) ) )
		return;
	weapon_creating_field[weapon_type] = fnc;
}



/*********************************************************************************************
*		BASIC WEAPON CLASS
*********************************************************************************************/

BasicWeapon::BasicWeapon()
{
	baseInit();
}

#include "crosshairs.h"

void BasicWeapon::baseInit()
{
	m_bIsOwned				= false;
	m_bNeedAmmo				= false;
	m_iAmmoType				= NO_AMMO;
	m_iAmmoAmount			= 0;
	m_iMaxAmmoAmount		= 0;
	m_pModel				= NULL;
	m_iLockedType			= UNLOCKED;
	m_uiCurrentAnimation	= -1;
	m_uiAnimationEndTime	= 0;
	anim_end_func			= NULL;
	m_bWeaponInHands		= false;
	m_bAnimationRepeated	= false;
	m_iCrosshair			= CROSSHAIR_NONE;
}

/** @param filename Cesta k souboru modelu.
*/
void BasicWeapon::setModel( std::string filename )
{
	m_pModel = modelLibrary.applyHalfLifeMDL( filename );


	m_sModelFilename = filename;
}


BasicWeapon::~BasicWeapon()
{
	modelLibrary.unloadModel( m_sModelFilename );
	if( m_pModel )
		modelLibrary.freeModelElement( m_pModel );
}

/** @return Aktualni pocet naboju, ktere jsou pro tuto zbran v zasobe. */
unsigned int BasicWeapon::getTotalAmmo()
{
	if( game.m_pPlayer )
	{
		return game.m_pPlayer->AmmoAndWeapons.getAmmoAmount( m_iAmmoType );
	}
	return 0;
}

void BasicWeapon::draw()
{
	if( m_pModel )
		m_pModel->draw();

}

void BasicWeapon::run()
{
	if( m_pModel )
		m_pModel->update( systemController::RUNTIME_PERIOD_MS );

	//animace se dokoncila
	if( !m_bAnimationRepeated && m_uiCurrentAnimation != -1 && !(HLMDL(m_pModel)->isPlaying())  )	
	{
		m_iLockedType = UNLOCKED;
		m_uiCurrentAnimation = -1;	//zadna animace
		if( m_pTargetWeapon && anim_end_func )
			(m_pTargetWeapon->*anim_end_func)();	//zavolame funkci, ktera mela byt spustena po dokonceni animace
		
		anim_end_func = NULL;
		m_pTargetWeapon = NULL;
	}

	//pokud se nic nedeje, tak spustime klidovy stav
	if( m_uiCurrentAnimation == -1 && m_iLockedType == UNLOCKED )
	{
		idle();
	}
}

/** @param id Identifikacni cislo (poradove cislo) animace.
*	@param length Doba, po kterou ma byt zadost o jinou animaci uzamcena (prebije to animace s vyssi prioritou zamku).
*	@param repeat Nastavuje opakovani animace.
*	@param lock_type Typ zamku z vyctu anim_lock_types.
*	@param function Clenska funkce tridy zbrane, ktera se ma zavolat po dokonceni animace.
*	@param pWeapon Objekt zbrane, kde se ma funkce z parametru 'function' zavolat.
*	@return True, pokud se animaci podari spustit, jinak false.
*/
bool BasicWeapon::playAnimation( int id, int length, bool repeat, int lock_type, WpnFunc function, BasicWeapon *pWeapon )
{
	if( m_pModel )
	{
		/*Pokud je stav: Odemceno nebo je normalni zamek, ale chcecme spustit animaci se 
		zamkem GRAB_OR_HOLSTER_LOCK a nebo vyprsela doba platnosti animace, tak muzeme
		pozadovanou animaci spustit. */
		if( m_iLockedType == UNLOCKED || ( m_iLockedType == NORMAL_LOCK && lock_type == GRAB_OR_HOLSTER_LOCK ) || m_uiAnimationEndTime < global_time::getGlobalTime() )
		{
			m_iLockedType = getFromRange( lock_type, (int)UNLOCKED, (int)GRAB_OR_HOLSTER_LOCK );	//osetreni zadani spatneho zamku
			
			HLMDL(m_pModel)->playSequence( id, repeat );

			m_uiCurrentAnimation = id;
			m_uiAnimationEndTime = global_time::getGlobalTime() + length;
			anim_end_func = function;
			m_pTargetWeapon	= pWeapon;
			m_bAnimationRepeated = repeat;
			return true;
		}
	}
	return false;
}

/** Funkce vlozi zbran hraci do rukou - nastavi prislusnou promenou.
*	@return True pokud lze provest tuto operaci (Primarne vzdy true).
*/
bool BasicWeapon::basic_grab()
{
	m_bWeaponInHands = true;
	return true;
}

/** @param pulse True, pokud jde o prvni volani pri stisku tlacitka mysi, pri opakovanem spousteni kvuli drzeni tlacitka mysi je false.
*	@return True, pokud lze operaci provest (zalezi na tom, jestli ma hrac zbran v rukach ( je vytazena ).
*/
bool BasicWeapon::basic_attack( bool pulse )
{
	return m_bWeaponInHands;
}

/** Funkce odebere zbran z rukou *nastavi prislusnou promennou a nastavi stav na UNLOCKED.
*	@return Vzdy true.
*/
bool BasicWeapon::basic_holster()
{
	m_bWeaponInHands = false;
	m_iLockedType = UNLOCKED;
	return true;
}

/** @param pulse True, pokud jde o prvni volani pri stisku tlacitka mysi, pri opakovanem spousteni kvuli drzeni tlacitka mysi je false.
*	@return True, pokud lze operaci provest (zalezi na tom, jestli ma hrac zbran v rukach ( je vytazena ).
*/
bool BasicWeapon::basic_reload( bool pulse )
{
	return m_bWeaponInHands;
}


void BasicWeapon::weapon_grab()
{
	basic_grab();
	grab();
}

/** Funkce testuje, zda je mozne provest primarni utok a pokud ano, tak zavola funkci primaryAttack s parametrem pulse.
*	@param pulse True, pokud jde o prvni volani pri stisku tlacitka mysi, pri opakovanem spousteni kvuli drzeni tlacitka mysi je false.
*/
void BasicWeapon::weapon_primaryAttack( bool pulse )
{
	if( basic_attack(pulse) )
		primaryAttack(pulse);
}

/** Funkce testuje, zda je mozne provest sekundarni utok a pokud ano, tak zavola funkci secondaryAttack s parametrem pulse.
*	@param pulse True, pokud jde o prvni volani pri stisku tlacitka mysi, pri opakovanem spousteni kvuli drzeni tlacitka mysi je false.
*/
void BasicWeapon::weapon_secondaryAttack( bool pulse )
{
	if( basic_attack(pulse) )
		secondaryAttack(pulse);
}

/** Funkce testuje, zda je mozne provest schovani zbrane a pokud ano, tak zavola funkci holster.
*/
void BasicWeapon::weapon_holster()
{
	if( basic_holster() )
		holster();
}

/** Funkce testuje, zda je mozne provest nabijeni a pokud ano, tak zavola funkci reload s parametrem pulse.
*	@param pulse True, pokud jde o prvni volani pri stisku tlacitka mysi, pri opakovanem spousteni kvuli drzeni tlacitka mysi je false.
*/
void BasicWeapon::weapon_reload( bool pulse )
{
	if( basic_reload(pulse) )
		reload(pulse);
}

#include "level_collision.h"

/** @param damage Velikost zraneni pri zasahu.
*	@param distance Maximalni vzdalenost utoku.
*	@param damage_type Typ utoku z vyctu dmg::dmg_types.
*	@param dispersion Rozptyl strely.
*	@return True pokud doslo k zasahu, v opacnem pripade false.
*/
bool BasicWeapon::basic_fight( int damage, AttackInfo &attackInfo, float distance, int damage_type, float dispersion )
{
	if( game.m_pPlayer )
	{
		attackInfo.create( game.m_pPlayer->getRenderOrigin(), game.vecDir, game.m_pPlayer, damage, distance, damage_type, dispersion );
		bool ret = collisionSystem.processShooting( attackInfo );


		if( damage_type == dmg::BULLET )
		{
			Point start = game.m_pPlayer->getRenderOrigin();
				
			start[0] += game.vecDir.y*0.8f ;	//otoceni o 90 stupnu doprava a pricteni k bodu
			start[1] -= game.vecDir.x*0.8f;
			start[2] -=  0.5f;

			start += game.vecDir*8.0f;
			
			bullet_tracks::addTrack( (float*)&start, (float*)(&attackInfo.m_End), 10 );

			//InteractionParms *tmp = interaction.getCurrentParms();
			//if( tmp )
			//{
			//	float start[3];
			//	memcpy( start,(float*)&(-game.m_ViewInterpolation.current),sizeof(start) );
			//	//toto je zatim takovy hocus pokus :D
			//	start[0] += tmp->m_vecDirection.y*0.8f ;	//otoceni o 90 stupnu doprava a pricteni k bodu
			//	start[1] -= tmp->m_vecDirection.x*0.8f;
			//	start[2] -=  0.5f;

			//	start[0] += tmp->m_vecDirection.x*8.0f;
			//	start[1] += tmp->m_vecDirection.y*8.0f;
			//	start[2] += tmp->m_vecDirection.z*8.0f;
			//	
			//	bullet_tracks::addTrack( start, (float*)&tmp->m_pointResult, 10 );
			//}
		}
		
		return ret;
	}

	return false;
}

void BasicWeapon::setOwned( bool owned )
{
	if( owned )
	{
		if( !m_bIsOwned )
		{
			m_iAmmoAmount = m_iMaxAmmoAmount;
			
		}
		else
			game.m_pPlayer->AmmoAndWeapons.addAmmo( m_iAmmoType, m_iMaxAmmoAmount );
	}

	m_bIsOwned = owned;
}



/**********************************************************************************************
*		DEFAULT WEAPON CLASS
**********************************************************************************************/

NoWeapon::NoWeapon(  ) : BasicWeapon( )
{
	m_bIsOwned = true;
}

