#include "mungal4.h"
#pragma hdrstop

#include "l4dinput.h"

#define	DI_TRIGGER	0
#define DI_THUMB1	2
#define	DI_THUMB2	1
#define DI_THUMB3	3

DIJoystick::DIJoystick(HINSTANCE hInstance, HWND hWnd)
{
	mDI = NULL;
	mJoystick = NULL;

	if (SUCCEEDED(DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&mDI, NULL)))
	{
		mDI->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, this, DIEDFL_ATTACHEDONLY);
		if (mJoystick)
			if (SUCCEEDED(mJoystick->SetDataFormat(&c_dfDIJoystick2)))
				if (SUCCEEDED(mJoystick->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE | DISCL_BACKGROUND)))
					if (SUCCEEDED(mJoystick->EnumObjects(EnumAxesCallback, this, DIDFT_AXIS)))
						return;
	}

	if (mDI)
	{
		mDI->Release();
		mDI = NULL;
	}
	if (mJoystick)
	{
		mJoystick->Release();
		mJoystick = NULL;
	}
}

DIJoystick::~DIJoystick()
{
	if (mJoystick)
	{
		mJoystick->Unacquire();
		mJoystick->Release();
		mJoystick = NULL;
	}

	if (mDI)
	{
		mDI->Release();
		mDI = NULL;
	}
}

BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext)
{
	DIJoystick *joystick = (DIJoystick*)pContext;
	if (FAILED(joystick->mDI->CreateDevice(pdidInstance->guidInstance, &joystick->mJoystick, NULL)))
		return DIENUM_CONTINUE;

	return DIENUM_STOP;
}

BOOL CALLBACK EnumAxesCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef)
{
	DIJoystick *joystick = (DIJoystick*)pvRef;
	DIPROPRANGE diprg;
	diprg.diph.dwSize = sizeof(DIPROPRANGE);
	diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
	diprg.diph.dwHow = DIPH_BYID;
	diprg.diph.dwObj = lpddoi->dwType;
	diprg.lMax = diprg.lMax = 0;

	if (lpddoi->guidType == GUID_XAxis)
	{
		if (SUCCEEDED(joystick->mJoystick->GetProperty(DIPROP_RANGE, &diprg.diph)))
		{
			joystick->xAxisMin = diprg.lMin;
			joystick->xAxisMax = diprg.lMax;
		}
	}
	else if (lpddoi->guidType == GUID_YAxis)
	{
		if (SUCCEEDED(joystick->mJoystick->GetProperty(DIPROP_RANGE, &diprg.diph)))
		{
			joystick->yAxisMin = diprg.lMin;
			joystick->yAxisMax = diprg.lMax;
		}
	}
	else if (lpddoi->guidType == GUID_ZAxis)
	{
		if (SUCCEEDED(joystick->mJoystick->GetProperty(DIPROP_RANGE, &diprg.diph)))
		{
			joystick->zAxisMin = diprg.lMin;
			joystick->zAxisMax = diprg.lMax;
		}
	}
	else if (lpddoi->guidType == GUID_RxAxis)
	{
	}
	else if (lpddoi->guidType == GUID_RyAxis)
	{
	}
	else if (lpddoi->guidType == GUID_RzAxis)
	{
		if (SUCCEEDED(joystick->mJoystick->GetProperty(DIPROP_RANGE, &diprg.diph)))
		{
			joystick->rzAxisMin = diprg.lMin;
			joystick->rzAxisMax = diprg.lMax;
		}
	}
	else if (lpddoi->guidType == GUID_Slider)
	{
		if (SUCCEEDED(joystick->mJoystick->GetProperty(DIPROP_RANGE, &diprg.diph)))
		{
			joystick->zAxisMin = diprg.lMin;
			joystick->zAxisMax = diprg.lMax;
		}
	}

	return DIENUM_CONTINUE;
}

bool DIJoystick::Poll()
{
	HRESULT hr;
	if (mJoystick)
	{
		hr = mJoystick->Poll();
		if (FAILED(hr))
		{
			DEBUG_STREAM << "Poll failed: HR = " << hr << std::endl << std::flush;
			hr = mJoystick->Acquire();
			while (hr == DIERR_INPUTLOST)
			{
				DEBUG_STREAM << "Acquire failed: HR = " << hr << std::endl << std::flush;
				hr = mJoystick->Acquire();
			}
			return false;
		}

		if (FAILED(hr = mJoystick->GetDeviceState(sizeof(DIJOYSTATE2), &mJoystate)))
		{
			DEBUG_STREAM << "GetState failed: HR = " << hr << std::endl << std::flush;
			return false;
		}
	}

	return true;
}

void DIJoystick::Update()
{
	if (Poll())
	{
		// Handle analog data
		Value.x = ((Scalar)(mJoystate.lX - xAxisMin) / (Scalar)(xAxisMax - xAxisMin)) * 2.0f - 1.0f;
		Value.y = ((Scalar)(mJoystate.lY - yAxisMin) / (Scalar)(yAxisMax - yAxisMin)) * 2.0f - 1.0f;
		LONG z = (mJoystate.lZ == 0 ? mJoystate.rglSlider[0] : mJoystate.lZ);
		ExtendedValue.y = 1.0f - (Scalar)(z - zAxisMin) / (Scalar)(zAxisMax - zAxisMin);
		// full left rotation = 0, center rotation = 1/2 range, full right = full range
		LONG rot = (Scalar)(mJoystate.lRz - rzAxisMin);
		Scalar half = (Scalar)(rzAxisMax - rzAxisMin) / 2.0f;
		Rotation.x = (Scalar)0;
		Rotation.y = (Scalar)0;
		if (rot <= half)
			Rotation.x = ((half - rot) / half) * 470.0;
		else
			Rotation.y = ((rot - half) / half) * 470.0;
		
		// Handle button changes
		int newButtons = 0;
		if (mJoystate.rgbButtons[DI_TRIGGER])
			newButtons |= Joystick::Trigger;
		if (mJoystate.rgbButtons[DI_THUMB1])
			newButtons |= Joystick::Thumb1;
		if (mJoystate.rgbButtons[DI_THUMB2])
			newButtons |= Joystick::Thumb2;
		if (mJoystate.rgbButtons[DI_THUMB3])
			newButtons |= Joystick::Thumb3;
		if (mJoystate.rgdwPOV[0] == 0)
			newButtons |= Joystick::HatUp;
		if (mJoystate.rgdwPOV[0] == 9000)
			newButtons |= Joystick::HatRight;
		if (mJoystate.rgdwPOV[0] == 18000)
			newButtons |= Joystick::HatDown;
		if (mJoystate.rgdwPOV[0] == 27000)
			newButtons |= Joystick::HatLeft;

		ButtonPressed = (oldButtons ^ newButtons) & newButtons;
		ButtonReleased = (oldButtons ^ newButtons) & oldButtons;
		oldButtons = newButtons;
	}
}

