/*
 * PlayState.cpp
 *
 *  Created on: 2009-07-10
 *      Author: wasyl
 */

#include "PlayState.h"

#include <string>
#include <allegro5/allegro.h>
#include <allegro5/a5_opengl.h>
#include "../prep/Common.h"
#include "../prep/ent/Entity.h"
#include "../prep/ent/World.h"
#include "../thing/Avatar.h"
#include "../thing/Level.h"
#include "../thing/Cloud.h"
#include "../thing/PopUp.h"
#include "../misc/LoadSave.h"
#include "../misc/Controls.h"
#include "../misc/Config.h"
#include "../misc/Theme.h"
#include "../prep/misc/SPrintF.h"
#include "../prep/a5dumb.h"
#include "../gui/GuiText.h"

PlayState::PlayState(int no, const char *file)
	: AppState<int>(al_get_current_display()), mNo(no), mQuit(ALLEGRO_KEY_ESCAPE)
{
	mZoom = 1.0;
	mPostMortem = NULL;

	LoadSave().load(file, &mWorld);

	Game *game = new Game();
	game->setAvatar( (Avatar*) mWorld.entity(ID("avat")));
	Game::set(game);

	for (int i = 0; i < 24; i++)
		mWorld.add(new Cloud());
	mWorld.reindex();

	game->level()->addTiles(&mWorld);
	Tile::resetPresent();

	if (no == -2)
	{
		Theme::set(0);
		al_stop_module();
		al_play_module("media/unreal_superhero.xm", 0, false);

		mWorld.add(new GuiText(Vec(400.0, 40), "WELL DONE!"));
		mWorld.add(new GuiText(Vec(400.0, 80), "You have completed the game!"));
		if (Config::get().unlockPoints() == Config::get().numLevels() * 3)
			mWorld.add(new GuiText(Vec(400.0, 120), "And you have all the unlock points, I'm impressed!"));
		else
			mWorld.add(new GuiText(Vec(400.0, 120), "Now try to earn all the unlock points on all the levels!"));
	}

	mWorld.reindex();
}

PlayState::~PlayState()
{
	delete Game::get();
	Game::set(NULL);
}

void PlayState::update(double dt)
{
	Theme::active().update(dt);

	Entities &dynamics = mWorld.index(ID("dyna"));
	FOREACH(vector,Entity*,e,dynamics)
		(*e)->update(dt);
	mWorld.reindex();

	if (Controls::get().quit->on())
		finish(0);
	if (Controls::get().restart->on())
		finish(1);

	Avatar *avatar = (Avatar*)mWorld.entity(ID("avat"));
	if (!avatar)
		return;

	Avatar::WinState ws = avatar->winState();
	if (ws.state == Avatar::WON)
	{
		if (!mPostMortem)
		{
			int grbCount = Game::get()->level()->garbageCount();
			int grb = Game::get()->counter(Game::GARBAGE);
			bool completion100 = (grb == grbCount);

			Config::get().levelComplete(mNo, completion100, Game::get()->dbCounter(Game::TIME));

			PopUp *pm = new PopUp();
			pm->addLine("You have completed the level!", "");
			pm->addLine("","");

			int grbPrc = grbCount ? (grb * 100.0 / grbCount) : 0;
			pm->addLine("Loose garbage collected:", SPrintF::format("%d%%", grbPrc));
			pm->addLine("Elapsed time:",  SPrintF::format("%.2lfs", Game::get()->dbCounter(Game::TIME)));
			pm->addLine("Target time:",  SPrintF::format("%.2lfs", Config::get().timeAttackTime(mNo)));

			pm->addLine("Jumps performed:",  SPrintF::format("%d", Game::get()->counter(Game::JUMPS)));
			pm->addLine("Double jumps:"   ,  SPrintF::format("%d", Game::get()->counter(Game::DOUBLE_JUMPS)));
			pm->addLine("Pixels traversed:", SPrintF::format("%d", Game::get()->counter(Game::TRAVERSED)));
			pm->addLine("Throws:", SPrintF::format("%d", Game::get()->counter(Game::THROWS)));

			pm->addLine("","");
			if (completion100)
				pm->addLine("You have gathered all the loose garbage! A golden crown for you!"," ");
			else
				pm->addLine("Try to get all the loose garbage on a level to earn a golden crown.", "");
			if (completion100 && Game::get()->dbCounter(Game::TIME) < Config::get().timeAttackTime(mNo))
				pm->addLine("And all that under the target time, well done! Silver crown awarded!"," ");
			else
				pm->addLine("If you do this under the target time, you will get a silver crown as well.", "");

			pm->addLine("","");
			pm->addLine("SPACE - next level, R - retry", "ESC - back to menu");


			pm->setShown(true);
			mPostMortem = pm;
		}

		mZoom = mZoom * 0.95 + 4.0 * 0.05;
		if (mZoom > 3.99)
			mZoom = 4.0;
	}
	if (ws.state == Avatar::DIED)
	{
		if (!mPostMortem)
		{
			std::string deathReason = SPrintF::format("You %s.", ws.comment.c_str());
			PopUp *pm = new PopUp();
			pm->addLine(deathReason.c_str(), "");
			pm->addLine("","");
			pm->addLine("Total deaths so far:", SPrintF::format("%d", Config::get().deathsSoFar()));
			pm->addLine("","");
			pm->addLine("","");
			pm->addLine("SPACE, R - retry", "ESC - back to menu");
			pm->setShown(true);
			mPostMortem = pm;
		}
	}

	if (ws.state != Avatar::NEITHER)
	{
		if (Controls::get().space->on())
			finish(ws.state);
	}
}

bool PlayState::quitRequested()
{
	finish(0);
	return true;
}

void PlayState::render(ALLEGRO_DISPLAY *disp)
{
	float botR, botG, botB, topR, topG, topB;
	al_unmap_rgb_f(Theme::active().gradientUp(), &topR, &topG, &topB);
	al_unmap_rgb_f(Theme::active().gradientDown(), &botR, &botG, &botB);

	glBegin(GL_QUADS);
		glColor3f(botR, botG, botB); glVertex2f(0.0f, 600.0f); glVertex2f(800.0f, 600.0f);
		glColor3f(topR, topG, topB); glVertex2f(800.0f, 0.0f); glVertex2f(0.0f, 0.0f);
	glEnd();

	Theme::active().preProcess();

	Entities &renders = mWorld.index(ID("rndr"));
	FOREACH(vector,Entity*,e,renders)
		(*e)->render();

	Theme::active().postProcess();

	if (mZoom > 1.0)
	{
		ALLEGRO_BITMAP *back = al_clone_bitmap(al_get_backbuffer());
		Entity *avatar = mWorld.entity(ID("avat"));

		float transfer = (mZoom - 1.0) / 3.0;
		Vec base = (avatar->pos() + Vec(10,-20)) * transfer + (1-transfer)*Vec(400,300);

		Rect r(base.x - 400.0 / mZoom, base.y - 300.0 / mZoom, base.x + 400.0 / mZoom, base.y + 300.0 / mZoom);
		if (r.left() < 0.0)
			r.move(Vec(-r.left(), 0));
		if (r.right() > 800.0)
			r.move(Vec(800.0 - r.right(), 0));
		if (r.top() < 0.0)
			r.move(Vec(0, -r.top()));
		if (r.bottom() > 600.0)
			r.move(Vec(0, 600.0 - r.bottom()));

		al_draw_scaled_bitmap(back, r.left(), r.top(), r.width(), r.height(), 0.0, 0.0, 800.0, 600.0, 0);
		al_destroy_bitmap(back);
	}
	if (mPostMortem)
		mPostMortem->render();
}
