#include "../interface.h"

#pragma comment(lib,"dinput8")
#pragma comment(lib,"dxguid")

DInput::DInput(_System* _sys)
:
_sys(_sys)
{
	din = 0;
	din_key = din_mouse = 0;
	din_joy_count = 0;
	m_acquired = false;

	for (unsigned i=0; i < Input::Joypad::count; i++) {
		din_joy[i] = 0;
	}
}

void DInput::init()
{
	term();

	if (FAILED(DirectInput8Create(GetModuleHandle(0), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&din, 0))) {
		throw;
	}

	din->CreateDevice(GUID_SysKeyboard, &din_key, 0);

	din_key->SetDataFormat(&c_dfDIKeyboard);
	din_key->SetCooperativeLevel(_sys->getHwnd(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
	din_key->Acquire();

	din->CreateDevice(GUID_SysMouse, &din_mouse, 0);

	din_mouse->SetDataFormat(&c_dfDIMouse2);
	din_mouse->SetCooperativeLevel(_sys->getHwnd(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
	din_mouse->Acquire();

	din->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoypadsCallback, this, DIEDFL_ATTACHEDONLY);
}

BOOL CALLBACK DInput::EnumJoypadsCallback(const DIDEVICEINSTANCE *instance, void *pRef)
{
	DInput* ptr = (DInput*)pRef;
	return ptr->enum_joypads(instance);
}

bool DInput::enum_joypads(const DIDEVICEINSTANCE *instance)
{
	HRESULT hr = din->CreateDevice(instance->guidInstance, &din_joy[din_joy_count], 0);

	if(FAILED(hr)) return DIENUM_CONTINUE;

	device_name[din_joy_count] = new char[100];
	sprintf(device_name[din_joy_count] , "%s", instance->tszProductName);

	din_joy[din_joy_count]->SetDataFormat(&c_dfDIJoystick2);
	din_joy[din_joy_count]->SetCooperativeLevel(0, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);

	if(++din_joy_count >= Input::Joypad::count) return DIENUM_STOP;

	return DIENUM_CONTINUE;
}

bool DInput::m_acquire()
{
	if(!din_mouse) return false;
	if(!m_acquired) {
		din_mouse->Unacquire();
		din_mouse->SetCooperativeLevel(_sys->getHwnd(), DISCL_EXCLUSIVE | DISCL_FOREGROUND);
		din_mouse->Acquire();
		m_acquired = true;
	}
	return true;
}

bool DInput::m_unacquire()
{
	if(!din_mouse) return false;
	if(m_acquired) {
		din_mouse->Unacquire();
		din_mouse->SetCooperativeLevel(_sys->getHwnd(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
		din_mouse->Acquire();
		m_acquired = false;
	}
	return true;
}


void DInput::poll(i16* i_map)
{
	memset(i_map, 0, Input::ElementCounter * sizeof(i16));

	if(din_key) {

		u8 keystate[Input::Keyboard::elements];

		if(FAILED( din_key -> GetDeviceState(Input::Keyboard::elements, (LPVOID)keystate))) {
			din_key -> Acquire();
			if(FAILED( din_key -> GetDeviceState(Input::Keyboard::elements, (LPVOID)keystate))) {
				memset(keystate, 0, sizeof(keystate));
			}
		}

		for(unsigned i=0; i < sizeof(keystate); i++) {
			*(i_map + i) = !!(keystate[i] & 0x80);
		}
	}

	i_map += Input::Keyboard::elements;

	if (din_mouse) {

		DIMOUSESTATE2 m_state;

		if(FAILED( din_mouse -> GetDeviceState(sizeof(DIMOUSESTATE2), (void*)&m_state))) {
			din_mouse -> Acquire();
			if(FAILED( din_mouse -> GetDeviceState(sizeof(DIMOUSESTATE2), (void*)&m_state))) {
				memset(&m_state, 0, sizeof(DIMOUSESTATE2));
			}
		}

		i_map[Input::Mouse::x] = m_state.lX;
		i_map[Input::Mouse::y] = m_state.lY;
		i_map[Input::Mouse::z] = m_state.lZ / WHEEL_DELTA;

		for(unsigned i = 0; i < Input::Mouse::buttons; i++) {
			i_map[Input::Mouse::button + i] = (bool)m_state.rgbButtons[i];
		}
	}

	i_map += Input::Mouse::elements;

	DIJOYSTATE2 js;

	for(int i = 0; i < din_joy_count; i++) {
		if(!din_joy[i]) continue;

		memset(js.rgbButtons, 0, Input::Joypad::buttons);

		if(FAILED( din_joy[i] -> Poll())) {
			din_joy[i]->Acquire();
			if(FAILED( din_joy[i]->Poll())) {
				continue;
			}
		}

		din_joy[i] -> GetDeviceState(sizeof(DIJOYSTATE2), &js);

		i16* addr = i_map + i * Input::Joypad::elements;

		unsigned x;

		int resistance_lo = 0x7fff - 28672;
		int resistance_hi = 0x8000 + 28672;

		*(addr + Input::Joypad::hat_up) = (js.lY <= resistance_lo);
		*(addr + Input::Joypad::hat_right) = (js.lX >= resistance_hi);
		*(addr + Input::Joypad::hat_down) = (js.lY >= resistance_hi);
		*(addr + Input::Joypad::hat_left) = (js.lX <= resistance_lo);
	 

		for(int n = 0; n < Input::Joypad::hats; n++) {
			unsigned pov = js.rgdwPOV[n];
			if(pov < 36000) {
				x = n * Input::Joypad::hat_directions;
				if(pov >= 31500 || pov <=  4500) *(addr + Input::Joypad::hat_up + x) = true;
				if(pov >=  4500 && pov <= 13500) *(addr + Input::Joypad::hat_right + x) = true;
				if(pov >= 13500 && pov <= 22500) *(addr + Input::Joypad::hat_down + x) = true;
				if(pov >= 22500 && pov <= 31500) *(addr + Input::Joypad::hat_left + x) = true;
			}
		}

		*(addr + Input::Joypad::axis + 0) = js.lX - 32768;
		*(addr + Input::Joypad::axis + 1) = js.lY - 32768;
		*(addr + Input::Joypad::axis + 2) = js.lZ - 32768;
		*(addr + Input::Joypad::axis + 3) = js.lRx - 32768;
		*(addr + Input::Joypad::axis + 4) = js.lRy - 32768;
		*(addr + Input::Joypad::axis + 5) = js.lRz - 32768;

		for(unsigned b = 0; b < Input::Joypad::buttons; b++) {
			*(addr + Input::Joypad::button + b) = (bool)js.rgbButtons[b];
		}
	}
}

void DInput::term()
{
	dx_release(din);
	dx_release(din_key);
	dx_release(din_mouse);
	
	for (int i=0; i < Input::Joypad::count; i++) {
		dx_release(din_joy[i]);
		device_name[i] = "<unknown>";
	}

	din_joy_count = 0;
}

DInput::~DInput()
{
	term();
}

