#include "gameScreen.h"
#include "game.h"
#include "world.h"
#include "bullets.h"
#include "assets.h"
#include "killLog.h"
#include "entitiesInclude.h"

void initGameScreenGui(GameScreen * gameScreen) {
	float width = GetScreenWidth();
	float height = GetScreenHeight();

	// It is kind of terrible but works.
	gameScreen->infoTextPosition = (Vector2){0.0, height / 1.5};

	gameScreen->targetInfoPosition = (Vector2){
		width - (GAME_SCREEN_TEXT_SIZE * (GAME_SCREEN_TARGET_INFO_MAX / 2.0)),
		height - RADAR_TEXTURE_SIZE - (GAME_SCREEN_TEXT_SIZE * 4.0)
	};

	gameScreen->zoomViewPosition = (Vector2){width - GAME_SCREEN_ZOOM_VIEW_UI_SIZE - 20.0, 10.0};

	gameScreen->killLogPosition = (Vector2){0.0, 40.0};

	resetRadarPosition(&gameScreen->radar);
	resetGyroscopePosition(&gameScreen->gyroscope);
}

void initGameScreen(Game * game, GameScreen * gameScreen) {

	// World render.
	if (game->settings.useWorldRenderTexture) {
		gameScreen->worldRender = LoadRenderTexture(game->settings.renderWidth, game->settings.renderHeight);
		gameScreen->usingWorldRenderTexture = true;
	} else {
		gameScreen->usingWorldRenderTexture = false;
	}

	// Zoom view.
	gameScreen->zoomViewTexture = LoadRenderTexture(GAME_SCREEN_ZOOM_VIEW_SIZE, GAME_SCREEN_ZOOM_VIEW_SIZE);

	gameScreen->gameOver = false;
	gameScreen->mainCamera = THIRD_PERSON_CAMERA;
	gameScreen->levelComplete = false;

	// Gyroscope indeed
	initGyroscope(&gameScreen->gyroscope);

	// Radar indeed.
	initRadar(&gameScreen->radar);

	initStars(&gameScreen->stars);

	initGameScreenGui(gameScreen);
}

void freeGameScreen(GameScreen * gameScreen) {
	closeGyroscope(&gameScreen->gyroscope);
	closeRadar(&gameScreen->radar);

	if (gameScreen->usingWorldRenderTexture)
		UnloadRenderTexture(gameScreen->worldRender);

	UnloadRenderTexture(gameScreen->zoomViewTexture);
}

void drawCrossHairPosition(Vector2 position, float size, float thick, Color color) {
	// Left to right.
	DrawLineEx(
		(Vector2){position.x - size, position.y},
		(Vector2){position.x + size, position.y},
		thick,
		color
	);

	// Top to bottom.
	DrawLineEx(
		(Vector2){position.x, position.y - size},
		(Vector2){position.x, position.y + size},
		thick,
		color
	);
}

void drawCrossHair(float size, float thick, Color color) {
	Vector2 center = (Vector2){GetScreenWidth() / 2.0, GetScreenHeight() / 2.0};
	drawCrossHairPosition(center, size, thick, color);
}

void drawGameScreenInfoText(Game * game, GameScreen * gameScreen) {
	Entity * player = getEntityFromWorld(game->world, 0);

	Vector3 position = player->position;
	Vector3 velocity = player->velocity.velocity;

	// Hello reader. I fucking hate you!
	size_t bufSize = 255;
	char buf[bufSize];

	// Format text.
	snprintf(
		buf,
		bufSize,
		"Health %.2f\n\nX %.2f\nY %.2f\nZ %.2f\n\nSpeed %.2f",
		player->health,
		position.x,
		position.y,
		position.z,
		Vector3Length(velocity)
	);

	// Draw info text.
	DrawText(
		buf,
		gameScreen->infoTextPosition.x,
		gameScreen->infoTextPosition.y,
		GAME_SCREEN_TEXT_SIZE,
		GREEN
	);
}

void drawGameScreenTargetInfo(Game * game, GameScreen * gameScreen) {
	Entity * player = getEntityFromWorld(game->world, 0);
	AntifaShip * data = (AntifaShip*)player->data;

	size_t bufSize = 255;
	char buf[bufSize];

	// Format.
	snprintf(
		buf,
		bufSize,
		"Auto: %s",
		data->doAutoTarget ? "On" : "Off"
	);

	// Is auto targeting.
	if (data->doAutoTarget) {
		Entity * targetedEntity = getEntityFromWorld(game->world, data->targetedEntityId);

		if (targetedEntity != NULL) {
			char bufCopy[bufSize];
			strncpy(bufCopy, buf, bufSize);

			// Add more formatted text.
			snprintf(
				buf,
				bufSize,
				"%s\nId: %d@%x\nDistance: %.2f\nOn Target: %s",
				bufCopy,
				data->targetedEntityId,
				data->targetedEntityFingerprint,
				Vector3Distance(player->position, targetedEntity->position),
				data->isOnTarget ? "Yes" : "No"
			);
		}
	}

	// Draw.
	DrawText(
		buf,
		gameScreen->targetInfoPosition.x,
		gameScreen->targetInfoPosition.y,
		GAME_SCREEN_TEXT_SIZE,
		GREEN
	);
}

void drawGameScreenGui(Game * game) {
	GameScreen * gameScreen = &game->gameScreen;

	// Draw cross hair.
	if (gameScreen->mainCamera == FIRST_PERSON_CAMERA || gameScreen->mainCamera == ZOOM_CAMERA) {
		// Get color depending if on target or not.
		Entity * player = getEntityFromWorld(game->world, 0);
		AntifaShip * data = (AntifaShip*)player->data;
		Color color = data->isOnTarget ? RED : BLUE;

		drawCrossHair(10.0, 2.0, color);
	}

	drawKillLog(&game->killLog, gameScreen->killLogPosition);
	drawGameScreenInfoText(game, gameScreen);
	drawGameScreenTargetInfo(game, gameScreen);
	drawGyroscope(game, &gameScreen->gyroscope);
	drawRadar(game, &gameScreen->radar);
}

void handleGameScreenInput(Game * game, GameScreen * gameScreen) {
	switch(GetKeyPressed()) {
		case KEY_ONE:
			gameScreen->mainCamera = FIRST_PERSON_CAMERA;
			break;
		case KEY_TWO:
			gameScreen->mainCamera = THIRD_PERSON_CAMERA;
			break;
		case KEY_THREE:
			gameScreen->mainCamera = ZOOM_CAMERA;
			break;
		case KEY_FOUR:
			gameScreen->mainCamera = DEBUG_CAMERA;
			break;
		default:
			break;
	}
}

void drawNextLevelScreen(Game * game, GameScreen * gameScreen) {
	float width = GetScreenWidth();
	float height = GetScreenHeight();

	size_t bufSize = 100;
	char buf[bufSize];

	// Complete message.
	snprintf(
		buf,
		bufSize,
		"Level %d complete",
		game->levels.currentLevel + 1
	);

	DrawText(
		buf,
		(width / 2.0) - (50.0 * strnlen(buf, bufSize) / 4.0),
		height / 3.0,
		50,
		GREEN
	);
}

void gameScreenHandleLevels(Game * game, GameScreen * gameScreen) {
	// Show complete screen if level complete.
	if (gameScreen->levelComplete) {
		drawNextLevelScreen(game, gameScreen);

		// Next fucking level.
		if (GetTime() - gameScreen->timeAtLevelComplete >= GAME_SCREEN_NEXT_LEVEL_DELAY) {
			gameScreen->levelComplete = false;
			startLevel(game, &game->levels, gameScreen->lastLevel + 1);
		}

		return;
	}

	bool complete = updateLevel(game, &game->levels);

	// This fucker been completed.
	if (complete) {
		gameScreen->lastLevel = game->levels.currentLevel;
		endLevel(game, &game->levels);
		gameScreen->levelComplete = true;
		gameScreen->timeAtLevelComplete = GetTime();
	}
}

void drawGameScreenStars(Game * game, GameScreen * gameScreen) {
	DrawModel(
		game->assets.models[SKY_ASSET],
		getEntityFromWorld(game->world, 0)->position,
		GAME_SCREEN_SKY_BOX_SIZE,
		WHITE
	);

	drawStars(game, &gameScreen->stars);
}

void renderWorldGameScreen(Game * game, GameScreen * gameScreen) {
	BeginMode3D(game->cameras[gameScreen->mainCamera]);

	drawGameScreenStars(game, gameScreen);

	// Draw world.
	drawWorld(&game->world, game);

	EndMode3D();
}

void drawZoomViewGameScreen(Game * game, GameScreen * gameScreen) {
	CameraId cameraId = ZOOM_CAMERA;

	if (gameScreen->mainCamera == ZOOM_CAMERA)
		cameraId = THIRD_PERSON_CAMERA;

	// Update camera.
	runCameraUpdate(game, game->cameras, cameraId);

	// Render onto texture.
	BeginTextureMode(gameScreen->zoomViewTexture);
	ClearBackground(BLACK);

	BeginMode3D(game->cameras[cameraId]);
	drawWorld(&game->world, game);
	EndMode3D();

	EndTextureMode();

	// Draw texture.
	DrawTexturePro(
		gameScreen->zoomViewTexture.texture,
		(Rectangle){0.0, 0.0, GAME_SCREEN_ZOOM_VIEW_SIZE, -GAME_SCREEN_ZOOM_VIEW_SIZE},
		(Rectangle){
			gameScreen->zoomViewPosition.x,
			gameScreen->zoomViewPosition.y,
			GAME_SCREEN_ZOOM_VIEW_UI_SIZE,
			GAME_SCREEN_ZOOM_VIEW_UI_SIZE,
		},
		(Vector2){0.0, 0.0},
		0.0,
		WHITE
	);

	// Draw cross hair.
	if (cameraId == ZOOM_CAMERA) {
		float halfSize = GAME_SCREEN_ZOOM_VIEW_UI_SIZE / 2.0;
		Vector2 crossHairPosition = Vector2Add(gameScreen->zoomViewPosition, (Vector2){halfSize, halfSize});

		Entity * player = getEntityFromWorld(game->world, 0);
		AntifaShip * data = (AntifaShip*)player->data;
		Color color = data->isOnTarget ? RED : BLUE;

		drawCrossHairPosition(crossHairPosition, 4.0, 2.0, color);
	}

	// Draw outline.
	DrawRectangleLines(
		gameScreen->zoomViewPosition.x,
		gameScreen->zoomViewPosition.y,
		GAME_SCREEN_ZOOM_VIEW_UI_SIZE,
		GAME_SCREEN_ZOOM_VIEW_UI_SIZE,
		GREEN
	);
}

void drawGameOverGameScreen(Game * game, GameScreen * gameScreen) {
	float width = GetScreenWidth();
	float height = GetScreenHeight();

	const char * gameOverMsg = "Game Over";

	Vector2 position = (Vector2){
		width / 2.0 - ((sizeof(gameOverMsg) + 1) * (GAME_SCREEN_GAME_OVER_FONT_SIZE / 2.0) / 2.0),
		height / 3.0
	};

	DrawText(gameOverMsg, position.x, position.y, GAME_SCREEN_GAME_OVER_FONT_SIZE, WHITE);
}

void resetGame(Game * game, GameScreen * gameScreen) {
	gameScreen->gameOver = false;
	gameScreen->lastLevel = 0;
	gameScreen->levelComplete = false;
	resetKillLog(&game->killLog);

	startLevel(game, &game->levels, 0);
}

bool handleGameOver(Game * game, GameScreen * gameScreen) {
	Entity * player = getEntityFromWorld(game->world, 0);

	// Already game over.
	if (gameScreen->gameOver) {

		// To main after after so much time.
		if (GetTime() - gameScreen->gameOverAt >= GAME_SCREEN_SHOW_GAME_OVER_FOR) {
			closeGameScreen(game);
			game->screenId = SCREEN_MAIN_MENU;
			resetGame(game, gameScreen);
		}

		return true;
	}

	// Check if there is player.
	if (!gameScreen->levelComplete) {
		if (player == NULL)
			gameScreen->gameOver = true;
		else if (player->health <= 0.0)
			gameScreen->gameOver = true;
	}

	// End game ):
	if (gameScreen->gameOver) {
		gameScreen->gameOverAt = GetTime();
		endLevel(game, &game->levels);
	}

	return gameScreen->gameOver;
}

void updateGameScreen(Game * game) {
	GameScreen * gameScreen = &game->gameScreen;

	handleGameScreenInput(game, gameScreen);

	ClearBackground(BLACK);

	// Game over ):
	if (handleGameOver(game, gameScreen)) {
		drawGameOverGameScreen(game, gameScreen);
		return;
	}
	
	// Levels indeed.
	gameScreenHandleLevels(game, gameScreen);

	if (gameScreen->levelComplete)
		return;

	// Update world.
	updateWorld(&game->world, game);

	// Camera.
	runCameraUpdate(game, game->cameras, gameScreen->mainCamera);

	// Draw world.
	if (gameScreen->usingWorldRenderTexture) {
		BeginTextureMode(gameScreen->worldRender);
		ClearBackground(BLACK);
		renderWorldGameScreen(game, gameScreen);
		EndTextureMode();

		DrawTexturePro(
			gameScreen->worldRender.texture,
			(Rectangle){0.0, 0.0, game->settings.renderWidth, -game->settings.renderHeight},
			(Rectangle){0.0, 0.0, GetScreenWidth(), GetScreenHeight()},
			(Vector2){0.0, 0.0},
			0.0,
			WHITE
		);
	} else {
		renderWorldGameScreen(game, gameScreen);
	}

	drawZoomViewGameScreen(game, gameScreen);

	// GUI.
	drawGameScreenGui(game);
}

void resizeGameScreen(Game * game, GameScreen * gameScreen) {
	initGameScreenGui(gameScreen);
}

void openGameScreen(Game * game) {
	game->screenId = SCREEN_GAME;

	if (game->settings.lockMouse)
		DisableCursor();
}

void closeGameScreen(Game * game) {
	if (game->settings.lockMouse)
		EnableCursor();
}