#include "input.h"
#include "ui.h"
#include "uielements.h"
#include "internal.h"
#include "internal/ui.h"
#include "internal/physics.h"

namespace kek::Defaults {

static KeyBinding
	keyForward,
	keyBackward,
	keyLeft,
	keyRight,
	keyUp,
	keyDown,
	keyOptions,
	keyToggleCursorMode,
	keyToggleNoclip,
	keyExit;

static ButtonElement *options;

static void defaultInput(GLFWwindow *window, void *data) {
	if(Input::isKeyboardCaptured()) return;

	glm::vec3 direction = glm::vec3(0);

	if(Input::getKeyState(keyForward) == GLFW_PRESS) {
		direction += kekData.activeCamera->direction;
	}

	if(Input::getKeyState(keyBackward) == GLFW_PRESS) {
		direction += -kekData.activeCamera->direction;
	}

	if(Input::getKeyState(keyLeft) == GLFW_PRESS) {
		direction += -glm::normalize(glm::cross(kekData.activeCamera->direction, glm::vec3(0.0f, 1.0f, 0.0f)));
	}

	if(Input::getKeyState(keyRight) == GLFW_PRESS) {
		direction += glm::normalize(glm::cross(kekData.activeCamera->direction, glm::vec3(0.0f, 1.0f, 0.0f)));
	}

	bool jump = false;
	if(Input::getKeyState(keyUp) == GLFW_PRESS) {
		direction += glm::vec3(0,1,0);
		jump = kekData.player->onGround;
	}

	if(Input::getKeyState(keyDown) == GLFW_PRESS) {
		direction += glm::vec3(0,-1,0);
	}

	direction = glm::normalize(direction);
	if(glm::length2(direction) > 0) {
		if(kekData.player->noclip) {
			kekData.activeCamera->translate(direction * KEK_NOCLIP_SPEED * kekData.lastFrameTime);
		}else {
			//kekData.player->physics->body->applyCentralImpulse(Physics::fromGLM(glm::normalize(direction) * (kekData.lastFrameTime * 20)));
			btVector3 vel = kekData.player->physics->body->getLinearVelocity();
			glm::vec3 newVel = glm::normalize(direction) * 6.0f;
			newVel.y = vel.y();
			if(jump) newVel.y = 2.7f;
			kekData.player->physics->body->setLinearVelocity(Physics::fromGLM(newVel));
		}
	}
}

static void defaultKeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods, void *data) {
	if(key == Input::getKeyBinding(keyExit).key && action == GLFW_PRESS) {
		glfwSetWindowShouldClose(window, true);
	}

	if(key == Input::getKeyBinding(keyOptions).key && action == GLFW_PRESS) {
		options->visible = !options->visible;
	}

	if(key == Input::getKeyBinding(keyToggleCursorMode).key && action == GLFW_PRESS) {
		if(Input::getCursorMode() == GLFWCursorMode::CAPTURE) {
			Input::setCursorMode(GLFWCursorMode::FREE);
		}else {
			Input::setCursorMode(GLFWCursorMode::CAPTURE);
		}
	}

	if(key == Input::getKeyBinding(keyToggleNoclip).key && action == GLFW_PRESS) {
		kekData.player->noclip = !kekData.player->noclip;
	}
}

static void defaultMouseCallback(GLFWwindow *window, double x, double y, void *data) {
	switch(Input::getCursorMode()) {
		case GLFWCursorMode::CAPTURE:
		{
			static bool firstMouse = true;
			static double lastX = 0, lastY = 0;
			if(firstMouse) {
				lastX = x;
				lastY = y;
				firstMouse = false;
			}

			float xoff = lastX - x;
			float yoff = lastY - y;
			lastX = x;
			lastY = y;

			xoff *= 0.1f;
			yoff *= 0.1f;

			kekData.activeCamera->rotateYaw(xoff);
			kekData.activeCamera->rotatePitch(yoff);
		}
		case GLFWCursorMode::FREE:
		case GLFWCursorMode::HIDDEN:
		{
			for(UIElement *element : kekData.ui->elements) {
				UIPoint childPos = element->getPosition();
				if(element->hoverAll(UIPoint((int) x - childPos.x, (int) y - childPos.y), UIPoint((int) x, (int) y))) break;
			}
		}
	}
}

static void defaultMouseButtonCallback(GLFWwindow* window, int button, int action, int mods, void *data) {
	switch(Input::getCursorMode()) {
		case GLFWCursorMode::CAPTURE:
		{
			// TODO
		}
		case GLFWCursorMode::FREE:
		case GLFWCursorMode::HIDDEN:
		{
			switch(action) {
				case GLFW_PRESS:
					break;
				case GLFW_RELEASE:
				{
					double x, y;
					glfwGetCursorPos(window, &x, &y);
					for(UIElement *element : kekData.ui->elements) {
						UIPoint childPos = element->getPosition();
						if(element->clickAll(UIPoint((int) x - childPos.x, (int) y - childPos.y), UIPoint((int) x, (int) y), (GLFWMouseButton) button)) break;
					}
				}
			}
		}
	}
}

void init() {
	keyForward = Input::createKeyBinding("Forward", GLFW_KEY_W);
	keyBackward = Input::createKeyBinding("Backward", GLFW_KEY_S);
	keyLeft = Input::createKeyBinding("Left", GLFW_KEY_A);
	keyRight = Input::createKeyBinding("Right", GLFW_KEY_D);
	keyUp = Input::createKeyBinding("Up", GLFW_KEY_SPACE);
	keyDown = Input::createKeyBinding("Down", GLFW_KEY_LEFT_CONTROL);
	keyOptions = Input::createKeyBinding("Options", GLFW_KEY_Q);
	keyToggleCursorMode = Input::createKeyBinding("Toggle Cursor Mode", GLFW_KEY_TAB);
	keyToggleNoclip = Input::createKeyBinding("Toggle Noclip", GLFW_KEY_N);
	keyExit = Input::createKeyBinding("Exit", GLFW_KEY_ESCAPE);

	Input::addPeriodicCallback(PeriodicCallback(defaultInput, nullptr));
	Input::addKeyListener(KeyCallback(defaultKeyCallback, nullptr));
	Input::addMouseListener(MouseCallback(defaultMouseCallback, nullptr));
	Input::addMouseButtonListener(MouseButtonCallback(defaultMouseButtonCallback, nullptr));

	options = new ButtonElement(uiPx(0), uiPx(100), uiPx(100), uiPx(50));
	//UI::addElement(options);

	kekData.player = new Player();
	btVector3 positions[2];
	positions[0] = btVector3(0, -KEK_PLAYER_HEIGHT / 2 + KEK_PLAYER_RADIUS, 0);
	positions[1] = btVector3(0, KEK_PLAYER_HEIGHT / 2 + KEK_PLAYER_RADIUS, 0);
	float radii[2] = {KEK_PLAYER_RADIUS, KEK_PLAYER_RADIUS};
	btCollisionShape *shape = new btMultiSphereShape(positions, radii, 2);
	btRigidBody *body = new btRigidBody(KEK_PLAYER_MASS, nullptr, shape);
	body->setFriction(3);
	body->setActivationState(DISABLE_DEACTIVATION);
	kekData.physics->world->addRigidBody(body);

	btCollisionShape *jumpShape = new btSphereShape(KEK_PLAYER_RADIUS / 2);
	btGhostObject *jumpCollider = new btGhostObject();
	jumpCollider->setCollisionShape(jumpShape);
	jumpCollider->setCollisionFlags(jumpCollider->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE);
	jumpCollider->setActivationState(DISABLE_DEACTIVATION);
	kekData.physics->world->addCollisionObject(jumpCollider);

	kekData.player->physics = new PlayerPhysicsObjectData();
	kekData.player->physics->body = body;
	kekData.player->physics->jumpCollider = jumpCollider;
	kekData.player->moveTo(glm::vec3(0,10,0));
}

void destroy() {

}

}
