#include <iostream>
#include <raylib.h>
#include "Morault_Resources.h"
#include "Morault_Player.h"
#include "Morault_Map.h"
#include <raymath.h>
#include <rlgl.h>
#include <string>
#include "Morault_Rendering.h"
#include "Morault_Safety.h"

#define MORAULT_MAP_RENDER_SCALE 0.1f

typedef enum {
	NONE = 0,
	GAME,
	EDITOR
} EngineState;

typedef enum {
	NA, A, B, C
} SectorTriangleSelection;
typedef enum {
	NOLINE, AB, BC, CA
} SectorLineSelection;

struct EditorSelection {
	int sector;
	SectorTriangleSelection point;
	SectorLineSelection line;
};
int main(int argc, char** argv) {
	bool running = true;
	SetConfigFlags(FLAG_WINDOW_RESIZABLE);
	InitWindow(1280, 720, "ProjectMorault");
	SetTargetFPS(60);
	SetExitKey(0);
	
	
	DisableCursor();


	Morault::Gameplay::Player player;
	player.data.playerName = "TestPlayer0001";
	player.data.hp = 100;
	player.data.xp = 0;
	bool gamePaused = false;
	
	EngineState eState = GAME;
	EditorSelection selection = { -1, SectorTriangleSelection::NA, SectorLineSelection::NOLINE };
	
	bool inSelection = false;
	Morault::Maps::Map* map = new Morault::Maps::Map();
	
	{
		using namespace Morault::Resources;
		Textures(load("data/textures/test.png"));
	}
	

	Vector2 editorOffset = { 0,0 };

	while (running) {

		if (WindowShouldClose()) {
			if (Morault::Safety::ConfirmQuitting()) running = false;
		}

		if (IsKeyDown(KEY_LEFT_ALT) && IsKeyPressed(KEY_ENTER)) ToggleFullscreen();

		switch(eState){
		case GAME: {
			if (IsKeyPressed(KEY_E)) {
				eState = EDITOR;
				EnableCursor();
			}
			player.controller.UpdatePlayerController(!gamePaused, map, MORAULT_MAP_RENDER_SCALE);

			BeginDrawing();
			ClearBackground(BLACK);
			BeginMode3D(player.controller.camera);
			// DrawGrid(100, 10);
			
			for (int i = 0; i < map->MapTriangles.size(); i++) {
				if (map->MapTriangles[i].floorTexture != "pms:none") {
					Morault::Rendering::DrawFloor(map->MapTriangles[i].a, map->MapTriangles[i].b, map->MapTriangles[i].c, map->MapTriangles[i].heightFloor, std::get<Texture2D>(Morault::Resources::Textures(Morault::Resources::get(map->MapTriangles[i].floorTexture))), MORAULT_MAP_RENDER_SCALE);
				}
				if (map->MapTriangles[i].ceilingTexture != "pms:none" && map->MapTriangles[i].ceilingTexture != "pms:SKY") {
					Morault::Rendering::DrawCeiling(map->MapTriangles[i].a, map->MapTriangles[i].b, map->MapTriangles[i].c, map->MapTriangles[i].heightCeiling, std::get<Texture2D>(Morault::Resources::Textures(Morault::Resources::get(map->MapTriangles[i].ceilingTexture))), MORAULT_MAP_RENDER_SCALE);
				}

				if (map->MapTriangles[i].wallAB != "pms:none") {
					Morault::Rendering::DrawWall(
						map->MapTriangles[i].a,
						map->MapTriangles[i].b,
						map->MapTriangles[i].heightFloor,
						map->MapTriangles[i].heightCeiling,
						std::get<Texture2D>(Morault::Resources::Textures(Morault::Resources::get(map->MapTriangles[i].wallAB))),
						MORAULT_MAP_RENDER_SCALE
					);
				}
				if (map->MapTriangles[i].wallBC != "pms:none") {
					Morault::Rendering::DrawWall(
						map->MapTriangles[i].b,
						map->MapTriangles[i].c,
						map->MapTriangles[i].heightFloor,
						map->MapTriangles[i].heightCeiling,
						std::get<Texture2D>(Morault::Resources::Textures(Morault::Resources::get(map->MapTriangles[i].wallBC))),
						MORAULT_MAP_RENDER_SCALE
					);
				}
				if (map->MapTriangles[i].wallCA != "pms:none") {
					Morault::Rendering::DrawWall(
						map->MapTriangles[i].c,
						map->MapTriangles[i].a,
						map->MapTriangles[i].heightFloor,
						map->MapTriangles[i].heightCeiling,
						std::get<Texture2D>(Morault::Resources::Textures(Morault::Resources::get(map->MapTriangles[i].wallCA))),
						MORAULT_MAP_RENDER_SCALE
					);
				}

			}
			
			
			
			EndMode3D();
			if (map->MapTriangles.size() == 0) DrawText("Map is empty, press E to open the map editor!", 0, GetScreenHeight()-94, 30, WHITE);
			DrawTexture(std::get<Texture2D>(Morault::Resources::Textures(Morault::Resources::get("data/ui/health.png"))), 0, GetScreenHeight() - 64, WHITE);
			DrawText(TextFormat("%i", player.data.hp), 66, GetScreenHeight() - 62, 64, BLACK);
			DrawText(TextFormat("%i", player.data.hp), 64, GetScreenHeight() - 64, 64, WHITE);
			DrawFPS(0, 0);
			EndDrawing();
		} break;
		case EDITOR: {
			if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_J)) {
				if (inSelection) {
					Vector2 selectedPoint;
					if (selection.point == A) selectedPoint = map->MapTriangles[selection.sector].a;
					if (selection.point == B) selectedPoint = map->MapTriangles[selection.sector].b;
					if (selection.point == C) selectedPoint = map->MapTriangles[selection.sector].c;
					for (int i = 0; i < map->MapTriangles.size(); i++) {
						if (i != selection.sector) {
							
							if (Vector2Distance(map->MapTriangles[i].a, selectedPoint) <= 3) map->MapTriangles[i].a = selectedPoint;
							if (Vector2Distance(map->MapTriangles[i].b, selectedPoint) <= 3) map->MapTriangles[i].b = selectedPoint;
							if (Vector2Distance(map->MapTriangles[i].c, selectedPoint) <= 3) map->MapTriangles[i].c = selectedPoint;
						}
					}
				}
			}
			if (IsKeyPressed(KEY_E)) {
				eState = GAME;
				DisableCursor();
			}
			if (IsKeyPressed(KEY_ESCAPE)) {
				inSelection = false;
				selection.line = SectorLineSelection::NOLINE;
			}
			if (IsKeyDown(KEY_LEFT_SHIFT)) {
				if (IsKeyPressed(KEY_N)) {
					
					Morault::Maps::MapTriangle tri;
					tri.a = { -128 - editorOffset.x, -128 - editorOffset.y };
					tri.b = { 128 - editorOffset.x, 128 - editorOffset.y };
					tri.c = { 128 - editorOffset.x,-128 - editorOffset.y };
					tri.floorTexture = "data/textures/test.png";
					tri.heightFloor = 0;
					tri.heightCeiling = 128;
					tri.hasFloor = true;
					tri.hasCeiling = false;
					tri.wallAB = "data/textures/test.png";
					tri.wallBC = "data/textures/test.png";
					tri.wallCA = "data/textures/test.png";
					map->MapTriangles.push_back(tri);
					if (IsKeyDown(KEY_LEFT_CONTROL)) {
						map->MapTriangles[map->MapTriangles.size() - 1].wallAB = "pms:none";

						tri.a = { -128 - editorOffset.x, -128 - editorOffset.y };
						tri.b = { -128 - editorOffset.x, 128 - editorOffset.y };
						tri.c = { 128 - editorOffset.x, 128 - editorOffset.y };
						tri.floorTexture = "data/textures/test.png";
						tri.heightFloor = 0;
						tri.heightCeiling = 128;
						tri.hasFloor = true;
						tri.hasCeiling = false;
						tri.wallAB = "data/textures/test.png";
						tri.wallBC = "data/textures/test.png";
						tri.wallCA = "pms:none";
						map->MapTriangles.push_back(tri);
					}
				}
			}

			if (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE)) editorOffset += GetMouseDelta();
			if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
				for (int i = 0; i < map->MapTriangles.size(); i++) {
					if (CheckCollisionPointCircle(GetMousePosition() - editorOffset, map->MapTriangles[i].a, 7)) {
						inSelection = true;
						selection = { i,A,SectorLineSelection::NOLINE };
					}
					if (CheckCollisionPointCircle(GetMousePosition() - editorOffset, map->MapTriangles[i].b, 7)) {
						inSelection = true;
						selection = { i,B,SectorLineSelection::NOLINE };
					}
					if (CheckCollisionPointCircle(GetMousePosition() - editorOffset, map->MapTriangles[i].c, 7)) {
						inSelection = true;
						selection = { i,C,SectorLineSelection::NOLINE };
					}
				}
			}

			if (inSelection) {
				if (IsKeyDown(KEY_RIGHT_SHIFT) && IsKeyDown(KEY_L)) {
					switch (selection.point) {
					case A:
						if (IsKeyPressed(KEY_B)) {
							selection.line = AB;
						}
						if (IsKeyPressed(KEY_C)) {
							selection.line = CA;
						}
						break;
					case B:
						if (IsKeyPressed(KEY_A)) {
							selection.line = AB;
						}
						if (IsKeyPressed(KEY_C)) {
							selection.line = BC;
						}
						break;
					case C:
						if (IsKeyPressed(KEY_B)) {
							selection.line = BC;
						}
						if (IsKeyPressed(KEY_A)) {
							selection.line = CA;
						}
						break;
					}
				}
			}
			if (IsKeyDown(KEY_LEFT_SHIFT) && IsKeyDown(KEY_S) && IsKeyPressed(KEY_DELETE)) {
				if (inSelection) {
					map->MapTriangles.erase(map->MapTriangles.begin() + selection.sector);
					inSelection = false;
				}
			}

			if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_O)) { // open
				std::cout << "What to open? (empty = cancel) >>> ";
				std::string path;
				std::getline(std::cin, path);
				if (path != "") map->LoadMap(path);
			}

			if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_S)) { // save
				std::cout << "Where to save? (empty = cancel) >>> ";
				std::string path;
				std::getline(std::cin, path);
				if (path != "") map->SaveMap(path);
			}

			if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_T)) {
				if (inSelection) {
					if (IsKeyDown(KEY_LEFT_SHIFT)) {
						std::cout << "Texture for ceiling " << selection.sector  << "? (current texture is: " << map->MapTriangles[selection.sector].ceilingTexture << ") >>>";

						std::string texture;
						std::getline(std::cin, texture);
						if (texture != "") map->MapTriangles[selection.sector].ceilingTexture = texture;
					}
					else{
						std::cout << "Texture for floor " << selection.sector << "? (current texture is: " << map->MapTriangles[selection.sector].floorTexture << ") >>>";

						std::string texture;
						std::getline(std::cin, texture);
						if (texture != "") map->MapTriangles[selection.sector].floorTexture = texture;
					}
				}
			}

			if (IsKeyPressed(KEY_T) && selection.line != NOLINE && !IsKeyDown(KEY_LEFT_CONTROL)) {
				std::cout << "Texture for line " << selection.sector << ":" << (selection.line == AB ? "AB" : selection.line == BC ? "BC" : "CA") << "? (current texture is: " << (selection.line == AB ? map->MapTriangles[selection.sector].wallAB : selection.line == BC ? map->MapTriangles[selection.sector].wallBC : map->MapTriangles[selection.sector].wallCA) << ") >>>";

				std::string texture;
				std::getline(std::cin, texture);
				if (texture != "") {
					if (selection.line == AB) map->MapTriangles[selection.sector].wallAB = texture;
					if (selection.line == BC) map->MapTriangles[selection.sector].wallBC = texture;
					if (selection.line == CA) map->MapTriangles[selection.sector].wallCA = texture;
				}
			}

			if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) {
				if (inSelection) {
					switch (selection.point) {
					case A:
						map->MapTriangles[selection.sector].a += GetMouseDelta();
						break;
					case B:
						map->MapTriangles[selection.sector].b += GetMouseDelta();
						break;
					case C:
						map->MapTriangles[selection.sector].c += GetMouseDelta();
						break;
					}
				}
			}

			BeginDrawing();
			ClearBackground(BLACK);

			for (int i = 0; i < map->MapTriangles.size(); i++) {
				DrawTriangleLines(map->MapTriangles[i].a + editorOffset, map->MapTriangles[i].b + editorOffset, map->MapTriangles[i].c + editorOffset, YELLOW);
				
				
				DrawCircle((map->MapTriangles[i].a + editorOffset).x, (map->MapTriangles[i].a + editorOffset).y, 3, RED);
				DrawText("A", (map->MapTriangles[i].a + editorOffset).x, (map->MapTriangles[i].a + editorOffset).y, 5, WHITE);
				DrawCircle((map->MapTriangles[i].b + editorOffset).x, (map->MapTriangles[i].b + editorOffset).y, 3, RED);
				DrawText("B", (map->MapTriangles[i].b + editorOffset).x, (map->MapTriangles[i].b + editorOffset).y, 5, WHITE);
				DrawCircle((map->MapTriangles[i].c + editorOffset).x, (map->MapTriangles[i].c + editorOffset).y, 3, RED);
				DrawText("C", (map->MapTriangles[i].c + editorOffset).x, (map->MapTriangles[i].c + editorOffset).y, 5, WHITE);
				if(inSelection && selection.sector == i){
					if (selection.point == A) DrawCircle((map->MapTriangles[i].a + editorOffset).x, (map->MapTriangles[i].a + editorOffset).y, 5, GREEN);
					if (selection.point == B) DrawCircle((map->MapTriangles[i].b + editorOffset).x, (map->MapTriangles[i].b + editorOffset).y, 5, GREEN);
					if (selection.point == C) DrawCircle((map->MapTriangles[i].c + editorOffset).x, (map->MapTriangles[i].c + editorOffset).y, 5, GREEN);
					if (selection.line == AB) DrawLineEx(map->MapTriangles[i].a + editorOffset, map->MapTriangles[i].b + editorOffset, 4, PURPLE);
					if (selection.line == BC) DrawLineEx(map->MapTriangles[i].b + editorOffset, map->MapTriangles[i].c + editorOffset, 4, PURPLE);
					if (selection.line == CA) DrawLineEx(map->MapTriangles[i].c + editorOffset, map->MapTriangles[i].a + editorOffset, 4, PURPLE);
				}

			}

			EndDrawing();
		}break;
		default:
			throw std::runtime_error("what the fuck?");
		}
	}


	CloseWindow();
}