/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#pragma once

#include "InputDeviceType.hpp"

namespace OpenVulkano::Input
{
	class InputKey
	{
	public:
		enum class InputType : uint8_t
		{
			BUTTON = 0, AXIS
		};

		class Keyboard
		{
		public:
			enum Key : int16_t
			{
				UNKNOWN = -1,
				// Numpad
				KEY_NUMPAD_0 = 0,
				KEY_NUMPAD_1 = 1,
				KEY_NUMPAD_2 = 2,
				KEY_NUMPAD_3 = 3,
				KEY_NUMPAD_4 = 4,
				KEY_NUMPAD_5 = 5,
				KEY_NUMPAD_6 = 6,
				KEY_NUMPAD_7 = 7,
				KEY_NUMPAD_8 = 8,
				KEY_NUMPAD_9 = 9,
				KEY_NUMPAD_DECIMAL = 10,
				KEY_NUMPAD_DIVIDE = 11,
				KEY_NUMPAD_MULTIPLY = 12,
				KEY_NUMPAD_SUBTRACT = 13,
				KEY_NUMPAD_ADD = 14,
				KEY_NUMPAD_ENTER = 15,
				KEY_NUMPAD_EQUAL = 16,
				KEY_NUM_LOCK = 17,

				KEY_ESCAPE = 18,
				KEY_ENTER = 19,
				KEY_TAB = 20,
				KEY_BACKSPACE = 21,
				KEY_INSERT = 22,
				KEY_DELETE = 23,
				KEY_RIGHT = 24,
				KEY_LEFT = 25,
				KEY_DOWN = 26,
				KEY_UP = 27,
				KEY_PAGE_UP = 28,
				KEY_PAGE_DOWN = 29,
				KEY_HOME = 30,
				KEY_END = 31,
				KEY_CAPS_LOCK = 32,
				KEY_SCROLL_LOCK = 33,
				KEY_PRINT_SCREEN = 34,
				KEY_PAUSE = 35,
				KEY_LEFT_SHIFT = 36,
				KEY_LEFT_CONTROL = 37,
				KEY_LEFT_ALT = 38,
				KEY_LEFT_SUPER = 39,
				KEY_RIGHT_SHIFT = 40,
				KEY_RIGHT_CONTROL = 41,
				KEY_RIGHT_ALT = 42,
				KEY_RIGHT_SUPER = 43,

				// Printable keys, most of them are mapped to their ascii codes
				KEY_COMMA = 44,
				KEY_MINUS = 45,
				KEY_PERIOD = 46,
				KEY_SLASH = 47,
				KEY_0 = 48,
				KEY_1 = 49,
				KEY_2 = 50,
				KEY_3 = 51,
				KEY_4 = 52,
				KEY_5 = 53,
				KEY_6 = 54,
				KEY_7 = 55,
				KEY_8 = 56,
				KEY_9 = 57,
				KEY_WORLD_1 = 58,
				KEY_SEMICOLON = 59,
				KEY_WORLD_2 = 60,
				KEY_EQUAL = 61,
				KEY_SPACE = 62,
				KEY_APOSTROPHE = 63, //'
				KEY_GRAVE_ACCENT = 64,  // `
				KEY_A = 65,
				KEY_B = 66,
				KEY_C = 67,
				KEY_D = 68,
				KEY_E = 69,
				KEY_F = 70,
				KEY_G = 71,
				KEY_H = 72,
				KEY_I = 73,
				KEY_J = 74,
				KEY_K = 75,
				KEY_L = 76,
				KEY_M = 77,
				KEY_N = 78,
				KEY_O = 79,
				KEY_P = 80,
				KEY_Q = 81,
				KEY_R = 82,
				KEY_S = 83,
				KEY_T = 84,
				KEY_U = 85,
				KEY_V = 86,
				KEY_W = 87,
				KEY_X = 88,
				KEY_Y = 89,
				KEY_Z = 90,
				KEY_LEFT_BRACKET = 91,  // [
				KEY_BACKSLASH = 92,
				KEY_RIGHT_BRACKET = 93,  // ]

				KEY_MENU = 99,

				// Function keys
				KEY_F1 = 101,
				KEY_F2 = 102,
				KEY_F3 = 103,
				KEY_F4 = 104,
				KEY_F5 = 105,
				KEY_F6 = 106,
				KEY_F7 = 107,
				KEY_F8 = 108,
				KEY_F9 = 109,
				KEY_F10 = 110,
				KEY_F11 = 111,
				KEY_F12 = 112,
				KEY_F13 = 113,
				KEY_F14 = 114,
				KEY_F15 = 115,
				KEY_F16 = 116,
				KEY_F17 = 117,
				KEY_F18 = 118,
				KEY_F19 = 119,
				KEY_F20 = 120,
				KEY_F21 = 121,
				KEY_F22 = 122,
				KEY_F23 = 123,
				KEY_F24 = 124,
				KEY_F25 = 125,

				KEY_LAST = KEY_F25
			};
		};

		class Mouse
		{
		public:
			enum Button : int16_t
			{
				BUTTON_1 = 0,
				BUTTON_2,
				BUTTON_3,
				BUTTON_4,
				BUTTON_5,
				BUTTON_6,
				BUTTON_7,
				BUTTON_8,
				BUTTON_LAST = BUTTON_8,
				BUTTON_LEFT = BUTTON_1,
				BUTTON_RIGHT = BUTTON_2,
				BUTTON_MIDDLE = BUTTON_3
			};

			enum Axis : int16_t
			{
				AXIS_X = 0,
				AXIS_Y,
				AXIS_WHEEL_X,
				AXIS_WHEEL_Y,
				AXIS_LAST = AXIS_WHEEL_Y
			};
		};

		class Controller
		{
		public:
			enum Button : int16_t
			{
				JOY_1 = 0,
				JOY_2,
				JOY_3,
				JOY_4,
				JOY_5,
				JOY_6,
				JOY_7,
				JOY_8,
				JOY_9,
				JOY_10,
				JOY_11,
				JOY_12,
				JOY_13,
				JOY_14,
				JOY_15,
				JOY_16,
				JOY_LAST = JOY_16,

				BUTTON_A = JOY_1,
				BUTTON_B = JOY_2,
				BUTTON_X = JOY_3,
				BUTTON_Y = JOY_4,
				BUTTON_LEFT_BUMPER = JOY_5,
				BUTTON_RIGHT_BUMPER = JOY_6,
				BUTTON_BACK = JOY_7,
				BUTTON_START = JOY_8,
				BUTTON_GUIDE = JOY_9,
				BUTTON_LEFT_THUMB = JOY_10,
				BUTTON_RIGHT_THUMB = JOY_11,
				BUTTON_DPAD_UP = JOY_12,
				BUTTON_DPAD_RIGHT = JOY_13,
				BUTTON_DPAD_DOWN = JOY_14,
				BUTTON_DPAD_LEFT = JOY_15,

				PS_BUTTON_CROSS = BUTTON_A,
				PS_BUTTON_CIRCLE = BUTTON_B,
				PS_BUTTON_SQUARE = BUTTON_X,
				PS_BUTTON_TRIANGLE = BUTTON_Y
			};

			enum Axis : int16_t
			{
				AXIS_LEFT_X = 0,
				AXIS_LEFT_Y,
				AXIS_RIGHT_X,
				AXIS_RIGHT_Y,
				AXIS_LEFT_TRIGGER,
				AXIS_RIGHT_TRIGGER,
				AXIS_LAST = AXIS_RIGHT_TRIGGER
			};
		};

		class Touch
		{
		public:
			enum Button: int16_t
			{
				BUTTON_TAP = 0,
				BUTTON_TWO_FINGER_TAP
			};

			enum Axis : int16_t
			{
				AXIS_TAP_X = 0,
				AXIS_TAP_Y,
				AXIS_TAP_DURATION,
				AXIS_TAP_TWO_FINGERS_X,
				AXIS_TAP_TWO_FINGERS_Y,
				AXIS_TAP_TWO_FINGER_DURATION,
				AXIS_PAN_X,
				AXIS_PAN_Y,
				AXIS_PAN_TWO_FINGERS_X,
				AXIS_PAN_TWO_FINGERS_Y,
				AXIS_PINCH,
				AXIS_PINCH_CENTER_X,
				AXIS_PINCH_CENTER_Y,
				AXIS_LAST = AXIS_PINCH_CENTER_Y
			};
		};

	private:
		InputDeviceType deviceType;
		InputType type;
		int16_t key;

	public:
		InputKey(Keyboard::Key keyboardKey) :
			deviceType(InputDeviceType::KEYBOARD), type(InputType::BUTTON), key(keyboardKey)
		{}

		InputKey(Mouse::Button mouseButton) :
			deviceType(InputDeviceType::MOUSE), type(InputType::BUTTON), key(mouseButton)
		{}

		InputKey(Mouse::Axis mouseAxis) :
			deviceType(InputDeviceType::MOUSE), type(InputType::AXIS), key(mouseAxis)
		{}

		InputKey(Controller::Button controllerButton) :
			deviceType(InputDeviceType::CONTROLLER), type(InputType::BUTTON), key(controllerButton)
		{}

		InputKey(Controller::Axis controllerAxis) :
			deviceType(InputDeviceType::CONTROLLER), type(InputType::AXIS), key(controllerAxis)
		{}

		InputKey(Touch::Button touchButton) :
			deviceType(InputDeviceType::TOUCH), type(InputType::BUTTON), key(touchButton)
		{}

		InputKey(Touch::Axis touchAxis) :
			deviceType(InputDeviceType::TOUCH), type(InputType::AXIS), key(touchAxis)
		{}

		[[nodiscard]] InputDeviceType GetInputDeviceType() const { return deviceType; }

		[[nodiscard]] InputType GetInputType() const { return type; }

		[[nodiscard]] int16_t GetInputKey() const { return key; }

		bool operator==(const InputKey& other) const
		{
			return deviceType == other.deviceType && type == other.type && key == other.key;
		}

		bool operator!=(const InputKey& other) const
		{
			return !(*this == other);
		}
	};
}
