/*
Copyright (C) 2015-2019 Parallel Realities

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "hud.h"

static void drawPlayerTargeter(void);
static void drawNumFighters(void);
static void drawHealthBars(void);
static void drawWeaponInfo(void);
static void drawObjectives(void);
static void drawDistancesInfo(void);
static void drawHudMessages(void);
static void drawPlayerSelect(void);
static void drawAbilityBars(void);
static void drawBoostECMBar(int current, int max, int x, int y, int r, int g, int b);
static void drawHealthShieldBar(int current, int max, int x, int y, int r, int g, int b, int flashLow);
static void drawSuspicionLevel(void);
static void drawMissileWarning(void);

static HudMessage hudMessageHead;
static HudMessage *hudMessageTail;
static AtlasImage *targetPointer;
static AtlasImage *targetCircle;
static AtlasImage *smallFighter;
static AtlasImage *arrowLeft;
static AtlasImage *arrowRight;
static AtlasImage *armour;
static AtlasImage *shield;
static AtlasImage *ecm;
static AtlasImage *boost;
static AtlasImage *nextGun;
static AtlasImage *clockIcon;
static AtlasImage *objectives;
static int numMessages;
static const char *gunName[BT_MAX];
static char *MISSILES_TEXT;
static char *TARGET_TEXT;
static char *NONE_TEXT;
static char *COMBINED_TEXT;
static char *SYSTEM_POWER_TEXT;
static char *LEADER_DIST_TEXT;
static char *TARGET_DIST_TEXT;
static char *OBJECTIVE_DIST_TEXT;
static char *JUMPGATE_DIST_TEXT;
static char *NEW_FIGHTER_TEXT;
static char *SUSPICION_TEXT;
static char *REMAINING_PILOTS_TEXT;
static char *WARNING_TEXT;

void initHud(void)
{
	memset(&hudMessageHead, 0, sizeof(HudMessage));
	hudMessageTail = &hudMessageHead;

	gunName[BT_NONE] = "";
	gunName[BT_PARTICLE] = _("Particle Cannon");
	gunName[BT_PLASMA] = _("Plasma Cannon");
	gunName[BT_LASER] = _("Laser Cannon");
	gunName[BT_MAG] = _("Mag Cannon");
	gunName[BT_ROCKET] = _("Rockets");
	gunName[BT_MISSILE] = _("Missiles");

	MISSILES_TEXT = _("Missiles (%d)");
	TARGET_TEXT = _("Target: %.2fkm");
	NONE_TEXT = _("(None)");
	COMBINED_TEXT = _("(Combined Guns)");
	SYSTEM_POWER_TEXT = _("System Power : %d%%");
	LEADER_DIST_TEXT = _("%s (Leader)");
	TARGET_DIST_TEXT = _("Target: %.2fkm");
	OBJECTIVE_DIST_TEXT = _("Objective: %.2fkm");
	JUMPGATE_DIST_TEXT = _("Jumpgate: %.2fkm");
	NEW_FIGHTER_TEXT = _("SELECT NEW FIGHTER");
	SUSPICION_TEXT = _("Suspicion");
	REMAINING_PILOTS_TEXT = _("Remaining Pilots: %d");
	WARNING_TEXT = _("WARNING: INCOMING MISSILE!");

	targetPointer = getAtlasImage("gfx/hud/targetPointer.png");
	targetCircle = getAtlasImage("gfx/hud/targetCircle.png");
	smallFighter = getAtlasImage("gfx/hud/smallFighter.png");
	arrowLeft = getAtlasImage("gfx/widgets/optionsLeft.png");
	arrowRight = getAtlasImage("gfx/widgets/optionsRight.png");
	armour = getAtlasImage("gfx/hud/armour.png");
	shield = getAtlasImage("gfx/hud/shield.png");
	ecm = getAtlasImage("gfx/hud/ecm.png");
	boost = getAtlasImage("gfx/hud/boost.png");
	nextGun = getAtlasImage("gfx/hud/nextGun.png");
	clockIcon = getAtlasImage("gfx/hud/clock.png");
	objectives = getAtlasImage("gfx/hud/objectives.png");
}

void doHud(void)
{
	HudMessage *hudMessage, *prev;

	numMessages = 0;

	prev = &hudMessageHead;

	for (hudMessage = hudMessageHead.next ; hudMessage != NULL ; hudMessage = hudMessage->next)
	{
		hudMessage->life--;

		numMessages++;

		if (hudMessage->life <= 0)
		{
			if (hudMessage == hudMessageTail)
			{
				hudMessageTail = prev;
			}

			prev->next = hudMessage->next;
			free(hudMessage);
			hudMessage = prev;

			numMessages--;
		}

		prev = hudMessage;
	}
}

void addHudMessage(SDL_Color c, char *format, ...)
{
	va_list args;

	HudMessage *hudMessage = malloc(sizeof(HudMessage));
	memset(hudMessage, 0, sizeof(HudMessage));
	hudMessageTail->next = hudMessage;
	hudMessageTail = hudMessage;

	va_start(args, format);
	vsprintf(hudMessageTail->message, format, args);
	va_end(args);

	hudMessage->color = c;
	hudMessage->life = FPS * 5;

	numMessages++;

	while (numMessages > MAX_HUD_MESSAGES)
	{
		hudMessage = hudMessageHead.next;
		hudMessageHead.next = hudMessage->next;
		free(hudMessage);

		numMessages--;
	}
}

void drawHud(void)
{
	if (player->alive == ALIVE_ALIVE)
	{
		drawHealthBars();

		drawAbilityBars();

		drawPlayerTargeter();

		drawWeaponInfo();

		drawNumFighters();

		drawObjectives();

		drawDistancesInfo();

		drawRadar();

		drawRadarRangeWarning();

		drawMissileWarning();

		drawSuspicionLevel();
	}

	drawHudMessages();

	if (battle.playerSelect)
	{
		drawPlayerSelect();
	}
}

static void drawHealthBars(void)
{
	float p;
	int r, g, b;

	r = g = b = 0;
	p = player->health;
	p /= player->maxHealth;

	if (p <= 0.25)
	{
		r = 255;
	}
	else if (p <= 0.5)
	{
		r = 255;
		g = 255;
	}
	else
	{
		g = 200;
	}

	setAtlasColor(255, 255, 255, 255);

	blit(armour, 6, 9, 0);
	drawHealthShieldBar(player->health, player->maxHealth, 30, 10, r, g, b, 1);

	blit(shield, 6, 29, 0);
	drawHealthShieldBar(player->shield, player->maxShield, 30, 30, 0, 200, 255, 0);
}

static void drawHealthShieldBar(int current, int max, int x, int y, int r, int g, int b, int flashLow)
{
	SDL_Rect rect;
	float percent = 0;

	if (max > 0)
	{
		percent = current;
		percent /= max;

		if (flashLow && percent <= 0.25 && battle.stats[STAT_TIME] % FPS < 30)
		{
			percent = 0;
		}
	}

	rect.x = x;
	rect.y = y;
	rect.w = 250;
	rect.h = 12;

	SDL_SetRenderDrawColor(app.renderer, r / 2, g / 2, b / 2, 255);
	SDL_RenderFillRect(app.renderer, &rect);

	SDL_SetRenderDrawColor(app.renderer, 255, 255, 255, 255);
	SDL_RenderDrawRect(app.renderer, &rect);

	if (current > 0)
	{
		rect.x += 2;
		rect.y += 2;
		rect.w -= 4;
		rect.h -= 4;

		rect.w *= percent;

		SDL_SetRenderDrawColor(app.renderer, r, g, b, 255);
		SDL_RenderFillRect(app.renderer, &rect);
	}
}

static void drawAbilityBars(void)
{
	setAtlasColor(255, 255, 255, 255);

	blit(boost, 6, 49, 0);
	drawBoostECMBar(battle.boostTimer, BOOST_RECHARGE_TIME, 30, 50, 128, 128, 255);

	blit(ecm, 155, 49, 0);
	drawBoostECMBar(battle.ecmTimer, ECM_RECHARGE_TIME, 175, 50, 255, 128, 0);
}

static void drawBoostECMBar(int current, int max, int x, int y, int r, int g, int b)
{
	SDL_Rect rect;

	float percent = current;
	percent /= max;

	rect.x = x;
	rect.y = y;
	rect.w = 105;
	rect.h = 12;

	SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 255);
	SDL_RenderFillRect(app.renderer, &rect);

	SDL_SetRenderDrawColor(app.renderer, 255, 255, 255, 255);
	SDL_RenderDrawRect(app.renderer, &rect);

	rect.x += 2;
	rect.y += 2;
	rect.w -= 4;
	rect.h -= 4;

	rect.w *= percent;

	if (current < max)
	{
		r /= 2;
		g /= 2;
		b /= 2;
	}

	SDL_SetRenderDrawColor(app.renderer, r, g, b, 255);
	SDL_RenderFillRect(app.renderer, &rect);
}

static void drawWeaponInfo(void)
{
	int i, y;

	setAtlasColor(255, 255, 255, 255);

	if (!player->combinedGuns)
	{
		if (battle.numPlayerGuns)
		{
			y = 70;

			for (i = 0 ; i < BT_MAX ; i++)
			{
				if (playerHasGun(i))
				{
					if (player->selectedGunType == i)
					{
						drawText(30, y, 14, TA_LEFT, colors.green, "%s", gunName[i]);

						blit(nextGun, 8, y + 5, 0);
					}
					else
					{
						drawText(30, y, 14, TA_LEFT, colors.darkGrey, "%s", gunName[i]);
					}

					y += 20;
				}
			}
		}
		else
		{
			drawText(30, 70, 14, TA_LEFT, colors.white, NONE_TEXT);
		}
	}
	else
	{
		drawText(30, 70, 14, TA_LEFT, colors.white, COMBINED_TEXT);
	}

	drawText(280, 70, 14, TA_RIGHT, colors.white, MISSILES_TEXT, player->missiles);
}

static void drawPlayerTargeter(void)
{
	float angle;
	int x, y;

	if (player->target || battle.missionTarget || jumpgateEnabled() || battle.messageSpeaker)
	{
		if (player->target)
		{
			setAtlasColor(255, 0, 0, 255);
		}
		else if (battle.missionTarget)
		{
			setAtlasColor(0, 255, 0, 255);
		}
		else if (battle.messageSpeaker)
		{
			setAtlasColor(255, 255, 255, 255);
		}
		else
		{
			setAtlasColor(255, 255, 0, 255);
		}

		blit(targetCircle, player->x - battle.camera.x, player->y - battle.camera.y, 1);
	}

	if (player->target)
	{
		angle = getAngle(player->x, player->y, player->target->x, player->target->y);
		x = player->x;
		y = player->y;

		x += sin(TO_RAIDANS(angle)) * 45;
		y += -cos(TO_RAIDANS(angle)) * 45;

		setAtlasColor(255, 0, 0, 255);

		blitRotated(targetPointer, x - battle.camera.x, y - battle.camera.y, angle);
	}

	if (battle.missionTarget)
	{
		angle = getAngle(player->x, player->y, battle.missionTarget->x, battle.missionTarget->y);
		x = player->x;
		y = player->y;

		x += sin(TO_RAIDANS(angle)) * 45;
		y += -cos(TO_RAIDANS(angle)) * 45;

		setAtlasColor(0, 255, 0, 255);

		blitRotated(targetPointer, x - battle.camera.x, y - battle.camera.y, angle);
	}

	if (jumpgateEnabled())
	{
		angle = getAngle(player->x, player->y, battle.jumpgate->x, battle.jumpgate->y);
		x = player->x;
		y = player->y;

		x += sin(TO_RAIDANS(angle)) * 45;
		y += -cos(TO_RAIDANS(angle)) * 45;

		setAtlasColor(255, 255, 0, 255);

		blitRotated(targetPointer, x - battle.camera.x, y - battle.camera.y, angle);
	}

	if (battle.messageSpeaker && battle.messageSpeaker != player)
	{
		angle = getAngle(player->x, player->y, battle.messageSpeaker->x, battle.messageSpeaker->y);
		x = player->x;
		y = player->y;

		x += sin(TO_RAIDANS(angle)) * 45;
		y += -cos(TO_RAIDANS(angle)) * 45;

		setAtlasColor(255, 255, 255, 255);

		blitRotated(targetPointer, x - battle.camera.x, y - battle.camera.y, angle);
	}
}

static void drawNumFighters(void)
{
	/* Allies */
	setAtlasColor(150, 200, 255, 255);
	blit(smallFighter, (app.winWidth / 2) - 185, 15, 0);
	drawText((app.winWidth / 2) - 160, 11, 14, TA_LEFT, colors.white, battle.numAllies < 1000 ? "(%d)" : "(999+)", battle.numAllies);

	/* Enemies */
	setAtlasColor(255, 100, 100, 255);
	blit(smallFighter, (app.winWidth / 2) + 170, 15, 0);
	drawText((app.winWidth / 2) + 160, 11, 14, TA_RIGHT, colors.white, !battle.unlimitedEnemies ? "(%d)" : "(999+)", battle.numEnemies);
}

static void drawObjectives(void)
{
	int timeRemaining;

	setAtlasColor(255, 255, 255, 255);

	if (!game.currentMission->challengeData.isChallenge)
	{
		blit(objectives, (app.winWidth / 2) - 50, 14, 0);
		drawText(app.winWidth / 2, 10, 16, TA_CENTER, colors.white, "%d / %d", battle.numObjectivesComplete, (battle.numObjectivesTotal + battle.numConditions));

		if (battle.isEpic && battle.epicLives > 0)
		{
			drawText(app.winWidth / 2, 35, 14, TA_CENTER, colors.white, REMAINING_PILOTS_TEXT, battle.epicLives - 1);
		}
	}
	else
	{
		if (game.currentMission->challengeData.timeLimit)
		{
			timeRemaining = game.currentMission->challengeData.timeLimit - battle.stats[STAT_TIME];

			blit(clockIcon, (app.winWidth / 2) - 50, 14, 0);
			drawText(app.winWidth / 2, 10, 16, TA_CENTER, (timeRemaining < 11 * FPS) ? colors.red : colors.white, timeToString(timeRemaining, 0));
		}
		else
		{
			drawText(app.winWidth / 2, 10, 16, TA_CENTER, colors.white, timeToString(battle.stats[STAT_TIME], 0));
			blit(clockIcon, (app.winWidth / 2) - 50, 14, 0);
		}

		if (game.currentMission->challengeData.killLimit)
		{
			drawText(app.winWidth / 2, 35, 14, TA_CENTER, colors.white, "%d / %d", battle.stats[STAT_ENEMIES_KILLED_PLAYER] + battle.stats[STAT_ENEMIES_DISABLED], game.currentMission->challengeData.killLimit);
		}
		else if (game.currentMission->challengeData.itemLimit)
		{
			drawText(app.winWidth / 2, 35, 14, TA_CENTER, colors.white, "%d / %d", battle.stats[STAT_ITEMS_COLLECTED] + battle.stats[STAT_ITEMS_COLLECTED_PLAYER], game.currentMission->challengeData.itemLimit);
		}
		else if (game.currentMission->challengeData.playerItemLimit)
		{
			drawText(app.winWidth / 2, 35, 14, TA_CENTER, colors.white, "%d / %d", battle.stats[STAT_ITEMS_COLLECTED_PLAYER], game.currentMission->challengeData.playerItemLimit);
		}
		else if (game.currentMission->challengeData.rescueLimit)
		{
			drawText(app.winWidth / 2, 35, 14, TA_CENTER, colors.white, "%d / %d", battle.stats[STAT_CIVILIANS_RESCUED], game.currentMission->challengeData.rescueLimit);
		}
		else if (game.currentMission->challengeData.disableLimit)
		{
			drawText(app.winWidth / 2, 35, 14, TA_CENTER, colors.white, "%d / %d", battle.stats[STAT_ENEMIES_DISABLED], game.currentMission->challengeData.disableLimit);
		}
		else if (game.currentMission->challengeData.surrenderLimit)
		{
			drawText(app.winWidth / 2, 35, 14, TA_CENTER, colors.white, "%d / %d", battle.stats[STAT_ENEMIES_SURRENDERED], game.currentMission->challengeData.surrenderLimit);
		}
		else if (game.currentMission->challengeData.waypointLimit)
		{
			drawText(app.winWidth / 2, 35, 14, TA_CENTER, colors.white, "%d / %d", battle.stats[STAT_WAYPOINTS_VISITED], game.currentMission->challengeData.waypointLimit);
		}
		else if (player->flags & EF_MUST_DISABLE)
		{
			drawText(app.winWidth / 2, 35, 14, TA_CENTER, colors.white, SYSTEM_POWER_TEXT, player->systemPower);
		}
	}
}

static float distanceToKM(int x1, int y1, int x2, int y2)
{
	float distance;

	distance = getDistance(x1, y1, x2, y2);

	/* 2px = 1m approx */
	distance /= 2;
	distance = (int)distance;
	distance /= 1000;

	return distance;
}

static void drawDistancesInfo(void)
{
	int y;
	float distance;

	y = 11;

	if (player->target)
	{
		if (player->target->flags & EF_AI_LEADER && player->target->speed > 0)
		{
			drawText(app.winWidth - 15, y, 18, TA_RIGHT, colors.red, LEADER_DIST_TEXT, player->target->name);
		}
		else
		{
			drawText(app.winWidth - 15, y, 18, TA_RIGHT, colors.red, player->target->name);
		}

		y += 30;

		distance = distanceToKM(player->x, player->y, player->target->x, player->target->y);

		drawText(app.winWidth - 15, y, 14, TA_RIGHT, colors.red, TARGET_DIST_TEXT, distance);

		y += 25;
	}

	if (battle.missionTarget)
	{
		distance = distanceToKM(player->x, player->y, battle.missionTarget->x, battle.missionTarget->y);

		drawText(app.winWidth - 15, y, 14, TA_RIGHT, colors.green, OBJECTIVE_DIST_TEXT, distance);

		y += 25;
	}

	if (jumpgateEnabled())
	{
		distance = distanceToKM(player->x, player->y, battle.jumpgate->x, battle.jumpgate->y);

		drawText(app.winWidth - 15, y, 14, TA_RIGHT, colors.yellow, JUMPGATE_DIST_TEXT, distance);

		y += 25;
	}

	if (battle.messageSpeaker)
	{
		distance = distanceToKM(player->x, player->y, battle.messageSpeaker->x, battle.messageSpeaker->y);

		drawText(app.winWidth - 15, y, 14, TA_RIGHT, colors.white, "%s: %.2fkm", battle.messageSpeaker->name, distance);

		y += 25;
	}
}

static void drawHudMessages(void)
{
	HudMessage *hudMessage;
	int y = app.winHeight - 25;

	for (hudMessage = hudMessageHead.next ; hudMessage != NULL ; hudMessage = hudMessage->next)
	{
		drawText(10, y, 14, TA_LEFT, hudMessage->color, hudMessage->message);

		y -= 25;
	}
}

static void drawPlayerSelect(void)
{
	SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND);
	SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 128);
	SDL_RenderFillRect(app.renderer, NULL);
	SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE);

	setAtlasColor(0, 200, 255, 255);

	blit(targetCircle, player->x - battle.camera.x, player->y - battle.camera.y, 1);

	drawText(app.winWidth / 2, 500, 28, TA_CENTER, colors.white, NEW_FIGHTER_TEXT);

	if (player->health > 0)
	{
		drawText(app.winWidth / 2, 540, 20, TA_CENTER, colors.white, "%s (%d%% / %d%%)", player->defName, getPercent(player->health, player->maxHealth), getPercent(player->shield, player->maxShield));
	}

	setAtlasColor(255, 255, 255, 255);

	blit(arrowLeft, (app.winWidth / 2) - 200, 520, 1);
	blit(arrowRight, (app.winWidth / 2) + 200, 520, 1);
}

static void drawSuspicionLevel(void)
{
	SDL_Rect r;

	if (battle.hasSuspicionLevel && !battle.incomingMissile)
	{
		battle.suspicionLevel = MIN(battle.suspicionLevel, MAX_SUSPICION_LEVEL);

		drawText((app.winWidth / 2) - 150, app.winHeight - 60, 18, TA_RIGHT, colors.white, SUSPICION_TEXT);

		r.x = (app.winWidth / 2) - 140;
		r.y = app.winHeight - 58;
		r.w = 400;
		r.h = 20;

		SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND);
		SDL_SetRenderDrawColor(app.renderer, 0, 0, 0, 128);
		SDL_RenderFillRect(app.renderer, &r);
		SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE);

		SDL_SetRenderDrawColor(app.renderer, 192, 192, 192, 255);
		SDL_RenderDrawRect(app.renderer, &r);

		r.x += 2;
		r.y += 2;
		r.w -= 4;
		r.h -= 4;

		r.w = MAX((r.w / MAX_SUSPICION_LEVEL) * battle.suspicionLevel, 0);

		if (battle.suspicionLevel < (MAX_SUSPICION_LEVEL * 0.5))
		{
			SDL_SetRenderDrawColor(app.renderer, 255, 255, 255, 255);
		}
		else if (battle.suspicionLevel < (MAX_SUSPICION_LEVEL * 0.75))
		{
			SDL_SetRenderDrawColor(app.renderer, 255, 128, 0, 255);
		}
		else
		{
			SDL_SetRenderDrawColor(app.renderer, 255, 0, 0, 255);
		}

		SDL_RenderFillRect(app.renderer, &r);

		drawText(r.x + r.w + 7, app.winHeight - 57, 12, TA_LEFT, colors.white, "%d%%", (battle.suspicionLevel > 0) ? getPercent(battle.suspicionLevel, MAX_SUSPICION_LEVEL) : 0);
	}
}

static void drawMissileWarning(void)
{
	if (battle.incomingMissile && battle.stats[STAT_TIME] % FPS < 40)
	{
		drawText(app.winWidth / 2, app.winHeight - 60, 18, TA_CENTER, colors.red, WARNING_TEXT);
	}
}

void resetHud(void)
{
	HudMessage *hudMessage;

	while (hudMessageHead.next)
	{
		hudMessage = hudMessageHead.next;
		hudMessageHead.next = hudMessage->next;
		free(hudMessage);
	}
}
