#include "inputcontroller/InputDeviceActiveState.h"
#include "shell/ShellKeyCodes.h"
#include "unittest/TestSuite.h"

static void testInit(void) {
	InputDeviceActiveState * state = InputDeviceActiveState_create((InputMap *) 0x1, (bool (*)(Atom)) 0x2);
	TestCase_assertPointerNonNULL(state);
	TestCase_assertPointerEqual(state->inputMap, 0x1);
	TestCase_assertPointerEqual(state->isDirectionalActionCallback, 0x2);
	InputDeviceActiveState_dispose(state);
}

static void testMostRecent(void) {
	InputDeviceActiveState * state = InputDeviceActiveState_create(NULL, NULL);
	
	InputDeviceType device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_KEYBOARD);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_KEYBOARD);
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_GAMEPAD);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_GAMEPAD);
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_MOUSE_MOVEMENT);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_MOUSE_MOVEMENT);
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_MOUSE_CLICK);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_MOUSE_CLICK);
	
	InputDeviceActiveState_input(state, INPUT_DEVICE_TYPE_KEYBOARD);
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_ALL);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_KEYBOARD);
	
	InputDeviceActiveState_input(state, INPUT_DEVICE_TYPE_GAMEPAD);
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_ALL);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_GAMEPAD);
	
	InputDeviceActiveState_input(state, INPUT_DEVICE_TYPE_MOUSE_MOVEMENT);
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_ALL);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_MOUSE_MOVEMENT);
	
	InputDeviceActiveState_input(state, INPUT_DEVICE_TYPE_MOUSE_CLICK);
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_ALL);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_MOUSE_CLICK);
	
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_KEYBOARD | INPUT_DEVICE_TYPE_BIT_GAMEPAD);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_GAMEPAD);
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_GAMEPAD | INPUT_DEVICE_TYPE_BIT_MOUSE_MOVEMENT);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_MOUSE_MOVEMENT);
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_MOUSE_MOVEMENT | INPUT_DEVICE_TYPE_BIT_MOUSE_CLICK);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_MOUSE_CLICK);
	
	InputDeviceActiveState_input(state, INPUT_DEVICE_TYPE_MOUSE_MOVEMENT);
	InputDeviceActiveState_input(state, INPUT_DEVICE_TYPE_GAMEPAD);
	InputDeviceActiveState_input(state, INPUT_DEVICE_TYPE_KEYBOARD);
	
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_ALL);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_KEYBOARD);
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_KEYBOARD | INPUT_DEVICE_TYPE_BIT_GAMEPAD);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_KEYBOARD);
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_GAMEPAD | INPUT_DEVICE_TYPE_BIT_MOUSE_MOVEMENT);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_GAMEPAD);
	device = InputDeviceActiveState_mostRecent(state, INPUT_DEVICE_TYPE_BIT_MOUSE_MOVEMENT | INPUT_DEVICE_TYPE_BIT_MOUSE_CLICK);
	TestCase_assertEnumEqual(device, INPUT_DEVICE_TYPE_MOUSE_MOVEMENT);
	
	InputDeviceActiveState_dispose(state);
}

static Atom ATOM_left = "left", ATOM_right = "right", ATOM_up = "up", ATOM_down = "down", ATOM_action = "action";

static bool isDirectionalActionCallback(Atom actionID) {
	return actionID == ATOM_left ||
	       actionID == ATOM_right ||
	       actionID == ATOM_down ||
	       actionID == ATOM_up;
}

static void testGetLastUsedBinding(void) {
	InputMap * inputMap = InputMap_create();
	InputMap_bindKey(inputMap, ATOM_left, KEY_CODE_LEFT_ARROW);
	InputMap_bindKey(inputMap, ATOM_right, KEY_CODE_RIGHT_ARROW);
	InputMap_bindKey(inputMap, ATOM_down, KEY_CODE_DOWN_ARROW);
	InputMap_bindKey(inputMap, ATOM_up, KEY_CODE_UP_ARROW);
	InputMap_bindKey(inputMap, ATOM_action, KEY_CODE_X);
	InputMap_bindKey(inputMap, ATOM_left, KEY_CODE_A);
	InputMap_bindKey(inputMap, ATOM_right, KEY_CODE_D);
	InputMap_bindKey(inputMap, ATOM_down, KEY_CODE_S);
	InputMap_bindKey(inputMap, ATOM_up, KEY_CODE_W);
	InputMap_bindKey(inputMap, ATOM_action, KEY_CODE_SPACE);
	InputMap_bindLogicalButtonAction(inputMap, ATOM_left, GAMEPAD_DPAD_LEFT);
	InputMap_bindLogicalButtonAction(inputMap, ATOM_right, GAMEPAD_DPAD_RIGHT);
	InputMap_bindLogicalButtonAction(inputMap, ATOM_down, GAMEPAD_DPAD_DOWN);
	InputMap_bindLogicalButtonAction(inputMap, ATOM_up, GAMEPAD_DPAD_UP);
	InputMap_bindLogicalButtonAction(inputMap, ATOM_action, GAMEPAD_BOTTOM_FACE_BUTTON);
	InputMap_bindLogicalAxisAction(inputMap, ATOM_left, GAMEPAD_LEFT_STICK_X, -1);
	InputMap_bindLogicalAxisAction(inputMap, ATOM_right, GAMEPAD_LEFT_STICK_X, 1);
	InputMap_bindLogicalAxisAction(inputMap, ATOM_down, GAMEPAD_LEFT_STICK_Y, -1);
	InputMap_bindLogicalAxisAction(inputMap, ATOM_up, GAMEPAD_LEFT_STICK_Y, 1);
	InputMap_bindLogicalButtonAction(inputMap, ATOM_action, GAMEPAD_LEFT_FRONT_SHOULDER);
	InputDeviceActiveState * state = InputDeviceActiveState_create(inputMap, isDirectionalActionCallback);
	
	InputMap_bindingGeneric lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_left);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_unknown);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_right);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_unknown);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_down);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_unknown);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_up);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_unknown);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_action);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_unknown);
	
	struct InputMap_keyboardActionBinding leftArrowBinding = {ATOM_left, KEY_CODE_LEFT_ARROW};
	bool changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_left, InputMap_genericKeyBinding(leftArrowBinding));
	TestCase_assertBoolTrue(changed);
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_left, InputMap_genericKeyBinding(leftArrowBinding));
	TestCase_assertBoolFalse(changed);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_left);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_key);
	TestCase_assertUIntEqual(lastUsedBinding.binding.key.keyCode, KEY_CODE_LEFT_ARROW);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_right);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_key);
	TestCase_assertUIntEqual(lastUsedBinding.binding.key.keyCode, KEY_CODE_RIGHT_ARROW);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_down);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_key);
	TestCase_assertUIntEqual(lastUsedBinding.binding.key.keyCode, KEY_CODE_DOWN_ARROW);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_up);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_key);
	TestCase_assertUIntEqual(lastUsedBinding.binding.key.keyCode, KEY_CODE_UP_ARROW);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_action);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_unknown);
	
	struct InputMap_keyboardActionBinding xKeyBinding = {ATOM_action, KEY_CODE_X};
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_action, InputMap_genericKeyBinding(xKeyBinding));
	TestCase_assertBoolTrue(changed);
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_action, InputMap_genericKeyBinding(xKeyBinding));
	TestCase_assertBoolFalse(changed);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_action);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_key);
	TestCase_assertUIntEqual(lastUsedBinding.binding.key.keyCode, KEY_CODE_X);
	
	struct InputMap_keyboardActionBinding wKeyBinding = {ATOM_up, KEY_CODE_W};
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_up, InputMap_genericKeyBinding(wKeyBinding));
	TestCase_assertBoolTrue(changed);
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_up, InputMap_genericKeyBinding(wKeyBinding));
	TestCase_assertBoolFalse(changed);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_left);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_key);
	TestCase_assertUIntEqual(lastUsedBinding.binding.key.keyCode, KEY_CODE_A);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_right);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_key);
	TestCase_assertUIntEqual(lastUsedBinding.binding.key.keyCode, KEY_CODE_D);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_down);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_key);
	TestCase_assertUIntEqual(lastUsedBinding.binding.key.keyCode, KEY_CODE_S);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_up);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_key);
	TestCase_assertUIntEqual(lastUsedBinding.binding.key.keyCode, KEY_CODE_W);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_action);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_key);
	TestCase_assertUIntEqual(lastUsedBinding.binding.key.keyCode, KEY_CODE_X);
	
	struct InputMap_keyboardActionBinding spaceKeyBinding = {ATOM_action, KEY_CODE_SPACE};
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_action, InputMap_genericKeyBinding(spaceKeyBinding));
	TestCase_assertBoolTrue(changed);
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_action, InputMap_genericKeyBinding(spaceKeyBinding));
	TestCase_assertBoolFalse(changed);
	lastUsedBinding = InputDeviceActiveState_getLastUsedKeyBinding(state, ATOM_action);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_key);
	TestCase_assertUIntEqual(lastUsedBinding.binding.key.keyCode, KEY_CODE_SPACE);
	
	
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_left);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_unknown);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_right);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_unknown);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_down);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_unknown);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_up);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_unknown);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_action);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_unknown);
	
	struct InputMap_gamepadLogicalButtonActionBinding dpadRightBinding = {ATOM_right, GAMEPAD_DPAD_RIGHT};
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_right, InputMap_genericGamepadButtonBinding(dpadRightBinding));
	TestCase_assertBoolTrue(changed);
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_right, InputMap_genericGamepadButtonBinding(dpadRightBinding));
	TestCase_assertBoolFalse(changed);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_left);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_gamepadButton);
	TestCase_assertEnumEqual(lastUsedBinding.binding.gamepadButton.elementID, GAMEPAD_DPAD_LEFT);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_right);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_gamepadButton);
	TestCase_assertEnumEqual(lastUsedBinding.binding.gamepadButton.elementID, GAMEPAD_DPAD_RIGHT);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_down);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_gamepadButton);
	TestCase_assertEnumEqual(lastUsedBinding.binding.gamepadButton.elementID, GAMEPAD_DPAD_DOWN);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_up);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_gamepadButton);
	TestCase_assertEnumEqual(lastUsedBinding.binding.gamepadButton.elementID, GAMEPAD_DPAD_UP);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_action);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_unknown);
	
	struct InputMap_gamepadLogicalButtonActionBinding bottomFaceButtonBinding = {ATOM_action, GAMEPAD_BOTTOM_FACE_BUTTON};
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_action, InputMap_genericGamepadButtonBinding(bottomFaceButtonBinding));
	TestCase_assertBoolTrue(changed);
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_action, InputMap_genericGamepadButtonBinding(bottomFaceButtonBinding));
	TestCase_assertBoolFalse(changed);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_action);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_gamepadButton);
	TestCase_assertEnumEqual(lastUsedBinding.binding.gamepadButton.elementID, GAMEPAD_BOTTOM_FACE_BUTTON);
	
	struct InputMap_gamepadLogicalAxisActionBinding leftStickDownBinding = {ATOM_down, GAMEPAD_LEFT_STICK_Y, -1};
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_down, InputMap_genericGamepadAxisActionBinding(leftStickDownBinding));
	TestCase_assertBoolTrue(changed);
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_down, InputMap_genericGamepadAxisActionBinding(leftStickDownBinding));
	TestCase_assertBoolFalse(changed);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_left);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_gamepadAxisAction);
	TestCase_assertEnumEqual(lastUsedBinding.binding.gamepadAxisAction.elementID, GAMEPAD_LEFT_STICK_X);
	TestCase_assertIntEqual(lastUsedBinding.binding.gamepadAxisAction.direction, -1);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_right);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_gamepadAxisAction);
	TestCase_assertEnumEqual(lastUsedBinding.binding.gamepadAxisAction.elementID, GAMEPAD_LEFT_STICK_X);
	TestCase_assertIntEqual(lastUsedBinding.binding.gamepadAxisAction.direction, 1);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_down);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_gamepadAxisAction);
	TestCase_assertEnumEqual(lastUsedBinding.binding.gamepadAxisAction.elementID, GAMEPAD_LEFT_STICK_Y);
	TestCase_assertIntEqual(lastUsedBinding.binding.gamepadAxisAction.direction, -1);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_up);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_gamepadAxisAction);
	TestCase_assertEnumEqual(lastUsedBinding.binding.gamepadAxisAction.elementID, GAMEPAD_LEFT_STICK_Y);
	TestCase_assertIntEqual(lastUsedBinding.binding.gamepadAxisAction.direction, 1);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_action);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_gamepadButton);
	TestCase_assertEnumEqual(lastUsedBinding.binding.gamepadButton.elementID, GAMEPAD_BOTTOM_FACE_BUTTON);
	
	struct InputMap_gamepadLogicalButtonActionBinding leftFrontShoulderButtonBinding = {ATOM_action, GAMEPAD_LEFT_FRONT_SHOULDER};
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_action, InputMap_genericGamepadButtonBinding(leftFrontShoulderButtonBinding));
	TestCase_assertBoolTrue(changed);
	changed = InputDeviceActiveState_setLastUsedActionBinding(state, ATOM_action, InputMap_genericGamepadButtonBinding(leftFrontShoulderButtonBinding));
	TestCase_assertBoolFalse(changed);
	lastUsedBinding = InputDeviceActiveState_getLastUsedGamepadBinding(state, ATOM_action);
	TestCase_assertEnumEqual(lastUsedBinding.type, InputMap_bindingType_gamepadButton);
	TestCase_assertEnumEqual(lastUsedBinding.binding.gamepadButton.elementID, GAMEPAD_LEFT_FRONT_SHOULDER);
	
	InputDeviceActiveState_dispose(state);
}

TEST_SUITE(InputDeviceActiveStateTest,
           testInit,
           testMostRecent,
           testGetLastUsedBinding)
