
#include "../interface.h"

Inputmanager::Device* Inputmanager::getDevice(Inputmanager::DeviceID deviceId) {
	for(unsigned i=0; i < vectorPos; i++) {
		if (deviceList[i]->id == deviceId) {
			return deviceList[i];
		}
	}
	return 0;
}

void Inputmanager::updateMap(Inputmanager::DeviceID deviceId, Inputmanager::IObjectID iobjectId, unsigned element) {
	Device* device;
	if ( ( device = getDevice(deviceId) ) == 0 ) return;
	device->updateMap(iobjectId, element);
}

unsigned Inputmanager::readMap(Inputmanager::DeviceID deviceId, Inputmanager::IObjectID iobjectId) {
	Device* device;
	if ( ( device = getDevice(deviceId) ) == 0 ) return 0;
	return device->readMap(iobjectId);
}

bool Inputmanager::pollDevices() {
	bool last = activeState;
	activeState ^= 1;
	dinput.poll( inputTable[activeState] );
	
	if (!waiting) return false; 
	//remap
	for(unsigned i=0; i < Input::ElementCounter; i++) {
			
		if (inputTable[activeState][i] == inputTable[last][i]) continue;
		if (! (InputElement::isAxis(i) || inputTable[activeState][i]) ) continue;

		InputObject::Type type = captureObject -> type;

		if ( type == InputObject::Button && !InputElement::isButton(i) ) continue;

		if (type == InputObject::Axis) {
			if (InputElement::isButton(i)) continue;

			i16 element_state = inputTable[activeState][i];

			if (InputElement::getElementType(i) == InputElement::MouseAxis && abs(element_state) < 8) {
				continue;	
			}

			if ( (InputElement::getElementType(i) == InputElement::JoypadAxis) &&
				(element_state > -28672 && element_state < +28672)) {
				continue;	
			}
		}

		captureObject -> element = i;
		waiting = false;
		flush();
		return true;
	}
	return false;
}

void Inputmanager::captureReady(Inputmanager::DeviceID deviceId,  Inputmanager::IObjectID iobjectId) {
	if (waiting) {
		return;
	}
	Device* device;
	if ( ( device = getDevice(deviceId) ) == 0 ) return;

	InputObject* iobject;
	if ( ( iobject = device->getInputObject(iobjectId) ) == 0 ) return;

	pollDevices();
	waiting = true;
	captureObject = iobject;
}

i16 Inputmanager::getInputState(DeviceID deviceId,  Inputmanager::IObjectID iobjectId) {
	Device* device;
	if ( ( device = getDevice(deviceId) ) == 0 ) return 0;
	return device->getState(iobjectId);
}

u8 Inputmanager::readPort(Inputmanager::Device::Port port, Inputmanager::IObjectID iobjectId) {
	if (port != 1 && port != 2) return 1;
	SmsPort &p = smsport[port-1];
	if (p.device == 0) return 1;

	switch(p.device->id) {
		case DeviceID::IdJoypad1:
		case DeviceID::IdJoypad2:
			return !(p.device->getState(iobjectId) & 1);

		case DeviceID::IdLightgun1:
		case DeviceID::IdLightgun2:
			//todo
			return 1;
	}
	return 1; //not pressed
}

void Inputmanager::setPort(Inputmanager::Device::Port port, Inputmanager::DeviceID deviceId) {
	if (port != 1 && port != 2) return;
	SmsPort &p = smsport[port-1];
	p.device = 0;
	Device* device;
	if ( ( device = getDevice(deviceId) ) == 0 ) return;
	if (device->port != port) return;
	p.device = device;
}

void Inputmanager::unmap(Inputmanager::DeviceID deviceId) {
	Device* device;
	if ( ( device = getDevice(deviceId) ) == 0 ) return;
	device->unmap();
}

unsigned Inputmanager::getDeviceObjectCount(Inputmanager::DeviceID deviceId) {
	Device* device;
	if ( ( device = getDevice(deviceId) ) == 0 ) return 0;
	return device->countInputObjects();
}

void Inputmanager::getDeviceName(Inputmanager::DeviceID deviceId, char* name) {
	Device* device;
	if ( ( device = getDevice(deviceId) ) == 0 ) name = "";
	strcpy( name, device->name );
}

Inputmanager::Device::Port Inputmanager::getDevicePort(Inputmanager::DeviceID deviceId) {
	Device* device;
	if ( ( device = getDevice(deviceId) ) == 0 ) return Device::Port::None;
	return device->port;
}

void Inputmanager::getDeviceObjectEntry(Inputmanager::DeviceID deviceId, Inputmanager::IObjectID iobjectId, char* entry) {
	Device* device;
	if ( ( device = getDevice(deviceId) ) == 0 ) entry = "";
	char* src = device->getEntry(iobjectId);
	strcpy( entry, src );
	free_mem(src);
}

bool Inputmanager::isHiToLoTransition(Inputmanager::DeviceID deviceId, Inputmanager::IObjectID iobjectId) {
	Device* device;
	if ( ( device = getDevice(deviceId) ) == 0 ) return false;
	return device->isHiToLoTransition(inputTable[activeState], inputTable[activeState ^ 1], iobjectId);
}

unsigned Inputmanager::getDeviceIdList(u8* list) {
	for(unsigned i=0; i < vectorPos; i++) {
		list[i] = (u8)deviceList[i]->id;
	}
	return getDeviceCount();
}

unsigned Inputmanager::getObjectIdList(Inputmanager::DeviceID deviceId, u8* list) {
	Device* device;
	if ( ( device = getDevice(deviceId) ) == 0 ) return 0;
	return device->getInputObjectIdList(list);
}

Inputmanager::InputElement::Type Inputmanager::InputElement::getElementType(unsigned element) {
	Type type;

	if (element < Input::Keyboard::elements) {
		type = KeyboardButton;
	} else if (element < (Input::Mouse::elements + Input::Keyboard::elements)) {
		
		u16 index = element - Input::Keyboard::elements;

		switch (index) {
			case Input::Mouse::x:
			case Input::Mouse::y:
			case Input::Mouse::z:
				type = MouseAxis;
				break;
			default:
				type = MouseButton;
				break;
		}
	} else if (element < Input::ElementCounter) {

		u16 index = (element - (Input::Mouse::elements + Input::Keyboard::elements)) % Input::Joypad::elements;

		switch (index) {
			case Input::Joypad::hat_up:		//+4 +8 +12
			case Input::Joypad::hat_right:	  //+4 +8 +12
			case Input::Joypad::hat_down:	  //+4 +8 +12
			case Input::Joypad::hat_left:	  //+4 +8 +12
			default:
				type = JoypadButton;
				break;

			case Input::Joypad::axis:
			case Input::Joypad::axis + 1:
			case Input::Joypad::axis + 2:
			case Input::Joypad::axis + 3:
			case Input::Joypad::axis + 4:
			case Input::Joypad::axis + 5:
				type = JoypadAxis;
				break;				
		}
	}
	return type;
}

Inputmanager::InputObject* Inputmanager::Device::getInputObject(Inputmanager::IObjectID iobjectId) {
	for(unsigned i=0; i < vectorPos; i++) {
		if (objectList[i]->id == iobjectId) {
			return objectList[i];
		}
	}
	return 0;
}

void Inputmanager::Device::updateMap(Inputmanager::IObjectID iobjectId, unsigned element) {
	InputObject* iobject;
	if ( ( iobject = getInputObject(iobjectId) ) == 0 ) return;
	iobject->element = element;
}

unsigned Inputmanager::Device::readMap(Inputmanager::IObjectID iobjectId) {
	InputObject* iobject;
	if ( ( iobject = getInputObject(iobjectId) ) == 0 ) return 0;
	return iobject->element;
}

i16 Inputmanager::Device::getState(Inputmanager::IObjectID iobjectId) {
	InputObject* iobject;
	if ( ( iobject = getInputObject(iobjectId) ) == 0 ) return 0;
	return iobject->state;
}

char* Inputmanager::Device::getEntry(Inputmanager::IObjectID iobjectId)
{
	char* out;
	char* key_name;
	InputObject* iobject;
	if ( ( iobject = getInputObject(iobjectId) ) == 0 ) return "";

	unsigned element = iobject -> element;
	char* ident = iobject -> name;

	InputObject::Type type = iobject -> type; //expected type

	if ((type == InputObject::Button && !InputElement::isButton(element)) ||
		(type == InputObject::Axis && !InputElement::isAxis(element)) ||
		(element == 0) )
	{
		out = concatStr(2, ident, "|<none>|<none>");
		iobject -> element = 0;
		return out;
	}
	key_name = new char[100];

	if (element < Input::Keyboard::elements) { //Keyboard	  

		inputmanager->dinput.get_key_name(element, key_name);

		out = concatStr(5, ident, "|" , "Keyboard", "|" , key_name);

	} else if (element < (Input::Mouse::elements + Input::Keyboard::elements)) { //Mouse

		u16 index = element - Input::Keyboard::elements;

		switch (index) {
			case Input::Mouse::x:
				sprintf(key_name, "%s", "X-Axis");
				break;
			case Input::Mouse::y:
				sprintf(key_name, "%s", "Y-Axis");
				break;
			case Input::Mouse::z:
				sprintf(key_name, "%s", "Z-Axis");
				break;
			default:
				sprintf(key_name, "%d", index - 2);
				key_name = concatStr(2, "Button: ", key_name);
				break;
		}
		out = concatStr(5, ident, "|", "Mouse", "|", key_name);

	} else if (element < Input::ElementCounter) { //Joypad

		u16 index = (element - (Input::Mouse::elements + Input::Keyboard::elements)) % Input::Joypad::elements;
		u8 joy = u8(double(element - (Input::Mouse::elements + Input::Keyboard::elements)) / double(Input::Joypad::elements));

		switch (index) {
			case Input::Joypad::hat_up:			sprintf(key_name, "%s", "hat0: Up"); break;
			case Input::Joypad::hat_up+4:	    sprintf(key_name, "%s", "hat1: Up"); break;
			case Input::Joypad::hat_up+8:	    sprintf(key_name, "%s", "hat2: Up"); break;
			case Input::Joypad::hat_up+12:	    sprintf(key_name, "%s", "hat3: Up"); break;

			case Input::Joypad::hat_right:	    sprintf(key_name, "%s", "hat0: Right"); break;
			case Input::Joypad::hat_right+4:	sprintf(key_name, "%s", "hat1: Right"); break;
			case Input::Joypad::hat_right+8:	sprintf(key_name, "%s", "hat2: Right"); break;
			case Input::Joypad::hat_right+12:	sprintf(key_name, "%s", "hat3: Right"); break;

			case Input::Joypad::hat_down:		sprintf(key_name, "%s", "hat0: Down"); break;
			case Input::Joypad::hat_down+4:		sprintf(key_name, "%s", "hat1: Down"); break;
			case Input::Joypad::hat_down+8:		sprintf(key_name, "%s", "hat2: Down"); break;
			case Input::Joypad::hat_down+12:	sprintf(key_name, "%s", "hat3: Down"); break;

			case Input::Joypad::hat_left:		sprintf(key_name, "%s", "hat0: Left"); break;
			case Input::Joypad::hat_left+4:		sprintf(key_name, "%s", "hat1: Left"); break;
			case Input::Joypad::hat_left+8:		sprintf(key_name, "%s", "hat2: Left"); break;
			case Input::Joypad::hat_left+12:	sprintf(key_name, "%s", "hat3: Left"); break;

			case Input::Joypad::axis:			sprintf(key_name, "%s", "X-Axis"); break;
			case Input::Joypad::axis + 1:		sprintf(key_name, "%s", "Y-Axis"); break;
			case Input::Joypad::axis + 2:	    sprintf(key_name, "%s", "Z-Axis"); break;
			case Input::Joypad::axis + 3:		sprintf(key_name, "%s", "X-Axis rot"); break;
			case Input::Joypad::axis + 4:	    sprintf(key_name, "%s", "Y-Axis rot"); break;
			case Input::Joypad::axis + 5:	    sprintf(key_name, "%s", "Z-Axis rot"); break;

			default:
				sprintf(key_name, "%d", index - 21);
				key_name = concatStr(2, "Button: ", key_name);
				break;
		}

		out = concatStr(5, ident, "|", inputmanager->dinput.device_name[joy], "|", key_name);
		free_mem(key_name);
	}
	return out;
}

void Inputmanager::Device::updateState(i16* table) {
	for(unsigned i = 0; i < vectorPos; i++) {
		if(InputElement::getElementType(objectList[i]->element) == InputElement::MouseAxis && !inputmanager->dinput.is_acquired()) {
			objectList[i]->state = 0; //mouse must be acquired (locked to window) to move axes
		} else if(InputElement::getElementType(objectList[i]->element) == InputElement::JoypadAxis) {
			//joypad axis range = -32768 to +32767, scale to -8 to +7 to roughly match mouse delta
			objectList[i]->state = table[objectList[i]->element] / 4096;
		} else {
			objectList[i]->state = table[objectList[i]->element];
		}
	}
}

bool Inputmanager::Device::isHiToLoTransition(i16* table, i16* tableLast, IObjectID iobjectId) {
	InputObject* iobject;
	if ( ( iobject = getInputObject(iobjectId) ) == 0 ) return false;

	unsigned element = iobject->element;
	if (tableLast[ element ] < table[ element ]) return true;
	return false;
}

void Inputmanager::Device::unmap() {
	for(unsigned i=0; i<vectorPos; i++) {
		objectList[i] -> element = 0;
	}
}

unsigned Inputmanager::Device::getInputObjectIdList(u8* list) {
	for(unsigned i=0; i < vectorPos; i++) {
		list[i] = (u8)objectList[i]->id;
	}
	return countInputObjects();
}