/*
	WinSTon

	DirectInput Joystick routines

	Some modern joysticks do not seem to function under standard Windows calls. So, we prefer to
	use DirectInput as a means for reading joystick movements. As most versions of NT do not
	support DirectInput we revert back to the older method should this fail
*/

#include "..\includes\winston.h"
#include "..\includes\debug.h"
#include "..\includes\errlog.h"
#include "..\includes\joy.h"
#include "..\includes\memory.h"

BOOL bDisableDirectInput=FALSE;
LPDIRECTINPUT2 g_lpDI = NULL;
LPDIRECTINPUTDEVICE2 g_lpDIDevice[2] = { NULL,NULL };
int DirectJoystickID = 1;
BOOL bAllowDirectInput = FALSE;

HRESULT CALLBACK DJoy_EnumInputDeviceCallback(LPDIDEVICEINSTANCE pdInst, LPVOID pvRef);

//-----------------------------------------------------------------------
/*
	Initialise DirectInput for reading of joysticks, return FALSE if fails
*/
BOOL DJoy_Init(void)
{
	// Is enabled?
	if (bDisableDirectInput) {
		// Stop any Direct Input access
		ErrLog_File("DirectInput: Disabled\n");
		bAllowDirectInput = FALSE;
		return(FALSE);
	}

	// Initialise Direct Input
	if (DirectInputCreate(hInst, DIRECTINPUT_VERSION, (LPDIRECTINPUTA *)&g_lpDI, NULL)!=DI_OK) {
		// DirectInput not available
		ErrLog_File("ERROR DirectInput not found\n");
	}
	else {
		// Report as OK
		ErrLog_File("DirectInput: 'DirectInputCreate' - OK\n");
		// Enumerate devices
		g_lpDI->EnumDevices(DIDEVTYPE_JOYSTICK, (LPDIENUMDEVICESCALLBACK)DJoy_EnumInputDeviceCallback, g_lpDI, DIEDFL_ATTACHEDONLY);

		// Did manage to find a joystick?
		if (bJoystickWorking[0] || bJoystickWorking[1]) {
			bAllowDirectInput = TRUE;
			return(TRUE);
		}
		else
			ErrLog_File("ERROR: DirectInput no joysticks detected\n");
	}

	bAllowDirectInput = FALSE;
	return(FALSE);
}

//-----------------------------------------------------------------------
/*
	Uninitialise DirectInput
*/
void DJoy_UnInit(void)
{
	int i;

	// Free DirectInput device and interface
	for(i=0; i<2; i++) {
		if (g_lpDIDevice[i]) {
			g_lpDIDevice[i]->Unacquire(); 
			g_lpDIDevice[i]->Release();	g_lpDIDevice[i] = NULL; 
		}
	}

	if (g_lpDI) {
		g_lpDI->Release();
	}
}

//-----------------------------------------------------------------------
/*
	Set joystick into neutral position (centre XY, no buttons)
*/
void DJoy_ForceNeutralStick(JOYREADING *pJoyReading)
{
	// Set details
	pJoyReading->XPos = 32768;
	pJoyReading->YPos = 32768;
	pJoyReading->Buttons = 0;
}

//-----------------------------------------------------------------------
/*
	DirectInput Callback
*/
HRESULT CALLBACK DJoy_EnumInputDeviceCallback(LPDIDEVICEINSTANCE pdInst, LPVOID pvRef)
{ 
	DIPROPRANGE DIprg;
	DIPROPDWORD DIpdw;

	// Create the DirectInput joystick device 
	if (g_lpDI->CreateDevice(pdInst->guidInstance, (LPDIRECTINPUTDEVICEA *)&g_lpDIDevice[DirectJoystickID], NULL)!=DI_OK) {
		ErrLog_File("ERROR DirectInput CreateDevice failed\n");
		return DIENUM_CONTINUE;
	}

	// Set format of data
	if (g_lpDIDevice[DirectJoystickID]->SetDataFormat(&c_dfDIJoystick)!=DI_OK) {
		ErrLog_File("ERROR DirectInput SetDataFormat failed\n");
		g_lpDIDevice[DirectJoystickID]->Release();	g_lpDIDevice[DirectJoystickID] = NULL;
		return DIENUM_CONTINUE;
	} 

	// Set cooperative level of joystick
	if(g_lpDIDevice[DirectJoystickID]->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND)!=DI_OK) {
		ErrLog_File("ERROR DirectInput SetCooperativeLevel failed\n");
		g_lpDIDevice[DirectJoystickID]->Release();	g_lpDIDevice[DirectJoystickID] = NULL;
		return DIENUM_CONTINUE;
	} 

	// Set joystick range in XY
	Memory_Clear(&DIprg,sizeof(DIprg));
	DIprg.diph.dwSize       = sizeof(DIprg);
	DIprg.diph.dwHeaderSize = sizeof(DIprg.diph);
	DIprg.diph.dwHow        = DIPH_DEVICE;
	DIprg.lMin              = -32768;
	DIprg.lMax              = 32768;
	if (g_lpDIDevice[DirectJoystickID]->SetProperty(DIPROP_RANGE, &DIprg.diph) != DI_OK) {
		ErrLog_File("ERROR DirectInput SetProperty DIPROP_RANGE failed\n");
		g_lpDIDevice[DirectJoystickID]->Release();	g_lpDIDevice[DirectJoystickID] = NULL;
		return DIENUM_CONTINUE;
	} 

	// Set no dead zone
	Memory_Clear(&DIpdw,sizeof(DIpdw));
    DIpdw.diph.dwSize       = sizeof(DIPROPDWORD);
    DIpdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
    DIpdw.diph.dwHow        = DIPH_DEVICE;
    DIpdw.dwData            = 0;
	if (g_lpDIDevice[DirectJoystickID]->SetProperty(DIPROP_DEADZONE, &DIpdw.diph) != DI_OK) {
		ErrLog_File("ERROR DirectInput SetProperty DIPROP_DEADZONE failed\n");
		g_lpDIDevice[DirectJoystickID]->Release();	g_lpDIDevice[DirectJoystickID] = NULL;
		return DIENUM_CONTINUE;
	} 

    // Start using the joystick
	if (g_lpDIDevice[DirectJoystickID]->Acquire()!=DI_OK) {
		ErrLog_File("ERROR DirectInput Acquire failed\n");
		g_lpDIDevice[DirectJoystickID]->Release();	g_lpDIDevice[DirectJoystickID] = NULL;
		return DIENUM_CONTINUE;
	} 

	// Signal we've got joystick
	ErrLog_File("DirectInput: 'InitJoystick %d' - OK\n",DirectJoystickID);
	bJoystickWorking[DirectJoystickID] = TRUE;
	DirectJoystickID--;

	// Have tried to find two joysticks? Stop if have both
	if (DirectJoystickID>=0)
		return DIENUM_CONTINUE;
	
	return DIENUM_STOP;
}

//-----------------------------------------------------------------------
/*
	Read joystick, return TRUE if all OK

	We notice that a joystick may loose input, that is Poll() fails. We need to 'Acquire' the
	input again when this happens - this is normal running. But, if we detect a run of
	consecutive errors we must assume something is wrong and disable the joystick.
*/
BOOL DJoy_ReadJoystick(int JoystickID,JOYREADING *pJoyReading)
{
	DIJOYSTATE DIjs;
	HRESULT hres;

	// DirectInput OK?
	if (!bAllowDirectInput)
		return(FALSE);

	if (bJoystickWorking[JoystickID]) {
		// Read joystick, NOTE Poll may fail if loose 'aquire' so try again
		if (g_lpDIDevice[JoystickID]) {
			hres = g_lpDIDevice[JoystickID]->Poll();

			if (hres==DI_OK)
				hres = g_lpDIDevice[JoystickID]->GetDeviceState(sizeof(DIjs),&DIjs);
		
			// So did manage to Poll and GetDeviceState?
			if (hres==DI_OK) {
				// Copy details
				pJoyReading->XPos = DIjs.lX+32768;
				pJoyReading->YPos = DIjs.lY+32768;
				pJoyReading->Buttons = 0;
				pJoyReading->Buttons |= (DIjs.rgbButtons[0]&0x80) ? JOY_BUTTON1:0;
				pJoyReading->Buttons |= (DIjs.rgbButtons[1]&0x80) ? JOY_BUTTON2:0;
			}
			else {
				// Looks like we've lost aquire, try again
				g_lpDIDevice[JoystickID]->Acquire();

				// Set joystick in neutral position
				DJoy_ForceNeutralStick(pJoyReading);
			}
		}

		return(TRUE);
	}

	return(FALSE);
}
