#include <psemek/sdl2/events.hpp>
#include <psemek/log/log.hpp>

#include <optional>
#include <SDL2/SDL.h>

namespace psemek::sdl2
{

	namespace
	{

		std::optional<app::mouse_button> mouse_button(Uint8 button)
		{
			switch (button)
			{
			case SDL_BUTTON_LEFT:
				return app::mouse_button::left;
			case SDL_BUTTON_MIDDLE:
				return app::mouse_button::middle;
			case SDL_BUTTON_RIGHT:
				return app::mouse_button::right;
			default:
				return std::nullopt;
			}
		}

		std::optional<app::keycode> keycode(SDL_Keysym key)
		{
			switch (key.scancode)
			{
			case SDL_SCANCODE_A:            return app::keycode::A;
			case SDL_SCANCODE_B:            return app::keycode::B;
			case SDL_SCANCODE_C:            return app::keycode::C;
			case SDL_SCANCODE_D:            return app::keycode::D;
			case SDL_SCANCODE_E:            return app::keycode::E;
			case SDL_SCANCODE_F:            return app::keycode::F;
			case SDL_SCANCODE_G:            return app::keycode::G;
			case SDL_SCANCODE_H:            return app::keycode::H;
			case SDL_SCANCODE_I:            return app::keycode::I;
			case SDL_SCANCODE_J:            return app::keycode::J;
			case SDL_SCANCODE_K:            return app::keycode::K;
			case SDL_SCANCODE_L:            return app::keycode::L;
			case SDL_SCANCODE_M:            return app::keycode::M;
			case SDL_SCANCODE_N:            return app::keycode::N;
			case SDL_SCANCODE_O:            return app::keycode::O;
			case SDL_SCANCODE_P:            return app::keycode::P;
			case SDL_SCANCODE_Q:            return app::keycode::Q;
			case SDL_SCANCODE_R:            return app::keycode::R;
			case SDL_SCANCODE_S:            return app::keycode::S;
			case SDL_SCANCODE_T:            return app::keycode::T;
			case SDL_SCANCODE_U:            return app::keycode::U;
			case SDL_SCANCODE_V:            return app::keycode::V;
			case SDL_SCANCODE_W:            return app::keycode::W;
			case SDL_SCANCODE_X:            return app::keycode::X;
			case SDL_SCANCODE_Y:            return app::keycode::Y;
			case SDL_SCANCODE_Z:            return app::keycode::Z;
			case SDL_SCANCODE_1:            return app::keycode::NUM_1;
			case SDL_SCANCODE_2:            return app::keycode::NUM_2;
			case SDL_SCANCODE_3:            return app::keycode::NUM_3;
			case SDL_SCANCODE_4:            return app::keycode::NUM_4;
			case SDL_SCANCODE_5:            return app::keycode::NUM_5;
			case SDL_SCANCODE_6:            return app::keycode::NUM_6;
			case SDL_SCANCODE_7:            return app::keycode::NUM_7;
			case SDL_SCANCODE_8:            return app::keycode::NUM_8;
			case SDL_SCANCODE_9:            return app::keycode::NUM_9;
			case SDL_SCANCODE_0:            return app::keycode::NUM_0;
			case SDL_SCANCODE_RETURN:       return app::keycode::RETURN;
			case SDL_SCANCODE_ESCAPE:       return app::keycode::ESCAPE;
			case SDL_SCANCODE_BACKSPACE:    return app::keycode::BACKSPACE;
			case SDL_SCANCODE_TAB:          return app::keycode::TAB;
			case SDL_SCANCODE_SPACE:        return app::keycode::SPACE;
			case SDL_SCANCODE_MINUS:        return app::keycode::MINUS;
			case SDL_SCANCODE_EQUALS:       return app::keycode::EQUALS;
			case SDL_SCANCODE_LEFTBRACKET:  return app::keycode::LEFTBRACKET;
			case SDL_SCANCODE_RIGHTBRACKET: return app::keycode::RIGHTBRACKET;
			case SDL_SCANCODE_BACKSLASH:    return app::keycode::BACKSLASH;
			case SDL_SCANCODE_NONUSHASH:    return app::keycode::NONUSHASH;
			case SDL_SCANCODE_SEMICOLON:    return app::keycode::SEMICOLON;
			case SDL_SCANCODE_APOSTROPHE:   return app::keycode::APOSTROPHE;
			case SDL_SCANCODE_GRAVE:        return app::keycode::BACKQUOTE;
			case SDL_SCANCODE_COMMA:        return app::keycode::COMMA;
			case SDL_SCANCODE_PERIOD:       return app::keycode::PERIOD;
			case SDL_SCANCODE_SLASH:        return app::keycode::SLASH;
			case SDL_SCANCODE_CAPSLOCK:     return app::keycode::CAPSLOCK;
			case SDL_SCANCODE_F1:           return app::keycode::F1;
			case SDL_SCANCODE_F2:           return app::keycode::F2;
			case SDL_SCANCODE_F3:           return app::keycode::F3;
			case SDL_SCANCODE_F4:           return app::keycode::F4;
			case SDL_SCANCODE_F5:           return app::keycode::F5;
			case SDL_SCANCODE_F6:           return app::keycode::F6;
			case SDL_SCANCODE_F7:           return app::keycode::F7;
			case SDL_SCANCODE_F8:           return app::keycode::F8;
			case SDL_SCANCODE_F9:           return app::keycode::F9;
			case SDL_SCANCODE_F10:          return app::keycode::F10;
			case SDL_SCANCODE_F11:          return app::keycode::F11;
			case SDL_SCANCODE_F12:          return app::keycode::F12;
			case SDL_SCANCODE_PRINTSCREEN:  return app::keycode::PRINTSCREEN;
			case SDL_SCANCODE_SCROLLLOCK:   return app::keycode::SCROLLLOCK;
			case SDL_SCANCODE_PAUSE:        return app::keycode::PAUSE;
			case SDL_SCANCODE_INSERT:       return app::keycode::INSERT;
			case SDL_SCANCODE_HOME:         return app::keycode::HOME;
			case SDL_SCANCODE_PAGEUP:       return app::keycode::PAGEUP;
			case SDL_SCANCODE_DELETE:       return app::keycode::DELETE;
			case SDL_SCANCODE_END:          return app::keycode::END;
			case SDL_SCANCODE_PAGEDOWN:     return app::keycode::PAGEDOWN;
			case SDL_SCANCODE_RIGHT:        return app::keycode::RIGHT;
			case SDL_SCANCODE_LEFT:         return app::keycode::LEFT;
			case SDL_SCANCODE_DOWN:         return app::keycode::DOWN;
			case SDL_SCANCODE_UP:           return app::keycode::UP;
			case SDL_SCANCODE_NUMLOCKCLEAR: return app::keycode::NUMLOCKCLEAR;
			case SDL_SCANCODE_KP_DIVIDE:    return app::keycode::KP_DIVIDE;
			case SDL_SCANCODE_KP_MULTIPLY:  return app::keycode::KP_MULTIPLY;
			case SDL_SCANCODE_KP_MINUS:     return app::keycode::KP_MINUS;
			case SDL_SCANCODE_KP_PLUS:      return app::keycode::KP_PLUS;
			case SDL_SCANCODE_KP_ENTER:     return app::keycode::KP_ENTER;
			case SDL_SCANCODE_KP_1:         return app::keycode::KP_1;
			case SDL_SCANCODE_KP_2:         return app::keycode::KP_2;
			case SDL_SCANCODE_KP_3:         return app::keycode::KP_3;
			case SDL_SCANCODE_KP_4:         return app::keycode::KP_4;
			case SDL_SCANCODE_KP_5:         return app::keycode::KP_5;
			case SDL_SCANCODE_KP_6:         return app::keycode::KP_6;
			case SDL_SCANCODE_KP_7:         return app::keycode::KP_7;
			case SDL_SCANCODE_KP_8:         return app::keycode::KP_8;
			case SDL_SCANCODE_KP_9:         return app::keycode::KP_9;
			case SDL_SCANCODE_KP_0:         return app::keycode::KP_0;
			case SDL_SCANCODE_KP_PERIOD:    return app::keycode::KP_PERIOD;
			case SDL_SCANCODE_APPLICATION:  return app::keycode::APPLICATION;
			case SDL_SCANCODE_MUTE:         return app::keycode::MUTE;
			case SDL_SCANCODE_VOLUMEUP:     return app::keycode::VOLUMEUP;
			case SDL_SCANCODE_VOLUMEDOWN:   return app::keycode::VOLUMEDOWN;
			case SDL_SCANCODE_LCTRL:        return app::keycode::LCTRL;
			case SDL_SCANCODE_LSHIFT:       return app::keycode::LSHIFT;
			case SDL_SCANCODE_LALT:         return app::keycode::LALT;
			case SDL_SCANCODE_LGUI:         return app::keycode::LGUI;
			case SDL_SCANCODE_RCTRL:        return app::keycode::RCTRL;
			case SDL_SCANCODE_RSHIFT:       return app::keycode::RSHIFT;
			case SDL_SCANCODE_RALT:         return app::keycode::RALT;
			case SDL_SCANCODE_RGUI:         return app::keycode::RGUI;
			default:
				return std::nullopt;
			}
		}

	}

	bool poll_events(app::event_handler & handler)
	{
		for (SDL_Event e; SDL_PollEvent(&e);) switch (e.type)
		{
		case SDL_QUIT:
			return true;
		case SDL_WINDOWEVENT: switch (e.window.event)
			{
			case SDL_WINDOWEVENT_CLOSE:
				return true;
			case SDL_WINDOWEVENT_RESIZED:
				log::info() << "Window resized to " << e.window.data1 << "x" << e.window.data2;
				handler.on_event(app::resize_event{{e.window.data1, e.window.data2}});
				break;
			case SDL_WINDOWEVENT_SIZE_CHANGED:
				log::info() << "Window resized to " << e.window.data1 << "x" << e.window.data2;
				handler.on_event(app::resize_event{{e.window.data1, e.window.data2}});
				break;
			case SDL_WINDOWEVENT_FOCUS_GAINED:
				handler.on_event(app::focus_event{true});
				break;
			case SDL_WINDOWEVENT_FOCUS_LOST:
				handler.on_event(app::focus_event{false});
				break;
			}
			break;
		case SDL_MOUSEMOTION:
			handler.on_event(app::mouse_move_event{{e.motion.x, e.motion.y}, {e.motion.xrel, e.motion.yrel}});
			break;
		case SDL_MOUSEWHEEL:
			handler.on_event(app::mouse_wheel_event{e.wheel.y});
			break;
		case SDL_MOUSEBUTTONDOWN:
			if (auto button = mouse_button(e.button.button))
				handler.on_event(app::mouse_button_event{*button, true});
			break;
		case SDL_MOUSEBUTTONUP:
			if (auto button = mouse_button(e.button.button))
				handler.on_event(app::mouse_button_event{*button, false});
			break;
		case SDL_KEYDOWN:
			if (auto key = keycode(e.key.keysym))
				handler.on_event(app::key_event{*key, true});
			break;
		case SDL_KEYUP:
			if (auto key = keycode(e.key.keysym))
				handler.on_event(app::key_event{*key, false});
			break;
		case SDL_TEXTINPUT:
			handler.on_event(app::text_input_event{e.text.text});
			break;
		}

		return false;
	}

}
