/* 
	TODO: finalize item support

	1) Make ItemSelectUp() work.
	2) Change cg.itemSelect to whatever var is used to store selected item.
	3) Make sure commands in itemCommands work in both multi & single player.
*/

#ifdef _JK2MP
#include "../../codemp/client/client.h"
#include "../../codemp/cgame/cg_local.h"
#else
#include "client.h"
#include "../cgame/cg_local.h"
#endif

#include "cl_input_hotswap.h"


#ifdef _JK2MP
#define FORCESELECTTIME forceSelectTime
#define FORCESELECT		forceSelect
#define INVSELECTTIME	invenSelectTime
#define INVSELECT		itemSelect
#define REGISTERSOUND	S_RegisterSound
#define STARTSOUND		S_StartLocalSound
#define WEAPONBINDSTR	"weaponclean"
#else
#define FORCESELECTTIME forcepowerSelectTime
#define FORCESELECT		forcepowerSelect
#define INVSELECTTIME	inventorySelectTime
#define INVSELECT		inventorySelect
#define REGISTERSOUND	cgi_S_RegisterSound
#define STARTSOUND		cgi_S_StartLocalSound
#define WEAPONBINDSTR	"weapon"
#endif

#define BIND_TIME 3000 //number of milliseconds button is held before binding
#define EXEC_TIME 500  //max ms button can be held to execute in bind mode


#ifdef _JK2MP
const char *itemCommands[HI_NUM_HOLDABLE] = {
	NULL,						//HI_NONE
	"use_seeker\n",
	"use_field\n",
	"use_bacta\n",
	"use_bactabig\n",
	"use_electrobinoculars\n",
	"use_sentry\n",
	"use_jetpack\n",
	NULL,						//ammo dispenser
	NULL,						//health dispenser
	"use_eweb\n",
	"use_cloak\n",
};
#else
const char *itemCommands[INV_MAX] = {
	"use_electrobinoculars\n",
	"use_bacta\n",
	"use_seeker\n",
	"use_goggles\n",
	"use_sentry\n",
	NULL,						//goodie key
	NULL,						//security key
};
#endif



HotSwapManager::HotSwapManager(int uniqueID) :
	uniqueID(uniqueID),
	forceBound(false)
{
	Reset();
}


char *HotSwapManager::GetBinding(void)
{
	char buf[64];

	sprintf(buf, "hotswap%d", uniqueID);
	cvar_t *cvar = Cvar_Get(buf, "", CVAR_ARCHIVE);

	if(cvar && cvar->string[0] != 0) {
		return cvar->string;
	} else {
		return NULL;
	}
}


void HotSwapManager::Bind(void)
{
	forceBound = false;

	if(WeaponSelectUp()) {
		HotSwapBind(uniqueID, HOTSWAP_CAT_WEAPON, cg.weaponSelect);
	} else if(ForceSelectUp()) {
		forceBound = true;
		HotSwapBind(uniqueID, HOTSWAP_CAT_FORCE, 
#ifdef _JK2MP
				cg.FORCESELECT
#else
				showPowers[cg.FORCESELECT]
#endif
				);
	} else if(ItemSelectUp()) {
		HotSwapBind(uniqueID, HOTSWAP_CAT_ITEM, cg.INVSELECT);
	} else{
		assert(0);
	}

	noExec = true;
	noBind = true;
	STARTSOUND(REGISTERSOUND("sound/interface/update"), 0);
}


bool HotSwapManager::ForceSelectUp(void)
{
	return cg.FORCESELECTTIME != 0 &&
		(cg.FORCESELECTTIME + WEAPON_SELECT_TIME >= cg.time);
}


bool HotSwapManager::WeaponSelectUp(void)
{
	return cg.weaponSelectTime != 0 &&
		(cg.weaponSelectTime + WEAPON_SELECT_TIME >= cg.time);
}


bool HotSwapManager::ItemSelectUp(void)
{
	return cg.INVSELECTTIME != 0 &&
		(cg.INVSELECTTIME + WEAPON_SELECT_TIME >= cg.time);
}


bool HotSwapManager::HUDInBindState(void)
{
	return ForceSelectUp() || WeaponSelectUp() || ItemSelectUp();
}


void HotSwapManager::Update(void)
{
	if(down) {
		//Increment bindTime only if HUD is in select mode.
		if(HUDInBindState()) {
			bindTime += cls.frametime;
		} else {

			//Clear bind time.
			bindTime = 0;

			//If a force power is bound, want to execute whenever the button
			//is down to handle powers which can be held.
			if(forceBound) {
				Execute();
			}
		}
		downTime += cls.frametime;
	}

	//Down long enough, bind button.
	if(!noBind && bindTime >= BIND_TIME) {
		Bind();
	}
}


void HotSwapManager::Execute(void)
{
	char *binding = GetBinding();
	if(binding && !noExec) {
		if(!forceBound) {
			noExec = true;
		}
		Cbuf_ExecuteText(EXEC_APPEND, binding);
	}
}


void HotSwapManager::SetDown(void)
{
	//Set the down flag.
	down = true;

	//Execute the bind if the HUD isn't up.
	if(!HUDInBindState()) {
		Execute();
	}
}


void HotSwapManager::SetUp(void)
{
	//Execute the bind if the button was held down for long enough.
	if(downTime <= EXEC_TIME) {
		Execute();
	}

	Reset();
}


void HotSwapManager::Reset(void)
{
	down = false;
	downTime = 0;
	bindTime = 0;
	noExec = false;
	noBind = false;
}


static void HotSwapBind(const char *uniqueID, const char *value)
{
	Cvar_Set(uniqueID, value);
}


void HotSwapBind(int buttonID, int category, int value)
{
	char uniqueID[64];
	sprintf(uniqueID, "hotswap%d", buttonID);

	switch(category) {
	case HOTSWAP_CAT_WEAPON:
		HotSwapBind(uniqueID, va("%s %d\n", WEAPONBINDSTR, value));
		break;
	case HOTSWAP_CAT_ITEM:
		assert(itemCommands[value]);
		HotSwapBind(uniqueID, itemCommands[value]);
		break;
	case HOTSWAP_CAT_FORCE:
		HotSwapBind(uniqueID, va("useGivenForce %d\n", value));
		break;
	default:
		assert(0);
	}
}

