#include "LevelManager.hpp"

#include "Exception.hpp"


#include "NormalBlock.hpp"
#include "LauncherBlock.hpp"
#include "ExplosionBlock.hpp"

#include "LevelLimit.hpp"
#include "LooseZone.hpp"
#include "Ball.hpp"
#include "Extra.hpp"
#include "Paddle.hpp"

LevelManager::LevelManager(Game* pgame):
game(pgame)
{
}

int LevelManager::getNumLevels()
{
	return 5;
}

std::list<Entity*> LevelManager::getEntities(int level)
{
	std::list<Entity*> result;
	
	level %= getNumLevels();

	switch (level)
	{
	case 0:
		addBlockRow(5 , "   1 L 1 2 1 L 1    ", result);
		addBlockRow(6 , "    1 2 1 2 1 2 1   ", result);
		addBlockRow(7 , "   1 2 1 L 1 2 1    ", result);
		addBlockRow(8 , "    1 2 1 2 1 2 1   ", result);
		addBlockRow(9 , "   1 2 1 2 1 2 1    ", result);
		addDefaultEntities(result);
		break;
	case 1:
		addBlockRow(5 , "   23L33333333L32   ", result);
		addBlockRow(6 , "   2     44     2   ", result);
		addBlockRow(7 , "   2   S    S   2   ", result);
		addBlockRow(8 , "   2S    LL    S2   ", result);
		addBlockRow(9 , "   222S222222S222   ", result);
		addDefaultEntities(result);
		break;
	case 2:
		addBlockRow(3 , "  212               ", result);
		addBlockRow(4 , "  1E1     L   212   ", result);
		addBlockRow(5 , "  212         1E1   ", result);
		addBlockRow(6 , "              212   ", result);
		addBlockRow(7 , "   L    212         ", result);
		addBlockRow(8 , "212     1E1    L    ", result);
		addBlockRow(9 , "1E1     212     212 ", result);
		addBlockRow(10, "212             1E1 ", result);
		addBlockRow(11, "        L       212 ", result);
		addBlockRow(12, "                    ", result);
		addBlockRow(13, "   212              ", result);
		addBlockRow(14, "   1E1     L   212  ", result);
		addBlockRow(15, "   212         1E1  ", result);
		addBlockRow(16, "               212  ", result);
		addDefaultEntities(result);
		break;
	case 3:
		{		
			addBlockRow(7 , "   1 L 1 L 1 L S    ", result);
			addBlockRow(8 , "    S 2 1 2 1 E 1   ", result);
			addBlockRow(9 , "   1 2 1 2 1 S 1    ", result);
			addBlockRow(10, "    E L 1 2 1 2 1   ", result);
			addBlockRow(11, "   1 2 1 2 E 2 1    ", result);

			//2 paddles, 2 loose zones :D
			Entity* entityPtr;
			
			entityPtr = new Paddle(game);
			entityPtr->setPosition(Vector3f(0.0f,1.0f,0.0f));
			entityPtr->LoadModel("Paddle/Paddle.obj");
			result.push_back(entityPtr);


			entityPtr = new Paddle(game);
			entityPtr->setPosition(Vector3f(0.0f,11.0f,0.0f));
			entityPtr->LoadModel("Paddle/Paddle.obj");
			result.push_back(entityPtr);

			//-- create level limits ---
			//top
			entityPtr=new LooseZone(game, CollisionModel(Vector3f(-18, 12, -1), Vector3f(+18, 20, +1)));
			result.push_back(entityPtr);
			
			entityPtr=new LevelLimit(game);//left
			entityPtr->setCollisionModel(CollisionModel(Vector3f(-18, -4, -1), Vector3f(-10, 12, +1)));
			result.push_back(entityPtr);
			
			entityPtr=new LevelLimit(game);//right
			entityPtr->setCollisionModel(CollisionModel(Vector3f(+10, -4, -1), Vector3f(+18, 12, +1)));
			result.push_back(entityPtr);

			//bottom
			entityPtr=new LooseZone(game, CollisionModel(Vector3f(-18, -12, -1), Vector3f(+18, -4, +1)));
			result.push_back(entityPtr);
		}
		break;
	case 4:
		addBlockRow(5 , "         3          ", result);
		addBlockRow(6 , "       L454L        ", result);
		addBlockRow(7 , "      2356532       ", result);
		addBlockRow(8 , "        E5E         ", result);
		addBlockRow(9 , "         3          ", result);
		addBlockRow(12, " S S S S S S S S S S", result);
		addBlockRow(15, "11111111111111111111", result);
		addDefaultEntities(result);
		break;
	}


	return result;
}


void LevelManager::addBlockRow(float YPos, std::string Blocks, std::list<Entity*>& EntityList)
{
	auto rnd_dir = uniform_real_distribution<>(-1, 1);

	if(Blocks.length()>20)
	{
		THROW(Exception() << ex_message("Too long Block line"));
	}

	YPos=11-YPos/2;

	for(unsigned int i=0; i<Blocks.size(); ++i)
	{
		Vector3f Pos=Vector3f(-10.0f + i*1.0f, YPos, 0.0f);
		Entity* entityPtr;

		if(false){}//now we just have else if's :)
		else if('1'==Blocks[i])
		{
			entityPtr = new NormalBlock(game, 1);
			entityPtr->LoadModel("Block/Block.obj");
		}
		else if('2'==Blocks[i])
		{
			entityPtr = new NormalBlock(game, 2);
			entityPtr->LoadModel("Block/Block.obj");
		}
		else if('3'==Blocks[i])
		{
			entityPtr = new NormalBlock(game, 3);
			entityPtr->LoadModel("Block/Block.obj");
		}
		else if('4'==Blocks[i])
		{
			entityPtr = new NormalBlock(game, 4);
			entityPtr->LoadModel("Block/Block.obj");
		}
		else if('5'==Blocks[i])
		{
			entityPtr = new NormalBlock(game, 5);
			entityPtr->LoadModel("Block/Block.obj");
		}
		else if('6'==Blocks[i])
		{
			entityPtr = new NormalBlock(game, 6);
			entityPtr->LoadModel("Block/Block.obj");
		}
		else if('7'==Blocks[i])
		{
			entityPtr = new NormalBlock(game, 7);
			entityPtr->LoadModel("Block/Block.obj");
		}

		else if('E'==Blocks[i])
		{
			entityPtr = new ExplosionBlock(game);
			entityPtr->LoadModel("ExplosionBlock/Dynamite.obj");
		}
		else if('S'==Blocks[i])
		{
			auto rnd_extra_speed = uniform_real_distribution<>(8, 16);

			//create a Exta for the launcher
			Vector3f Vel=Vector3f(rnd_dir(game->rnd), rnd_dir(game->rnd)*2, 0).normalize();
			Vel *= rnd_extra_speed(game->rnd);

			Extra* ext = new Extra(game);
			ext->setVelocity(Vel);
			ext->LoadModel("Extra01/Extra.obj");
			ext->setCollisionModel(CollisionModel(Vector3f(-0.1), Vector3f(0.1)));

			entityPtr = new LauncherBlock(game, ext);
			entityPtr->LoadModel("ExtraBlock/Barrel.obj");
		}
		else if('L'==Blocks[i])
		{
			//create a ball for the launcher block
			auto rnd_ball_speed = uniform_real_distribution<>(6, 8);
			Vector3f Vel=Vector3f(rnd_dir(game->rnd), rnd_dir(game->rnd)*2, 0).normalize();
			Vel *= rnd_ball_speed(game->rnd);

			Entity* ballPtr = new Ball(game, Vel);
			entityPtr = new LauncherBlock(game, ballPtr);

			entityPtr->LoadModel("LauncherBlock/LauncherBlock.obj");
		}
		//TODO: support other block types
		else if(' '==Blocks[i] || '>'==Blocks[i])
		{
			//do nothing
			continue;
		}
		else
		{
			THROW(Exception() << ex_message("Invalid Block qualifier") << ex_detail(Blocks));
		}

		entityPtr->setPosition(Pos);
		EntityList.push_back(entityPtr);

	}

}

void LevelManager::addDefaultEntities(std::list<Entity*>& EntityList)
{
	Entity* entityPtr;
			
	entityPtr = new Paddle(game);
	entityPtr->setPosition(Vector3f(0.0f,1.0f,0.0f));
	entityPtr->LoadModel("Paddle/Paddle.obj");
	EntityList.push_back(entityPtr);

	//-- create level limits ---
	entityPtr=new LevelLimit(game);//top
	entityPtr->setCollisionModel(CollisionModel(Vector3f(-18, 12, -1), Vector3f(+18, 20, +1)));
	EntityList.push_back(entityPtr);
			
	entityPtr=new LevelLimit(game);//left
	entityPtr->setCollisionModel(CollisionModel(Vector3f(-18, -4, -1), Vector3f(-10, 12, +1)));
	EntityList.push_back(entityPtr);
			
	entityPtr=new LevelLimit(game);//right
	entityPtr->setCollisionModel(CollisionModel(Vector3f(+10, -4, -1), Vector3f(+18, 12, +1)));
	EntityList.push_back(entityPtr);

	//bottom
	entityPtr=new LooseZone(game, CollisionModel(Vector3f(-18, -12, -1), Vector3f(+18, -4, +1)));
	EntityList.push_back(entityPtr);
}