
#include "standard.h"
#include "global.h"
#include "winmain.h"
#include "template.h"
#include "di.h"



/*** globals ***/
defglobal List<Joystick> G_JoystickList;
defglobal Joystick G_Joystick;

/*** locals ***/
local IDirectInput* L_DirectInput=0;
local IDirectInputDevice* L_Keyboard=0;
local IDirectInputDevice* L_Joystick=0;

/*** code ***/
bool DI_Create()
	{
	Log(LT_Enter,"DI_Create");
	if (DX_TRY(DirectInputCreate(App.hInstance,DIRECTINPUT_VERSION,&L_DirectInput,0)))
		{
		Log(LT_Error,"DirectInputCreate failed");
		return false;
		}
	return true;
	}

void DI_Release()
	{
	Log(LT_Enter,"DI_Release");
	RELEASE(L_DirectInput);
	}

local BOOL CALLBACK DI_EnumJoystickCallback(const DIDEVICEINSTANCE* pDevInst,void* pContext)
	{
	Log(LT_Enter,"DI_EnumJoystickCallback: %s (%s)",pDevInst->tszProductName,pDevInst->tszInstanceName);
	List<Joystick>* pList=static_cast<List<Joystick>*>(pContext);
	Joystick& joystick=pList->NewTail();
	joystick.SetGUID(&(pDevInst->guidInstance));
	strcpy(joystick.strDescription,pDevInst->tszProductName);
	strcpy(joystick.strName,pDevInst->tszInstanceName);
	return DIENUM_CONTINUE;
	}

local bool DI_EnumerateJoysticks(List<Joystick>& rDeviceList)
	{
	Log(LT_Enter,"DI_EnumerateJoysticks");
	if (DX_TRY(L_DirectInput->EnumDevices(DIDEVTYPE_JOYSTICK,DI_EnumJoystickCallback,&rDeviceList,DIEDFL_ATTACHEDONLY)))
		{
		Log(LT_Error,"EnumDevices failed");
		return false;
		}
	return true;
	}

local bool DI_MakeAdapterList()
	{
	Log(LT_Enter,"DI_MakeAdapterList");
	G_JoystickList.RemoveAll();
	if (!DI_Create())
		{
		Log(LT_Error,"Couldn't create DirectInput");
		return false;
		}
	bool tResult=DI_EnumerateJoysticks(G_JoystickList);
	DI_Release();
	return tResult;
	}

void DI_ReadKeyboard(unsigned char *keymap)
	{
	HRESULT err;
	// Get Direct Input keyboard input (reaquire if focus lost for some reason)
	err=L_Keyboard->GetDeviceState(256,keymap);
	if (FAILED(err))
		{
		if (err==DIERR_INPUTLOST)
			{
			if (DX_TRY(L_Keyboard->Acquire()) || DX_TRY(L_Keyboard->GetDeviceState(256,keymap)))
				{
				Log(LT_Warning,"Can't re-aquire keyboard; Clearing keyboard data (dodgy)");
				memset(keymap,0,256);	// this is real dodgy.  function should return an error value if keyboard data is invalid.
				}
			else
				Log(LT_Info,"Re-aquired keyboard");
			}
		else
			DX_TRY(err);
		}
	}

int DI_ReadJoystick(int& joy_x,int& joy_y)
	{
	static int no_joy=1;
	static JOYCAPS joystick;
	JOYINFOEX jinfo;
	

	
	// joystick can be disabled
	if (App.tJoystickEnabled == false)
	{
		joy_x=joy_y=0;
		return 0;
	}

	jinfo.dwSize=sizeof(JOYINFOEX);
	jinfo.dwFlags=JOY_RETURNBUTTONS|JOY_RETURNX|JOY_RETURNY;
//	jinfo.dwFlags=JOY_RETURNBUTTONS|JOY_RETURNR|JOY_RETURNV;
	if (joyGetPosEx(JOYSTICKID1,&jinfo)!=JOYERR_NOERROR)
		{	// No joystick available
		no_joy=1;
		joy_x=joy_y=0;
		return 0;
		}
	else if (no_joy)
		{	// Joystick just became available? Get capabilities
		if (joyGetDevCaps(JOYSTICKID1,&joystick,sizeof(joystick))==JOYERR_NOERROR)
			no_joy = 0;
		else
			{
			joy_x=joy_y=0;
			return 0;
			}
		}
	joy_x=((int)jinfo.dwXpos*32)/(joystick.wXmax-joystick.wXmin)-16;
	joy_y=((int)jinfo.dwYpos*32)/(joystick.wYmax-joystick.wYmin)-16;
//	joy_x=((int)(jinfo.dwVpos^0xffff)*32)/(joystick.wVmax-joystick.wVmin)-16;
//	joy_y=((int)jinfo.dwRpos*32)/(joystick.wRmax-joystick.wRmin)-16;

	
	  
		return jinfo.dwButtons;
	}

bool DI_Init()
	{
	Log(LT_Enter,"DI_Init");
	return DI_MakeAdapterList();
	}

void DI_Cleanup()
	{
	Log(LT_Enter,"DI_Cleanup");
	}

Position DI_FindPreferredJoystick(GUID* pGUID)
	{
	Position found=0;
	
	  Log(LT_Enter,"DI_FindPreferredJoystick");
	if (G_JoystickList.GetCount()==0)
		return false;
	if (pGUID)
		for_all(p,G_JoystickList)
			{
			Joystick& joystick=G_JoystickList[p];
			if (*pGUID==joystick.AdapterGUID)
				{
				found=p;
				break;
				}
			}
	if (!found)
		found=G_JoystickList.GetHead();
	Joystick& joystick=G_JoystickList[found];
	Log(LT_Info,"Using joystick: %s (%s)",static_cast<char*>(joystick.strDescription),
											static_cast<char*>(joystick.strName));
	
	  return found;
	}

void DI_StartKeyboard()
	{
	Log(LT_Enter,"DI_StartKeyboard");
	if (DX_TRY(L_DirectInput->CreateDevice(GUID_SysKeyboard,&L_Keyboard,0)))
		throw INIT_ERR_CantCreateKeyboardDevice;
	if (DX_TRY(L_Keyboard->SetCooperativeLevel(App.WindowHandle/*RF*/,DISCL_FOREGROUND|DISCL_NONEXCLUSIVE)))	// DISCL_EXCLUSIVE is not available to keyboard devices
		throw INIT_ERR_CantSetKBCooperativeLevel;
	if (DX_TRY(L_Keyboard->SetDataFormat(&c_dfDIKeyboard)))	// Set data format to 256 bytes of keyboard info using handy global defined in 'dinput.h'
		throw INIT_ERR_CantSetKBDataFormat;
	if (DX_TRY(L_Keyboard->Acquire()))						// Acquire the keyboard now, and don't release it until shutdown
		throw INIT_ERR_CantAcquireKeyboard;
	}

void DI_FinishKeyboard()
	{
	Log(LT_Enter,"DI_FinishKeyboard");
	if (L_Keyboard)
		L_Keyboard->Unacquire();
	RELEASE(L_Keyboard);
	}

bool DI_StartJoystick()
	{

	Log(LT_Enter,"DI_StartJoystick");
	if (!App.Joystick)
		return true;

	//G_Joystick=G_JoystickList[App.Joystick]; //rf


#if 0	// still using joyGetPosEx
	if (DX_TRY(L_DirectInput->CreateDevice(G_Joystick.AdapterGUID,&L_Joystick,0)))
		{
		Log(LT_Error,"Can't create joystick device");
		return false;
		}
	if (DX_TRY(L_Joystick->SetDataFormat(&c_dfDIJoystick)))
		{
		Log(LT_Error,"Can't set joystick data format");
		return false;
		}
	if (DX_TRY(L_Joystick->SetCooperativeLevel(GhWnd,DISCL_NONEXCLUSIVE|DISCL_FOREGROUND)))
		{
		Log(LT_Error,"Can't set joystick cooperativelevel");
		return false;
		}
	
	// todo: set properties...

	if (DX_TRY(L_Joystick->Acquire()))
		{
		Log(LT_Error,"Can't acquire joystick");
		return false;
		}
#endif

	return true;
	}

void DI_FinishJoystick()
	{
	Log(LT_Enter,"DI_FinishJoystick");
#if 0
	if (L_Joystick)
		DX_TRY(L_Joystick->Unacquire());
	RELEASE(L_Joystick);
#endif
	}

void DI_Start()
	{
	Log(LT_Enter,"DI_Start");
	if (!DI_Create())
		throw INIT_ERR_CantCreateDirectInput;
	DI_StartKeyboard();
	DI_StartJoystick();
	}

void DI_Finish()
	{
	Log(LT_Enter,"DI_Finish");
	DI_FinishJoystick();
	DI_FinishKeyboard();
	DI_Release();
	}

void DI_SetupJoystick(HWND hWnd)
{
	if (L_DirectInput)
		DX_TRY(L_DirectInput->RunControlPanel(hWnd, 0));
}