//
// attack.cpp, made by harSens, based on ammo.cpp
//
// implementation of CHudAttack class
//

#include "hud.h"
#include "cl_util.h"
#include "parsemsg.h"

#include <string.h>
#include <stdio.h>

#include "vgui_TeamFortressViewport.h"

WEAPON *gpActiveSel;	// NULL means off, 1 means just the menu bar, otherwise
						// this points to the active weapon menu item
//added by harSens
WEAPON *gpLastActiveSel;

client_sprite_t *GetSpriteList(client_sprite_t *pList, const char *psz, int iRes, int iCount);

int g_weaponselect = 0;

#define MAX_WEAPON_POSITIONS		MAX_WEAPON_SLOTS


class WeaponsResource
{
private:
	// Information about weapons & ammo
	WEAPON		rgWeapons[MAX_WEAPONS];	// Weapons Array

	// counts of weapons * ammo
	WEAPON*		rgSlots[MAX_WEAPON_SLOTS+1][MAX_WEAPON_POSITIONS+1];	// The slots currently in use by weapons.  The value is a pointer to the weapon;  if it's NULL, no weapon is there	

public:
	void Init( void )
	{
		memset( rgWeapons, 0, sizeof rgWeapons );
		memset( rgSlots, 0, sizeof rgSlots );
	}

	
	WEAPON *GetWeapon( int iId ) { return &rgWeapons[iId]; }
	void AddWeapon( WEAPON *wp ) 
	{ 
		rgWeapons[ wp->iId ] = *wp;			
		
		LoadWeaponSprites( &rgWeapons[ wp->iId ] );
	}

	void PickupWeapon( WEAPON *wp )
	{
		rgSlots[ wp->iSlot ][ wp->iSlotPos ] = wp;
	}

	void DropWeapon( WEAPON *wp )
	{
		rgSlots[ wp->iSlot ][ wp->iSlotPos ] = NULL;
	}

	void DropAllWeapons( void )
	{
		for ( int i = 0; i < MAX_WEAPONS; i++ )
		{
			if ( rgWeapons[i].iId )
				DropWeapon( &rgWeapons[i] );
		}
	}

	WEAPON* GetWeaponSlot( int slot, int pos ) { return rgSlots[slot][pos]; }

	void LoadWeaponSprites( WEAPON* wp );
	void LoadAllWeaponSprites( void );
	WEAPON* GetFirstPos( int iSlot );
	void SelectSlot( int iSlot, int fAdvance, int iDirection );
	WEAPON* GetNextActivePos( int iSlot, int iSlotPos );
};

WeaponsResource gWR;

void WeaponsResource :: LoadAllWeaponSprites( void )
{
	for (int i = 0; i < MAX_WEAPONS; i++)
	{
		if ( rgWeapons[i].iId )
			LoadWeaponSprites( &rgWeapons[i] );
	}
}

void WeaponsResource :: LoadWeaponSprites( WEAPON *pWeapon )
{
	int i, iRes;

	if (ScreenWidth < 640)
		iRes = 320;
	else
		iRes = 640;

	char sz[128];

	if ( !pWeapon )
		return;

	memset( &pWeapon->rcActive, 0, sizeof(wrect_t) );
	pWeapon->hActive = 0;
	
	sprintf(sz, "sprites/%s.txt", pWeapon->szName);
	client_sprite_t *pList = SPR_GetList(sz, &i);

	if (!pList)
		return;

	client_sprite_t *p;
	
	p = GetSpriteList( pList, "crosshair", iRes, i );
	if (p)
	{
		sprintf(sz, "sprites/%s.spr", p->szSprite);
		pWeapon->hCrosshair = SPR_Load(sz);
		pWeapon->rcCrosshair = p->rc;
	}
	else
		pWeapon->hCrosshair = NULL;

	p = GetSpriteList(pList, "weapon_s", iRes, i);
	if (p)
	{
		sprintf(sz, "sprites/%s.spr", p->szSprite);
		pWeapon->hActive = SPR_Load(sz);
		pWeapon->rcActive = p->rc;
	}
	else
		pWeapon->hActive = 0;	
}

// Returns the first weapon for a given slot.
WEAPON *WeaponsResource :: GetFirstPos( int iSlot )
{
	WEAPON *pret = NULL;

	for (int i = 0; i < MAX_WEAPON_POSITIONS; i++)
	{
		if ( rgSlots[iSlot][i])
		{
			pret = rgSlots[iSlot][i];
			break;
		}
	}

	return pret;
}


WEAPON* WeaponsResource :: GetNextActivePos( int iSlot, int iSlotPos )
{
	if ( iSlotPos >= MAX_WEAPON_POSITIONS || iSlot >= MAX_WEAPON_SLOTS )
		return NULL;

	WEAPON *p = gWR.rgSlots[ iSlot ][ iSlotPos+1 ];
	
	if ( !p )
		return GetNextActivePos( iSlot, iSlotPos + 1 );

	return p;
}


DECLARE_MESSAGE(m_Attack, WeaponList);	// new weapon type
DECLARE_MESSAGE(m_Attack, CurWeapon);	
DECLARE_MESSAGE(m_Attack, WeapUpdate);	
DECLARE_MESSAGE(m_Attack, HideWeapon);	// sends the hud showing value

DECLARE_COMMAND(m_Attack, Slot1);
DECLARE_COMMAND(m_Attack, Slot2);
DECLARE_COMMAND(m_Attack, Slot3);
DECLARE_COMMAND(m_Attack, Slot4);
DECLARE_COMMAND(m_Attack, Slot5);
DECLARE_COMMAND(m_Attack, Slot6);
DECLARE_COMMAND(m_Attack, Slot7);
DECLARE_COMMAND(m_Attack, Slot8);
DECLARE_COMMAND(m_Attack, Slot9);
DECLARE_COMMAND(m_Attack, Slot10);
DECLARE_COMMAND(m_Attack, NextWeapon);
DECLARE_COMMAND(m_Attack, PrevWeapon);
DECLARE_COMMAND(m_Attack, Close);

int CHudAttack::Init(void)
{
	HOOK_MESSAGE(CurWeapon);
	HOOK_MESSAGE(WeaponList);
	HOOK_MESSAGE(WeapUpdate);
	HOOK_MESSAGE(HideWeapon);
	
	HOOK_COMMAND("slot1", Slot1);
	HOOK_COMMAND("slot2", Slot2);
	HOOK_COMMAND("slot3", Slot3);
	HOOK_COMMAND("slot4", Slot4);
	HOOK_COMMAND("slot5", Slot5);
	HOOK_COMMAND("slot6", Slot6);
	HOOK_COMMAND("slot7", Slot7);
	HOOK_COMMAND("slot8", Slot8);
	HOOK_COMMAND("slot9", Slot9);
	HOOK_COMMAND("slot10", Slot10);
	HOOK_COMMAND("invnext", NextWeapon);
	HOOK_COMMAND("invprev", PrevWeapon);
	HOOK_COMMAND("cancelselect", Close);

	m_iActiveWeaponFrame = 0;
	m_fSecondAnimLoop = false;
	
	Reset();
	gWR.Init();

	gHUD.AddHudElem(this);
	m_iFlags |= HUD_ACTIVE; //!!!
	return 1;
};

void CHudAttack::Reset(void)
{
	gpActiveSel = NULL;
	//gWR.DropAllWeapons();
	gHUD.m_iHideHUDDisplay = 0;	
}

int CHudAttack::VidInit(void)
{	// If we've already loaded weapons, let's get new sprites
	gWR.LoadAllWeaponSprites();
	return 1;
}

//
// Think:
//  Used for selection of weapon menu item.
//
void CHudAttack::Think(void)
{
	if ( gHUD.m_fPlayerDead )
		return;
	
	if (!gpActiveSel)
		return;

	if (gpActiveSel != gpLastActiveSel)
	{	ServerCmd(gpActiveSel->szName);
		g_weaponselect = gpActiveSel->iId;
	}	
}

// Menu Selection Code

void WeaponsResource :: SelectSlot( int iSlot, int fAdvance, int iDirection )
{
	if ( gHUD.m_Menu.m_fMenuDisplayed && (fAdvance == FALSE) && (iDirection == 1) )	
	{ // menu is overriding slot use commands
		gHUD.m_Menu.SelectMenuItem( iSlot + 1 );  // slots are one off the key numbers
		return;
	}

	if ( iSlot > MAX_WEAPON_SLOTS )
		return;

	if ( gHUD.m_fPlayerDead || gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL ) )
		return;

	if (!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)) ))
		return;

	WEAPON *p = NULL;

	if ( (gpActiveSel == NULL) || (iSlot != gpActiveSel->iSlot) )
	{
		p = GetFirstPos( iSlot );		
	}
	else
	{
		if ( gpActiveSel )
			p = GetNextActivePos( gpActiveSel->iSlot, gpActiveSel->iSlotPos );
		if ( !p )
			p = GetFirstPos( iSlot );
	}

	if (p)
	{
		ServerCmd( p->szName );
		gpActiveSel = p;
		g_weaponselect = p->iId;
	}	
}

//------------------------------------------------------------------------
// Message Handlers
//------------------------------------------------------------------------
int CHudAttack::MsgFunc_WeapUpdate( const char *pszName, int iSize, void *pbuf )
{
	BEGIN_READ( pbuf, iSize );
	
	char iID = READ_CHAR();

	if(iID>0)
	{	WEAPON *p = gWR.GetWeapon(iID);
		gWR.PickupWeapon(p);
	}
	else
	{	//negative value: drop
		iID=-iID;
		WEAPON *p = gWR.GetWeapon(iID);		
		gWR.DropWeapon(p);
	}
	return 1;
}

// 
//  CurWeapon: Update hud state with the current weapon and clip count. Ammo
//  counts are updated with AmmoX. Server assures that the Weapon ammo type 
//  numbers match a real ammo type.
//
int CHudAttack::MsgFunc_CurWeapon(const char *pszName, int iSize, void *pbuf )
{
	static wrect_t nullrc;
	int fOnTarget = FALSE;

	BEGIN_READ( pbuf, iSize );

	int iState = READ_BYTE();
	int iId = READ_CHAR();
	int iClip = READ_CHAR();

	// detect if we're also on target
	if ( iState > 1 )
	{
		fOnTarget = TRUE;
	}

	if ( iId < 1 )
	{
		SetCrosshair(0, nullrc, 0, 0, 0);
		return 0;
	}

	// Is player dead???
	if ((iId == -1) && (iClip == -1))
	{
		gHUD.m_fPlayerDead = TRUE;
		gpActiveSel = NULL;
		return 1;
	}
	gHUD.m_fPlayerDead = FALSE;

	WEAPON *pWeapon = gWR.GetWeapon( iId );

	if ( !pWeapon )
	{	ConsolePrint("Invalid weapon id in CurWeapon\n");
		return 0;
	}

	if ( iState == 0 )	// we're not the current weapon, so update no more
		return 1;

	m_pWeapon = pWeapon;
	gpActiveSel = pWeapon;

	if ( !(gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL )) )
	{	// normal crosshairs
		SetCrosshair(m_pWeapon->hCrosshair, m_pWeapon->rcCrosshair, 255, 255, 255);
	}
	
	m_iFlags |= HUD_ACTIVE;
	
	return 1;
}

//
// WeaponList -- Tells the hud about a new weapon type.
//
int CHudAttack::MsgFunc_WeaponList(const char *pszName, int iSize, void *pbuf )
{
	BEGIN_READ( pbuf, iSize );
	
	WEAPON Weapon;

	strcpy( Weapon.szName, READ_STRING() );

	Weapon.iSlot = READ_CHAR();
	Weapon.iSlotPos = READ_CHAR();
	Weapon.iId = READ_CHAR();
	Weapon.iFlags = READ_BYTE();

	gWR.AddWeapon( &Weapon );

	//make sure we don't still hold this weapon
	gWR.DropWeapon(&Weapon);
	
	return 1;
}

//------------------------------------------------------------------------
// Command Handlers
//------------------------------------------------------------------------
// Slot button pressed
void CHudAttack::SlotInput( int iSlot )
{
	// Let the Viewport use it first, for menus
	if ( gViewPort && gViewPort->SlotInput( iSlot ) )
	{	return;
	}

	gWR.SelectSlot(iSlot, FALSE, 1);
}

void CHudAttack::UserCmd_Slot1(void)
{
	SlotInput( 0 );
}

void CHudAttack::UserCmd_Slot2(void)
{
	SlotInput( 1 );
}

void CHudAttack::UserCmd_Slot3(void)
{
	SlotInput( 2 );
}

void CHudAttack::UserCmd_Slot4(void)
{
	SlotInput( 3 );
}

void CHudAttack::UserCmd_Slot5(void)
{
	SlotInput( 4 );
}

void CHudAttack::UserCmd_Slot6(void)
{
	SlotInput( 5 );
}

void CHudAttack::UserCmd_Slot7(void)
{
	SlotInput( 6 );
}

void CHudAttack::UserCmd_Slot8(void)
{
	SlotInput( 7 );
}

void CHudAttack::UserCmd_Slot9(void)
{
	SlotInput( 8 );
}

void CHudAttack::UserCmd_Slot10(void)
{
	SlotInput( 9 );
}

// Selects the next item in the weapon menu
void CHudAttack::UserCmd_NextWeapon(void)
{
	if ( gHUD.m_fPlayerDead || (gHUD.m_iHideHUDDisplay & (HIDEHUD_WEAPONS | HIDEHUD_ALL)) )
		return;

	if ( !gpActiveSel )
		gpActiveSel = m_pWeapon;

	int pos = 0;
	int slot = 0;
	if ( gpActiveSel )
	{
		pos = gpActiveSel->iSlotPos + 1;
		slot = gpActiveSel->iSlot;
	}

	for ( int loop = 0; loop <= 1; loop++ )
	{
		for ( ; slot < MAX_WEAPON_SLOTS; slot++ )
		{
			for ( ; pos < MAX_WEAPON_POSITIONS; pos++ )
			{
				WEAPON *wsp = gWR.GetWeaponSlot( slot, pos );

				if ( wsp )
				{
					gpActiveSel = wsp;
					return;
				}
			}

			pos = 0;
		}

		slot = 0;  // start looking from the first slot again
	}

	//gpActiveSel = NULL;
}

// Selects the previous item in the menu
void CHudAttack::UserCmd_PrevWeapon(void)
{
	if ( gHUD.m_fPlayerDead || (gHUD.m_iHideHUDDisplay & (HIDEHUD_WEAPONS | HIDEHUD_ALL)) )
		return;

	if ( !gpActiveSel )
		gpActiveSel = m_pWeapon;
	
	int pos = MAX_WEAPON_POSITIONS-1;
	int slot = MAX_WEAPON_SLOTS-1;
	if ( gpActiveSel )
	{
		pos = gpActiveSel->iSlotPos - 1;
		slot = gpActiveSel->iSlot;
	}
	
	for ( int loop = 0; loop <= 1; loop++ )
	{
		for ( ; slot >= 0; slot-- )
		{
			for ( ; pos >= 0; pos-- )
			{
				WEAPON *wsp = gWR.GetWeaponSlot( slot, pos );

				if ( wsp )
				{
					gpActiveSel = wsp;
					return;
				}
			}

			pos = MAX_WEAPON_POSITIONS-1;
		}
		
		slot = MAX_WEAPON_SLOTS-1;
	}
}



//-------------------------------------------------------------------------
// Drawing code
//-------------------------------------------------------------------------

int CHudAttack::Draw(float flTime)
{	if (!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)) ))
		return 1;

	if ( (gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL )) )
		return 1;

	// Draw Weapon Menu
	DrawWList(flTime);
	return 1;
}

//
// Draw Weapon Menu
//
int CHudAttack::DrawWList(float flTime)
{	if ( !gpActiveSel )
		return 0;

	int iActiveSlot;

	if ( !gpActiveSel )
		iActiveSlot = -1;	// current slot has no weapons
	else 
		iActiveSlot = gpActiveSel->iSlot;

	// Ensure that there are available choices in the active slot
	if ( iActiveSlot > 0 )
	{
		if ( !gWR.GetFirstPos( iActiveSlot ) )
		{
			gpActiveSel = NULL;
			iActiveSlot = -1;
		}
	}
		
	WEAPON *p = gWR.GetFirstPos( iActiveSlot );
	int iWidth = 0;
	int iHeight = 0;
	if ( p )
	{	iWidth = p->rcActive.right - p->rcActive.left;
		iHeight = p->rcActive.bottom - p->rcActive.top;		
	}	

	int r,g,b;
	UnpackRGB( r,g,b, RGB_WHITEISH );

	for ( int iPos = 0; iPos < MAX_WEAPON_POSITIONS; iPos++ )
	{	p = gWR.GetWeaponSlot( iActiveSlot, iPos );
		if ( !p || !p->iId )
			continue;

		if ( gpActiveSel == p )
		{	SPR_Set(p->hActive, r, g, b );
			if (gpActiveSel != gpLastActiveSel)
			{	m_iActiveWeaponFrame = 0;
				m_fSecondAnimLoop = false;
				gpLastActiveSel = gpActiveSel;
			}
			int x = gHUD.m_SensuBean.m_iWidth + gHUD.m_Transform.m_iWidth;
			//int y = ScreenHeight - iHeight * 3;
			int y = ScreenHeight - iHeight * 4;
			SPR_Draw(m_iActiveWeaponFrame, x, y, &p->rcActive);
					
			//limit to 1 frames/0.1sec
			if( !m_fSecondAnimLoop || m_iActiveWeaponFrame < SPR_Frames(p->hActive)/2)
			{	if(flTime < m_flLastWeaponDraw || flTime - m_flLastWeaponDraw > 0.1)
				{	m_iActiveWeaponFrame++;
					m_flLastWeaponDraw = flTime;
				}
			}

			//make it cycle
			if (m_iActiveWeaponFrame >= SPR_Frames(p->hActive) )
			{	m_fSecondAnimLoop = true;
				m_iActiveWeaponFrame = 0;
			}
		}/*
		else
		{
			for ( int iPos = 0; iPos < MAX_WEAPON_POSITIONS; iPos++ )
			{
				WEAPON *p = gWR.GetWeaponSlot( iActiveSlot, iPos );				
				if ( !p || !p->iId )
					continue;								
			}			
		}*/
	}
	return 1;
}


void CHudAttack::UserCmd_Close(void)
{
	ClientCmd("escape");
}

/* =================================
	GetSpriteList

Finds and returns the matching 
sprite name 'psz' and resolution 'iRes'
in the given sprite list 'pList'
iCount is the number of items in the pList
================================= */
client_sprite_t *GetSpriteList(client_sprite_t *pList, const char *psz, int iRes, int iCount)
{
	if (!pList)
		return NULL;

	int i = iCount;
	client_sprite_t *p = pList;

	while(i--)
	{
		if ((!strcmp(psz, p->szName)) && (p->iRes == iRes))
			return p;
		p++;
	}

	return NULL;
}

int CHudAttack::MsgFunc_HideWeapon( const char *pszName, int iSize, void *pbuf )
{
	BEGIN_READ( pbuf, iSize );
	
	gHUD.m_iHideHUDDisplay = READ_BYTE();

	if (gEngfuncs.IsSpectateOnly())
		return 1;

	if ( gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL ) )
	{
		static wrect_t nullrc;
		gpActiveSel = NULL;
		SetCrosshair( 0, nullrc, 0, 0, 0 );
	}
	else
	{
		if ( m_pWeapon )
			SetCrosshair( m_pWeapon->hCrosshair, m_pWeapon->rcCrosshair, 255, 255, 255 );
	}

	return 1;
}
