#include "scripts/bpong/common.txt"
#include "scripts/Math.txt"
#include "scripts/bpong/ball.txt"
#include "scripts/bpong/pad.txt"
#include "scripts/bpong/wall.txt"
#include "scripts/bpong/scoreboard.txt"

namespace BPong {
	//------------------------------------------------------------------------------------------------------------------------
	const kVec3 CamPos(0.0f, 7000.0f, 0.0f);
	
	const float TopWallZ = 655.0f;
	const float BottomWallZ = -700.0f;
	const float LeftGoalX = -1360.0f;
	const float RightGoalX = 1360.0f;
	
	const float BallSpawnX = 0.0f;
	const float BallSpawnY = 8000.0f;
	const float BallSpawnMinZ = -650.0f;
	const float BallSpawnMaxZ = 600.0f;
	const float BallSpawnCenterZ = -80.0f;
	
	const float PadAreaLeftMinX = -1300.0f;
	const float PadAreaLeftMaxX = -300.0f;
	const float PadAreaRightMinX = 300.0f;
	const float PadAreaRightMaxX = 1300.0f;
	const float PadAreaMinZ = -700.0f;
	const float PadAreaMaxZ = 500.0f;
	
	const float PadCenterZ = -150;
		
	const array<kStr> BallHitSounds = {"sounds/shaders/bullet_impact_1.ksnd",
									"sounds/shaders/bullet_impact_2.ksnd",
									"sounds/shaders/bullet_impact_3.ksnd",
									"sounds/shaders/bullet_impact_4.ksnd",
									"sounds/shaders/bullet_impact_5.ksnd",
									"sounds/shaders/bullet_impact_6.ksnd",
									"sounds/shaders/bullet_impact_7.ksnd",
									"sounds/shaders/bullet_impact_8.ksnd",
									"sounds/shaders/bullet_impact_13.ksnd",
									"sounds/shaders/bullet_impact_14.ksnd",
									"sounds/shaders/bullet_ricochet_1.ksnd",
									"sounds/shaders/bullet_ricochet_2.ksnd",
									"sounds/shaders/bullet_ricochet_3.ksnd",
									"sounds/shaders/bullet_ricochet_4.ksnd" };
									
	const array<kStr> BGSounds = {"sounds/shaders/lightning_strike_1.ksnd",
									"sounds/shaders/lightning_strike_2.ksnd",
									"sounds/shaders/lightning_strike_3.ksnd"};
									
	const array<kStr> P2GoalSounds = {"sounds/shaders/human_effort_injury_grunt_1.ksnd",
									"sounds/shaders/human_effort_injury_grunt_2.ksnd",
									"sounds/shaders/human_effort_injury_grunt_3.ksnd",
									"sounds/shaders/longhunter_taunt_3.ksnd"};
									
	const array<kStr> P1GoalSounds = {"sounds/shaders/demon_death_scream_1.ksnd",
									"sounds/shaders/demon_death_scream_2.ksnd",
									"sounds/shaders/demon_death_scream_3.ksnd",
									"sounds/shaders/demon_effort_injury_grunt_1.ksnd",
									"sounds/shaders/demon_effort_injury_grunt_2.ksnd",
									"sounds/shaders/demon_effort_injury_grunt_3.ksnd"};
	
	enum GameState {
		GS_Playing,
		GS_UserQuit,
		GS_P1Win,
		GS_P2Win
	}
	//------------------------------------------------------------------------------------------------------------------------
	void CameraShake(void) {
		Camera.Tremor().x = Math::RandRange(Math::Deg2Rad(-0.5f), Math::Deg2Rad(0.5f));
		Camera.Tremor().y = Math::RandRange(Math::Deg2Rad(-0.5f), Math::Deg2Rad(0.5f));
		Camera.Tremor().z = Math::RandRange(Math::Deg2Rad(-0.5f), Math::Deg2Rad(0.5f));
	}
	//------------------------------------------------------------------------------------------------------------------------
	void PlayRandomSound(const array<kStr> sounds) {
		Game.PlaySound(sounds[Math::RandMax(sounds.length())]);
	}
	//------------------------------------------------------------------------------------------------------------------------
	class Main {
		array<Ball@> balls(5);
		//Ball @ball = null;
		Wall @wallTop = null;
		Wall @wallBottom = null;
		Pad @pad1 = null;
		Pad @pad2 = null;
		Pad @pad2b = null;
		Scoreboard @score1;
		Scoreboard @score2;
		kActor @bgEffect;
		//kVec3 camPos(0.0f, 7000.0f, 0.0f);
		float moveSpeed = 20.0f;
		int state = GS_Playing;
		int playTicks = 0;
		int bgSoundTick = -1;
		int difficulty = 0;
		//------------------------------------------------------------------------------------------------------------------------
		Main() {
			//Setup Camera and Lock Player
			Camera.StartCinematic(CMF_LOCK_PLAYER|CMF_UNLOCK_PLAYER_ON_FINISH|CMF_NO_INITIAL_FADEOUT|CMF_NO_LETTERBOX);
			Camera.fov = 74.0f;
			Camera.origin = CamPos;

			//Top wall
			@wallTop = Wall(World.GetActorByTID(3));
			wallTop.SetPos(kVec3(0.0f, 8000.0f, 790.0f));
			wallTop.SetScale(kVec3(10.0f, 1.0f, 0.1f));
			
			//Bottom wall
			@wallBottom = Wall(World.GetActorByTID(5));
			wallBottom.SetPos(kVec3(0.0f, 8000.0f, -930.0f));
			wallBottom.SetScale(kVec3(10.0f, 1.0f, 0.1f));
			
			//Pad1
			@pad1 = Pad(World.GetActorByTID(8), true);
			pad1.Pos = kVec3(-1150.0f, 8000.0f, PadCenterZ);
			pad1.Scale = kVec3(0.25f, 0.1f, 0.15f);
			
			//Pad2
			@pad2 = Pad(World.GetActorByTID(9), false);
			pad2.Pos = kVec3(1150.0f, 8000.0f, PadCenterZ);
			pad2.Scale = kVec3(0.25f, 0.1f, 0.15f);
			pad2.aiEnabled = true;
			pad2.Visible = true;
			
			//Pad2b
			@pad2b = Pad(World.GetActorByTID(7), false);
			pad2b.Pos = kVec3(500.0f, 8000.0f, PadCenterZ);
			pad2b.Scale = kVec3(0.25f, 0.1f, 0.15f);
			pad2b.aiEnabled = true;
			pad2b.Visible = false;
			
			//Setup Balls
			for (uint i = 0; i < balls.length(); i++) {
				@balls[i] = Ball(i);
			}
			
			//Scoreboard 1
			@score1 = Scoreboard(false);
			score1.SetPos(kVec3(-530.0f, 7450.0f, 278.0f));
			
			//Scoreboard 2
			@score2 = Scoreboard(true);
			score2.SetPos(kVec3(530.0f, 7450.0f, 278.0f));
			
			//Background
			//Emitter_Misc2 - lightning - pitch 90 degrees
			@bgEffect = ActorFactory.Spawn("Emitter_Misc2", 0.0f, 7250.0f, 0.0f, 0, 0);
			bgEffect.Pitch() = Math::Deg2Rad(90);
		}
		//------------------------------------------------------------------------------------------------------------------------
		~Main() {
			bgEffect.Remove();
		}
		//------------------------------------------------------------------------------------------------------------------------
		void Start(const int newDifficulty) {
			state = GS_Playing;
			difficulty = newDifficulty;
			balls[0].Spawn((Math::Rand() % -2) != 0, false);
			
			if (difficulty == 0) { //Easy
				pad2.aiSpeed = 10.0f;
			} else if (difficulty == 1) { //Normal
				pad2.aiSpeed = 20.0f;
			} else { //Hard
				pad2.aiSpeed = 30.0f;
				pad2b.aiSpeed = 10.0f;
				pad2b.Pos = kVec3(500.0f, 8000.0f, PadCenterZ);
				pad2b.Visible = true;
				balls[1].Spawn((Math::Rand() % -2) != 0, false);
			}
			
			score1.RestoreLifes();
			score2.RestoreLifes();
			bgSoundTick = playTicks + 1500 + Math::RandMax(2000);
		}
		//------------------------------------------------------------------------------------------------------------------------
		void End() {
			HideBalls();
			//reset pad positions
			pad1.Pos = kVec3(-1150.0f, 8000.0f, PadCenterZ);
			pad2.Pos = kVec3(1150.0f, 8000.0f, PadCenterZ);
			pad2b.Visible = false;
			score1.RestoreLifes();
			score2.RestoreLifes();
		}
		//------------------------------------------------------------------------------------------------------------------------
		void HideBalls() {
			for (uint i = 0; i < balls.length(); i++) {
				balls[i].Visible = false;
			}
		}
		//------------------------------------------------------------------------------------------------------------------------
		void ShowTurok() {
			Player.Actor().AnimState().Set(anim_player_idle, 8.0f, ANF_LOOP);
			Player.Actor().Flags() &= ~AF_NODRAW;
			Player.Actor().Origin() = kVec3(0.0f, 7100.0f, -20.0f);
			Player.Actor().Yaw() = Math::Deg2Rad(180);
		}
		//------------------------------------------------------------------------------------------------------------------------
		void HideTurok() {
			Player.Actor().Flags() |= AF_NODRAW;
		}
		//------------------------------------------------------------------------------------------------------------------------
		int GetInActiveBallIndex() {
			for (uint i = 0; i < balls.length(); i++) {
				if (!balls[i].Visible) {
					return i;
				}
			}
			return -1;
		}
		//------------------------------------------------------------------------------------------------------------------------
		void MandatoryUpdate() {
			for (uint i = 0; i < balls.length(); i++) {
				balls[i].UpdateActorGibTimer();
			}
		}
		//------------------------------------------------------------------------------------------------------------------------
		void Update(void) {
			UpdateInput();
			
			for (uint i = 0; i < balls.length(); i++) {
				balls[i].Update(@this);
				if (balls[i].Visible) {
					//Do Multiball Hit
					if (balls[i].multiMode) {
						balls[i].multiMode = false;
						int inactiveBallIndex = GetInActiveBallIndex();
						//can spawn at least 1 more ball and ball is in multiball mode
						if (inactiveBallIndex != -1) {
							//deactivate this ball and spawn 3 more
							balls[i].Visible = false;
							for (uint j = 0; j < 3; j++) {
								balls[inactiveBallIndex].Spawn((Math::Rand() % -2) != 0, true);
								inactiveBallIndex = GetInActiveBallIndex();
								if (inactiveBallIndex == -1) {
									break;
								}
							}
						}
					}
				}
			}

			wallTop.Update();
			wallBottom.Update();
			pad1.Update(@this);
			pad2.Update(@this);
			pad2b.Update(@this);
			score1.Update();
			if (score1.lifes == 0) {
				state = GS_P2Win;
			}
			score2.Update();
			if (score2.lifes == 0) {
				state = GS_P1Win;
			}
			
			//Game.PrintLine("Ticks " + PlayLoop.Ticks(), 1, 60);
			if (PlayLoop.Ticks() % 3 == 0) {
				Game.SpawnFx("fx/spurt_blood.kfx", kVec3(-1270.0f, 8000.0f, 780.0f), 0);
				Game.SpawnFx("fx/spurt_blood.kfx", kVec3(1270.0f, 8000.0f, 780.0f), 0);
			}
			if (PlayLoop.Ticks() % 6 == 0) {
				Game.SpawnFx("fx/spurt_blood.kfx", kVec3(-560.0f, 7440.0f, 360.0f), 0);
				Game.SpawnFx("fx/spurt_blood.kfx", kVec3(560.0f, 7440.0f, 360.0f), 0);
			}
			playTicks++;
			if (bgSoundTick == playTicks) {
				PlayRandomSound(BGSounds);
				bgSoundTick = playTicks + 1500 + Math::RandMax(2000);
			}
		}
		//------------------------------------------------------------------------------------------------------------------------
		void UpdateInput(void) {
			uint16 buttons = Player.Buttons();
    
			if (Camera.UserInterrupted()) {
				state = GS_UserQuit;
				return;
			}
			
			pad1.Pos = InputControls(buttons, pad1.Pos);
			
			//if ((buttons & (BC_WEAPONLEFT)) != 0) {
				//state = GS_P1Win;
			//}
			//if ((buttons & (BC_WEAPONRIGHT)) != 0) {
				//state = GS_P2Win;
			//}
			
			//if ((buttons & BC_JUMP) != 0) {
				//for (uint i = 0; i < balls.length(); i++) {
					//if (balls[i].Visible) {
						//balls[i].multiMode = true;
					//}
				//}
			//}
		}
		//------------------------------------------------------------------------------------------------------------------------
		// Returns a new Pos by using inputs
		//------------------------------------------------------------------------------------------------------------------------
		kVec3 InputControls(uint16 buttons, kVec3 curPos) {
			if ((buttons & BC_FORWARD) != 0) {
				curPos.z += moveSpeed;
				//Game.PrintLine("Z is " + curPos.z, 2, 60);
			}
			if ((buttons & BC_BACKWARD) != 0) {
				curPos.z -= moveSpeed;
				//Game.PrintLine("Z is " + curPos.z, 2, 60);
			}
			if ((buttons & BC_STRAFERIGHT) != 0) {
				curPos.x += moveSpeed;
				//Game.PrintLine("X is " + curPos.x, 2, 60);
			}
			if ((buttons & BC_STRAFELEFT) != 0) {
				curPos.x -= moveSpeed;
				//Game.PrintLine("X is " + curPos.x, 2, 60);
			}
			//if ((buttons & BC_MAPZOOMIN) != 0) {
				//curPos.y += moveSpeed;
				//Game.PrintLine("Y is " + curPos.y, 2, 60);
			//}
			//if ((buttons & BC_MAPZOOMOUT) != 0) {
				//curPos.y -= moveSpeed;
				//Game.PrintLine("Y is " + curPos.y, 2, 60);
			//}
			return curPos;
		}
		//------------------------------------------------------------------------------------------------------------------------
		bool IsRunning(void) {
			return state == GS_Playing;
		}
		//------------------------------------------------------------------------------------------------------------------------
		bool UserQuit(void) {
			return state == GS_UserQuit;
		}
		//------------------------------------------------------------------------------------------------------------------------
		bool Player1Won(void) {
			return state == GS_P1Win;
		}
		//------------------------------------------------------------------------------------------------------------------------
		bool Player2Won(void) {
			return state == GS_P2Win;
		}
		//------------------------------------------------------------------------------------------------------------------------
	}
}
