#ifndef _H_INPUT_H
#define _H_INPUT_H

#include "utility/logger.h"
#include <SDL_mouse.h>
#include <unordered_map>
#include <SDL_keycode.h>
#include <SDL_events.h>

#include "utility/mousestate.h"

class Command;
class MouseCommand;
class GameActor;

enum {
	MOUSE_BUTTON_LEFT = 1,
	MOUSE_BUTTON_RIGHT = 2,
	MOUSE_BUTTON_MIDDLE = 4,
	MOUSE_MOTION = 8,
	MOUSE_SCROLL = 16
};

class Keyboard
{
protected:
	Keyboard() {}

	void captureKeyboard(const SDL_Event& e)
	{
		if (e.type == SDL_KEYDOWN)
			keys[e.key.keysym.sym] = true;
		if (e.type == SDL_KEYUP)
			keys[e.key.keysym.sym] = false;
	}
	std::unordered_map<SDL_Keycode, bool> keys;
};

class Mouse
{
protected:
	Mouse() {}

	void captureMouse(const SDL_Event& e)
	{
		if (e.type == SDL_MOUSEBUTTONDOWN)
			mouseButtons[MOUSE_BUTTON_LEFT] = true;
		if (e.type == SDL_MOUSEBUTTONUP)
			mouseButtons[MOUSE_BUTTON_LEFT] = false;
		if (e.type == SDL_MOUSEWHEEL)
			mouse_state.scroll = e.wheel.preciseY;
		if (e.type == SDL_MOUSEMOTION) {
			mouse_state.x = static_cast<float>(e.motion.x);
			mouse_state.y = static_cast<float>(e.motion.y);
		}
	}
	MouseState mouse_state;
	std::unordered_map<Uint8, bool> mouseButtons;
};

class InputHandler : public Keyboard, public Mouse
{
private:
	struct CommandWithDelay{
		std::unique_ptr<Command> cmd;
		Uint32 lastExecution;
		Uint32 delay;
	};
	struct MouseCommandWithDelay{
		std::unique_ptr<MouseCommand> cmd;
		Uint32 lastExecution;
		Uint32 delay;
	};
public:
	InputHandler() {}
	void captureInput(const SDL_Event& e) {
		Keyboard::captureKeyboard(e); Mouse::captureMouse(e);
	}
	void setActor(GameActor* actor) {
		this->actor = actor;
	}

	void bindKeyCommand(SDL_Keycode key, Uint32 delay, std::unique_ptr<Command> command) {
		keyCommands[key] = { std::move(command), 0, delay };
	}
	void bindMouseCommand(Uint8 mouse, Uint32 delay, std::unique_ptr<MouseCommand> command) {
		mouseCommands[mouse] = { std::move(command), 0, delay };
	}
	void bindMouseMotion(std::unique_ptr<MouseCommand> command) {
		mouseMotionCommand = std::move(command);
	}
	void bindMouseScroll(std::unique_ptr<MouseCommand> command) {
		mouseScrollCommand = std::move(command);
	}
	void handleInput();
	void executeCommands();

	~InputHandler();

private:
	GameActor* actor = nullptr;
	std::unordered_map<SDL_Keycode, CommandWithDelay> keyCommands;
	std::unordered_map<Uint8, MouseCommandWithDelay> mouseCommands;
	std::vector<Command*> commandQueue;
	std::vector<MouseCommand*> mouseQueue;
	std::unique_ptr<MouseCommand> mouseMotionCommand = nullptr;
	std::unique_ptr<MouseCommand> mouseScrollCommand = nullptr;
};

#endif // _H_INPUT_H
