#include "gameframe.h"
#include "gameplayer.h"
#include "types.h"
#include "game_planet.h"
#include "game_stats.h"


GameFrame::GameFrame()
	: GenericFrame("game")
	, frozen(false)
	, debug(false)
	, running(true)
	, firstCollected(true)
	, elapsedTime(0)
	, planetN(0)
{
	fieldSize = Vector2f(1024, 640);
}


void GameFrame::Init()
{
	std::cout << "---------------------------------\nStarting stage\n" << std::endl;

	// Create planet
	nextPlanet();

	// Play music
	Audio::get()->playMusic("music01.ogg", 1);

	GameStats::reset();
}


void GameFrame::DeInit()
{
}


//#include <boost/pool/pool.hpp>
//static boost::pool<> pool(128);

bool GameFrame::DoUpdate(float time)
{
	elapsedTime += time;
	setInputBindings();

	if (playerInput->isButtonPressed(Player_Freeze)) {
		frozen = !frozen;
	}

	if (playerInput->isButtonPressed(Player_Toggle_Debug)) {
		debug = !debug;
	}

	if (playerInput->isButtonPressed(Player_Action1) && planetN == 5) {
		GameStats::finished = true;
		Switch("title");
		return true;
	}

	if (playerInput->isButtonPressed(Player_Menu)) {
		running = false;
	}

	// Check planet
	if (planet) {
		float left = planet->getTimeLeft();
		if (left < -LAND_TIME) {
			nextPlanet();
		}
	}

	if (messageTime > 0) {
		messageTime -= time;
	}

	// Main stepping
	collision.reset();
	stepAllObjects(time);
	spawnPending(time);
	collision.runCollisions(sprites);

	return running;
}


void GameFrame::nextPlanet()
{
	if (planet) {
		planet->die();
	}
	if (planetN < 5) {
		planet = shared_ptr<GamePlanet>(new GamePlanet(playerInput, this, planetN));
		spawn(planet);
		if (planetN == 4) {
			//Audio::get()->stopMusic();
		}
	}
	planetN++;
}


void GameFrame::stepAllObjects(float time)
{
	for (auto cur=sprites.begin(); cur!=sprites.end(); cur++) {
		stepObjects(time, cur->second);
	}
}


static int sq(int a) {
	return a*a;
}


void GameFrame::DoRender()
{
	glClearColor(0, 0, 0, 0);
	glClear(GL_COLOR_BUFFER_BIT);

	glPushMatrix();
	glTranslatef(1024/2, 0, 0);

	// Planet
	float tLeft = planet->getTimeLeft();
	float plOff = planet->getOffset();

	renderer.startRendering();

	auto text = TextDrawer::GetDrawer("ocraext.ttf", 22, true, false);
	text->SetColour(Colour(0.72f, 0.72f, 0.72f));
	text->SetBorder(Colour(0.10f, 0.10f, 0.10f), 2);

	// Draw background
	int start = -512;
	int off = planet ? planet->getBackgroundScroll() % 1024 : 0;
	if (off < 0) {
		off += 1024;
	}
	for (int i=0; i<2; i++) {
		float alpha = elapsedTime / (60*6);
		if (planetN == 5) alpha = 0;
		float voff = max(640.0f-1024.0f, -plOff/2.0f);
		renderer.drawImage(Vector2f(start + off + (i-1) * 1024, voff), Vector2f(0, 0), TextureManager::get("sky.png")->GetId(), 0, 1, 0);
		renderer.drawImage(Vector2f(start + off + (i-1) * 1024, voff), Vector2f(0, 0), TextureManager::get("sky1.png")->GetId(), 0, 1, 0, false, Colour(1, 1, 1, alpha));
	}

	// Draw halos
	Vector2f haloPos = Vector2f(-512, 100-plOff);
	float aHalo0 = tLeft > 60 ? 1.0f : max(0.0f, (tLeft-30) / 30.0f);
	float aHalo1 = tLeft > 60 ? 0 : (tLeft > 30 ? 1-aHalo0 : tLeft / 30.0f);
	float aHalo2 = tLeft > 30 ? 0 : 1-aHalo1;
	if (aHalo0 > 0) renderer.drawImage(haloPos, Vector2f(0, 0), TextureManager::get("halo0.png")->GetId(), 0, 1, 0, false, Colour(1, 1, 1, aHalo0));
	if (aHalo1 > 0) renderer.drawImage(haloPos, Vector2f(0, 0), TextureManager::get("halo1.png")->GetId(), 0, 1, 0, false, Colour(1, 1, 1, aHalo1));
	if (aHalo2 > 0) renderer.drawImage(haloPos, Vector2f(0, 0), TextureManager::get("halo2.png")->GetId(), 0, 1, 0, false, Colour(1, 1, 1, aHalo2));

	// Draw sprites
	renderer.renderAllLayers();

	if (messageTime > 0) {
		text->Print(message, Vector2f(0, 450), Vector2f(0.5f, 0.5f));
	}

	// Draw HUD
	if (planetN < 5) {
		renderer.drawImage(Vector2f(0, 540), Vector2f(0.5f, 0.5f), TextureManager::get("hud.png")->GetId(), 0, 1, 0);
		String str = "";
		if (planet) str = str + ": planet: " + planet->getName() + " :";
		str = str + "\n: unit: NoAH-X"+ String::IntegerToString(GameStats::lives+2)+" :";
		str = str + "\n: ........... :";
		if (tLeft >= 0) {
			char leftStr[64];
			sprintf(leftStr, "%02i:%02i.%03i", int(tLeft)/60, int(tLeft)%60, min(999, int(fmod(tLeft, 1.0f)*1000)));
			str = str + "\n: time left: " + leftStr + " :";
		}
		text->Print(str, Vector2f(-462, 575), Vector2f(0, 0));

		if (planet) {
			// Draw inventory
			spGamePlayer player = planet->player;
			if (player) {
				for (size_t i=0; i<player->inventory.size(); i++) {
					ObjectType t = player->inventory[i];
					int id = TextureManager::get(GamePlanetObject::getCollectibleImage(t))->GetId();
					renderer.drawImage(Vector2f(345+i*90, 570), Vector2f(0.5, 0.5), id, 0, 1, 0);
				}

				if (player->inventory.size() > 0 && firstCollected) {
					firstCollected = false;
					showMessage("Take the resource back to your ship to collect it.");
				}
			}

			// Draw arrows
			float left = planet->getTimeLeft();
			if (left < 15.0f && left > 0) {
				showMessage("Warning: The planet is about to burn! Get to your ship!", 0.1f);

				Angle1f ang = Angle1f::FromRadians(planet->getAngle());
				int arrow = 0;
				if (fabs(ang.Distance(Angle1f::FromRadians(0)).GetRadians()) > 1.2f) {
					if (ang.TurnSide(Angle1f::FromRadians(0)) < 0) arrow = -1;
					else arrow = 1;
					if (arrow != 0) {
						int n = int(elapsedTime * 5) % 3;
						int id = TextureManager::get("arrow"+String::IntegerToString(n)+".png")->GetId();
						Vector2f pos(-400, 300);
						if (arrow == 1) pos.x *= -1;
						renderer.drawImage(pos, Vector2f(0.5, 0.5), id, 0, 1, 0, arrow == 1);
					}
				}
			}
		}
	}


	// Score breakdown
	else {
		String str = "You have reached Terra Nova! You have rescued:";
		text->Print(str, Vector2f(-462, 585), Vector2f(0, 0));
		const char* groups[] = { "Plants", "Animals", "People", "Rarities" };

		std::vector<int> scores;
		std::vector<int> scoresEx;
		scores.resize(4);
		scoresEx.resize(4);

		for (int i=0; i<4; i++) {
			str = String(groups[i]) + ":\n";
			for (int j=0; j<5; j++) {
				ObjectType type = (ObjectType) (Object_Collectible0_0 + (i*5 + j));
				int n = 0;
				for (size_t k=0; k<GameStats::collected.size(); k++) {
					if (GameStats::collected[k] == type) n++;
				}
				if (n > 0) {
					scores[i]++;
					if (j==4) scoresEx[i]++;
				}

				str += String::IntegerToString(n) + "x " + GamePlanetObject::getCollectibleName(type) + "\n";
			}

			int dx = i * 250;
			text->Print(str, Vector2f(-462 + dx, 520), Vector2f(0, 0));
		}

		int happy = (scores[2] + scores[1]) * (1 + scoresEx[0] + scoresEx[1] + scoresEx[2]);
		int develop = (scores[2] + scores[0]) * (1 + scoresEx[0] + scoresEx[1] + scoresEx[2]);
		int sustain = ((scores[0] * scores[1]) - (scores[2] * 2))*2;
		int years = sq(max(0, develop + sustain)) + sq(happy + sq(scoresEx[3]) * scoresEx[3]);
		text->Print("Happiness: "+String::IntegerToString(happy), Vector2f(-400, 220), Vector2f(0, 0));
		text->Print("Development: "+String::IntegerToString(develop), Vector2f(-80, 220), Vector2f(0, 0));
		text->Print("Sustainability: "+String::IntegerToString(sustain), Vector2f(180, 220), Vector2f(0, 0));
		text->Print("Your civilization shall thrive for at least "+String::IntegerToString(years) + " years!", Vector2f(-400, 180), Vector2f(0, 0));
		text->Print(String::IntegerToString(GameStats::lives)+" NoAHs were harmed in the making of this planet.", Vector2f(-400, 100), Vector2f(0, 0));
		text->Print("Press \"Jump\" to exit.", Vector2f(0, 50), Vector2f(0.5f, 0));
	}


	// Done drawing
	renderer.endRendering();

	glPopMatrix();
}


void GameFrame::spawn(spSprite sprite)
{
	sprite->setParent(this);
	toSpawn.push_back(sprite);
}


void GameFrame::spawnPending(float t)
{
	while (!toSpawn.empty()) {
		SpriteList spawning;
		std::swap(toSpawn, spawning);
		for (auto cur=spawning.begin(); cur != spawning.end(); cur++) {
			doSpawn(*cur);
		}
		stepObjects(0, spawning);	// Step with t=0
	}
}


void GameFrame::doSpawn(spSprite sprite)
{
	// Collision/stepping list
	Collision::Mask colType = sprite->getColType().mask;
	sprites[colType].push_back(sprite);

	// Rendering list
	renderer.addSprite(sprite);
}


void GameFrame::stepObjects(float t, SpriteList& objs)
{
	auto end = objs.end();
	for (auto cur=objs.begin(); cur != end; ) {
		spSprite& obj = *cur;
		bool alive = obj->isAlive() && obj->step(t);
		
		if (!alive) {
			auto next = cur;
			next++;
			renderer.removeSprite(obj);
			objs.erase(cur);
			cur = next;
		}

		else {
			// Add trigger if it exists
			if (obj->hasTriggers()) {
				collision.addTriggerMap(obj, obj->getTriggers());
			}

			cur++;
		}
	}
}

void GameFrame::increaseLives()
{
	GameStats::lives++;
}

void GameFrame::showMessage(String msg, float t)
{
	if (messageTime <= 0 || msg != message) Audio::get()->play("messagereceived.ogg");
	message = msg;
	messageTime = t;
}

