/*
Copyright (C) 2003 Parallel Realities
Copyright (C) 2011, 2012, 2013 Guus Sliepen
Copyright (C) 2012, 2015-2020 Layla Marchant <diligentcircle@riseup.net>

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 3
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, see <http://www.gnu.org/licenses/>.
*/

#include <math.h>
#include <stdlib.h>

#include "SDL.h"

#include "defs.h"
#include "structs.h"

#include "alien.h"
#include "audio.h"
#include "engine.h"
#include "colors.h"
#include "game.h"
#include "gfx.h"
#include "info.h"
#include "player.h"
#include "renderer.h"
#include "screen.h"
#include "weapons.h"
#include "window.h"

Object player;
int player_chargerFired = 0;
int player_damageDelay = 0;
int player_resetDamageDelay = 0;

/*
Initialises the player for a new game.
*/
void player_init()
{
	player.active = 1;
	player.x = screen->w / 2;
	player.y = screen->h / 2;
	player.speed = 2;
	player.systemPower = player.maxShield;
	player.face = 0;

	player.image[0] = gfx_shipSprites[SS_FIREFLY];
	player.image[1] = gfx_shipSprites[SS_FIREFLY_L];

	player.engineX = player.image[0]->w;
	player.engineY = (player.image[0]->h / 2);

	player.owner = &player;
	player.flags = FL_FRIEND;

	player.weaponType[0] = W_PLAYER_WEAPON;

	if (weapons[W_PLAYER_WEAPON].ammo[0] < game.minPlasmaOutput)
		weapons[W_PLAYER_WEAPON].ammo[0] = game.minPlasmaOutput;
	if (weapons[W_PLAYER_WEAPON].damage < game.minPlasmaDamage)
		weapons[W_PLAYER_WEAPON].damage = game.minPlasmaDamage;
	if (weapons[W_PLAYER_WEAPON].reload[0] > rate2reload[game.minPlasmaRate])
		weapons[W_PLAYER_WEAPON].reload[0] = rate2reload[game.minPlasmaRate];

	player.hit = 0;

	engine.lowShield = (player.maxShield >= 3) ? (player.maxShield / 3) : 1;
	engine.averageShield = engine.lowShield + engine.lowShield;

	if ((player.weaponType[1] == W_CHARGER) || (player.weaponType[1] == W_LASER))
		player.ammo[1] = 0;
}

void player_setTarget(int index)
{
	engine.targetIndex = index;
}

/*
Attempt to damage the player by ``amount``.  If ``delay`` is specified,
delay any damage dealt by that number of frames (i.e. require that
number of frames of continuous damage before it registers).
Return 1 if damage is inflicted, 0 otherwise.
*/
void player_damage(int amount, int delay)
{
	int oldshield = player.shield;

	player_resetDamageDelay = 0;

	if ((!engine.cheatShield) && (engine.missionCompleteTimer == 0) &&
			((!player.hit) ||
				(game.difficulty == DIFFICULTY_ORIGINAL) ||
				((player.shield != engine.lowShield) &&
					(player.shield != 1))))
	{
		if ((game.difficulty == DIFFICULTY_ORIGINAL) ||
				(player_damageDelay >= delay))
		{
			player.shield -= amount;

			LIMIT(player.shield, 0, player.maxShield);
			player.hit = 5; // Damage flash timer
			audio_playSound(SFX_HIT, player.x, player.y);

			// Damage tiers (not in Classic mode)
			if ((oldshield > engine.lowShield) &&
					(player.shield <= engine.lowShield))
			{
				info_setLine("!!! WARNING: SHIELD LOW !!!", FONT_RED);
				if (game.difficulty != DIFFICULTY_ORIGINAL)
				{
					player.shield = engine.lowShield;
					player_damageDelay = 0;
				}
			}
			else if ((oldshield > 1) && (player.shield <= 1))
			{
				info_setLine("!!! WARNING: SHIELD CRITICAL !!!", FONT_RED);
				if (game.difficulty != DIFFICULTY_ORIGINAL)
				{
					player.shield = 1;
					player_damageDelay = 0;
				}
			}
		}
		else
		{
			player_damageDelay += amount;
		}
	}
}

void player_checkShockDamage(float x, float y)
{
	float distX = fabsf(x - player.x);
	float distY = fabsf(y - player.y);

	// Don't let the player be hurt by an explosion after they have completed
	// all the mission Objectives. That would be *really* annoying!
	if ((engine.cheatShield) || (engine.missionCompleteTimer != 0))
		return;

	if ((distX <= 50) && (distY <= 50))
	{
		if (distX >= 1)
			distX /= 5;

		if (distY >= 1)
			distY /= 5;

		player_damage((int)((10 - distX) + (10 - distY)), 0);
		player.hit = 10;
	}
}

void player_exit()
{
	player_chargerFired = 0;

	if ((player.weaponType[1] == W_CHARGER) || (player.weaponType[1] == W_LASER))
		player.ammo[1] = 0;
}

void player_flushInput()
{
	for (int i = 0; i < KEY_LAST; i++)
		engine.keyState[i] = 0;

	while (SDL_PollEvent(&engine.event)){}
}

static enum keys mapkey(int code) {
	switch (code) {
		case SDLK_UP:
		case SDLK_KP_8:
			return KEY_UP;
		case SDLK_DOWN:
		case SDLK_KP_2:
		case SDLK_KP_5:
			return KEY_DOWN;
		case SDLK_LEFT:
		case SDLK_KP_4:
			return KEY_LEFT;
		case SDLK_RIGHT:
		case SDLK_KP_6:
			return KEY_RIGHT;
		case SDLK_LCTRL:
		case SDLK_RCTRL:
		case SDLK_RETURN:
		case SDLK_z:
		case SDLK_y:
		case SDLK_c:
		case SDLK_a:
		case SDLK_d:
		case SDLK_f:
		case SDLK_SLASH:
		case SDLK_COMMA:
		case SDLK_1:
		case SDLK_3:
		case SDLK_KP_0:
		case SDLK_HOME:
		case SDLK_END:
			return KEY_FIRE;
		case SDLK_SPACE:
		case SDLK_x:
		case SDLK_s:
		case SDLK_PERIOD:
		case SDLK_2:
		case SDLK_KP_1:
		case SDLK_PAGEUP:
		case SDLK_PAGEDOWN:
			return KEY_ALTFIRE;
		case SDLK_LSHIFT:
		case SDLK_RSHIFT:
		case SDLK_LALT:
		case SDLK_RALT:
		case SDLK_KP_7:
		case SDLK_KP_9:
			return KEY_SWITCH;
		case SDLK_p:
		case SDLK_PAUSE:
			return KEY_PAUSE;
		case SDLK_ESCAPE:
		case SDLK_q:
		case SDLK_BACKSPACE:
		case SDLK_DELETE:
			return KEY_ESCAPE;
		case SDLK_F11:
			return KEY_FULLSCREEN;
		default:
			return KEY_DUMMY;
	}
}

void player_getInput()
{
	static int prevjoyup = 0, prevjoydown = 0, prevjoyleft = 0, prevjoyright = 0;
	int joyup, joydown, joyleft, joyright;
	double val;
	static int px = -1, py = -1;
	int x, y, w, h;

	while (SDL_PollEvent(&engine.event))
	{
		switch (engine.event.type)
		{
			case SDL_QUIT:
				exit(0);
				break;

			case SDL_MOUSEBUTTONDOWN:
				if (engine.gameSection == SECTION_INTERMISSION)
				{
					if (engine.event.button.button == SDL_BUTTON_LEFT)
						engine.keyState[KEY_FIRE] = 1;
					if (engine.event.button.button == SDL_BUTTON_RIGHT)
						engine.keyState[KEY_ALTFIRE] = 1;
				}
				break;

			case SDL_KEYDOWN:
				if (!engine.event.key.repeat)
				{
					engine.keyState[mapkey(engine.event.key.keysym.sym)] = 1;

					if (engine.gameSection != SECTION_GAME)
						engine.paused = 0;
				}
				break;

			case SDL_KEYUP:
				if (engine.event.key.keysym.sym != SDLK_p)
					engine.keyState[mapkey(engine.event.key.keysym.sym)] = 0;
				break;

			case SDL_JOYBUTTONDOWN:
			case SDL_JOYBUTTONUP:
				if (!engine.useController)
				{
					switch (engine.event.jbutton.button)
					{
						case 0:
						case 3:
							engine.keyState[KEY_ALTFIRE] = CSDLP(
								engine.event.jbutton.state);
							break;
						case 1:
						case 2:
							engine.keyState[KEY_FIRE] = CSDLP(
								engine.event.jbutton.state);
							break;
						case 4:
						case 6:
							engine.keyState[KEY_ESCAPE] = CSDLP(
								engine.event.jbutton.state);
							break;
						case 5:
						case 7:
						case 8:
							engine.keyState[KEY_SWITCH] = CSDLP(
								engine.event.jbutton.state);
							break;
						case 9:
							engine.keyState[KEY_PAUSE] = CSDLP(
								engine.event.jbutton.state);
							break;
					}
				}
				break;
			
			case SDL_CONTROLLERBUTTONDOWN:
			case SDL_CONTROLLERBUTTONUP:
				if (engine.useController)
				{
					switch (engine.event.cbutton.button)
					{
						case SDL_CONTROLLER_BUTTON_A:
						case SDL_CONTROLLER_BUTTON_Y:
							engine.keyState[KEY_ALTFIRE] = CSDLP(
								engine.event.cbutton.state);
							break;
						case SDL_CONTROLLER_BUTTON_B:
						case SDL_CONTROLLER_BUTTON_X:
							engine.keyState[KEY_FIRE] = CSDLP(
								engine.event.cbutton.state);
							break;
						case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
							engine.keyState[KEY_ESCAPE] = CSDLP(
								engine.event.cbutton.state);
							break;
						case SDL_CONTROLLER_BUTTON_BACK:
						case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
							engine.keyState[KEY_SWITCH] = CSDLP(
								engine.event.cbutton.state);
							break;
						case SDL_CONTROLLER_BUTTON_START:
							engine.keyState[KEY_PAUSE] = CSDLP(
								engine.event.cbutton.state);
							break;
						case SDL_CONTROLLER_BUTTON_DPAD_UP:
							engine.keyState[KEY_UP] = CSDLP(
								engine.event.cbutton.state);
							break;
						case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
							engine.keyState[KEY_DOWN] = CSDLP(
								engine.event.cbutton.state);
							break;
						case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
							engine.keyState[KEY_LEFT] = CSDLP(
								engine.event.cbutton.state);
							break;
						case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
							engine.keyState[KEY_RIGHT] = CSDLP(
								engine.event.cbutton.state);
							break;
					}
				}
				break;

			case SDL_JOYHATMOTION:
				if (!engine.useController)
				{
					engine.keyState[KEY_UP]    = (engine.event.jhat.value & SDL_HAT_UP ? 1 : 0);
					engine.keyState[KEY_DOWN]  = (engine.event.jhat.value & SDL_HAT_DOWN ? 1 : 0);
					engine.keyState[KEY_LEFT]  = (engine.event.jhat.value & SDL_HAT_LEFT ? 1 : 0);
					engine.keyState[KEY_RIGHT] = (engine.event.jhat.value & SDL_HAT_RIGHT ? 1 : 0);
				}
				break;

			case SDL_JOYAXISMOTION:
				if (!engine.useController)
				{
					if (engine.gameSection == SECTION_TITLE) {
						if (engine.event.jaxis.axis == 0) {
							joyleft = engine.event.jaxis.value < -16384;
							joyright = engine.event.jaxis.value >= 16384;
							if (joyleft != prevjoyleft)
								engine.keyState[KEY_LEFT] = prevjoyleft = joyleft;
							if (joyright != prevjoyright)
								engine.keyState[KEY_RIGHT] = prevjoyright = joyright;
						} else if (engine.event.jaxis.axis == 1) {
							joyup = engine.event.jaxis.value < -16384;
							joydown = engine.event.jaxis.value >= 16384;
							if (joyup != prevjoyup)
								engine.keyState[KEY_UP] = prevjoyup = joyup;
							if (joydown != prevjoydown)
								engine.keyState[KEY_DOWN] = prevjoydown = joydown;
						}
					} else {
						val = MIN(1, (double)(abs(engine.event.jaxis.value)) / JS_MAX);
						if (val < JS_DEADZONE)
							val = 0;

						if (engine.event.jaxis.axis == 0)
							engine.xaxis = copysign(val, engine.event.jaxis.value);
						else if (engine.event.jaxis.axis == 1)
							engine.yaxis = copysign(val, engine.event.jaxis.value);
					}
				}

				break;

			case SDL_CONTROLLERAXISMOTION:
				if (engine.useController)
				{
					if (engine.gameSection == SECTION_TITLE) {
						if (engine.event.caxis.axis == SDL_CONTROLLER_AXIS_LEFTX)
						{
							joyleft = engine.event.caxis.value < -16384;
							joyright = engine.event.caxis.value >= 16384;
							if (joyleft != prevjoyleft)
								engine.keyState[KEY_LEFT] = prevjoyleft = joyleft;
							if (joyright != prevjoyright)
								engine.keyState[KEY_RIGHT] = prevjoyright = joyright;
						}
						else if (engine.event.caxis.axis == SDL_CONTROLLER_AXIS_LEFTY)
						{
							joyup = engine.event.caxis.value < -16384;
							joydown = engine.event.caxis.value >= 16384;
							if (joyup != prevjoyup)
								engine.keyState[KEY_UP] = prevjoyup = joyup;
							if (joydown != prevjoydown)
								engine.keyState[KEY_DOWN] = prevjoydown = joydown;
						}
					} else {
						val = MIN(1, (double)(abs(engine.event.caxis.value)) / JS_MAX);
						if (val < JS_DEADZONE)
							val = 0;

						if (engine.event.caxis.axis == SDL_CONTROLLER_AXIS_LEFTX)
							engine.xaxis = copysign(val, engine.event.caxis.value);
						else if (engine.event.caxis.axis == SDL_CONTROLLER_AXIS_LEFTY)
							engine.yaxis = copysign(val, engine.event.caxis.value);
					}
				}

				break;

			case SDL_WINDOWEVENT:
				if (engine.event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
				{
					screen_adjustDimensions(engine.event.window.data1, engine.event.window.data2);
					renderer_reset();
					gfx_scaleBackground();
					screen_clear(black);
					renderer_update();
					screen_clear(black);
					screen_addBuffer(0, 0, screen->w, screen->h);
				}

				if (engine.autoPause &&
						(engine.event.window.event == SDL_WINDOWEVENT_FOCUS_LOST))
					engine.paused = 1;
				break;
		}

		if (engine.keyState[KEY_FULLSCREEN])
		{
			engine_setFullscreen(!engine.fullScreen);
			engine.keyState[KEY_FULLSCREEN] = 0;
		}
	}

	if (engine.gameSection == SECTION_INTERMISSION)
	{
		// Get the current mouse position
		SDL_GetMouseState(&x, &y);
		SDL_GetWindowSize(window, &w, &h);
		x = screen->w * x / w;
		y = screen->h * y / h;
		if (px == x && py == y) {
			if (engine.keyState[KEY_UP])
				engine.cursor_y -= 4;
			if (engine.keyState[KEY_DOWN])
				engine.cursor_y += 4;
			if (engine.keyState[KEY_LEFT])
				engine.cursor_x -= 4;
			if (engine.keyState[KEY_RIGHT])
				engine.cursor_x += 4;
			if (engine.xaxis)
				engine.cursor_x += 10 * engine.xaxis;
			if (engine.yaxis)
				engine.cursor_y += 10 * engine.yaxis;
		} else {
			engine.cursor_x = px = x;
			engine.cursor_y = py = y;
		}
	}

}

void player_leaveSector()
{
	engine.keyState[KEY_UP] = 0;
	engine.keyState[KEY_DOWN] = 0;
	engine.keyState[KEY_LEFT] = 0;
	engine.keyState[KEY_RIGHT] = 0;
	engine.keyState[KEY_FIRE] = 0;
	engine.keyState[KEY_ALTFIRE] = 0;
	engine.xaxis = 0;
	engine.yaxis = 0;

	if (engine.done == ENGINE_RUNNING)
		engine.done = ENGINE_FORMATION;

	if (engine.done == ENGINE_FORMATION)
	{
		player.face = 0;
		if (player.x > -100)
		{
			player.x += engine.ssx;
			engine.ssx -= 1;
			if (player.y > screen->h / 2)
				player.y--;
			if (player.y < screen->h / 2)
				player.y++;
		}

		if (player.x <= -100)
		{
			engine.done = ENGINE_SYSEXIT;
			audio_playSound(SFX_FLY, screen->w / 2, screen->h / 2);
		}
	}

	if (engine.done == ENGINE_SYSEXIT)
	{
		player.face = 0;
		player.x += WARP_SPEED;
		engine.ssx -= 0.2;
		if (player.x > (2 * screen->w))
			engine.done = ENGINE_CLOSING;
	}
}

