#include <string>

#include "Instantiation.h"
#include "Basic.h"
#include "Graphics/Animation.h"
#include "Graphics/Map.h"
#include "Assets/Wildling.h"
#include "Mechanics/Object.h"
#include "Mechanics/Map.h"
#include "Mechanics/Townsfolk.h"
#include "Mechanics/TownBuilding.h"
#include "Mechanics/Den.h"
#include "Mechanics/PlayerGame.h"
#include "Mechanics/Weapons/MeleeWeapon.h"
#include "Mechanics/Armors/BodyArmor.h"
#include "Mechanics/Item.h"

mechanics::Townsfolk *createTownsfolk()
{
	graphics::Animation *base, *hair, *armor;
	Gender::Type gender;

	switch(gge::utils::Random(0, 2)) {
	case 0:
		gender = Gender::female;
		break;
	default:
		gender = Gender::male;
		break;
	}

	std::string gender_prefix;

	if(gender==Gender::male)
		gender_prefix="Male";
	else
		gender_prefix="Female";

	//!should be created by game and stay with the game
	std::vector<assets::AnimationProvider*> basemodels, hairs, cloths;

	//!should create vectors for options and choose randomly
	for(auto it = assets::AnimationProvider::list.begin(); it != assets::AnimationProvider::list.end(); ++it)
	{
		if(it->second && it->second->type == BaseModel && startsWith(it->second->ID, gender_prefix))
			basemodels.push_back(it->second);

		if(it->second && it->second->type == Hair && startsWith(it->second->ID, gender_prefix+"_hair"))
			hairs.push_back(it->second);

		if(it->second && it->second->type == Armor && startsWith(it->second->ID, gender_prefix+"_cloth"))
			cloths.push_back(it->second);
	}

	base=new graphics::Animation(*gge::utils::Random(basemodels));
	hair=new graphics::Animation(*gge::utils::Random(hairs));
	armor=new graphics::Animation(*gge::utils::Random(cloths));

	mechanics::Townsfolk *tf = new mechanics::Townsfolk(*base, *hair, *armor, gender);
	if(gender == Gender::male)
	{
		tf->name(assets::constants::human::maleNames[gge::utils::Random<int>(0, assets::constants::human::maleNames.size())]);
	}
	else
	{
		tf->name(assets::constants::human::femaleNames[gge::utils::Random<int>(0, assets::constants::human::femaleNames.size())]);
	}

	tf->surname(assets::constants::human::surnames[gge::utils::Random<int>(0, assets::constants::human::surnames.size())]);

	tf->levelUp();

	return tf;
}

mechanics::Item *createItem(std::string ID) {
	mechanics::Item *item;
	assets::Item &itemProvider = *assets::Item::list[ID];

	// create graphical representation of an Item
	auto anim = new graphics::Animation(*itemProvider.animation);

	if(itemProvider.type == assets::Item::Melee || itemProvider.type == assets::Item::Thrust) {
		item = new mechanics::MeleeWeapon(itemProvider, *anim);
	}
	//! ranged items
	// else if(itemProvider.type == assets::Item::Ranged) {
	//	item = new RangedWeapon(itemProvider, *anim);
	// }
	else if(itemProvider.type == assets::Item::Armor && itemProvider.slot == assets::Item::BodyArmor) {
		item = new mechanics::BodyArmor(itemProvider, *anim);
	}
	//! headarmor
	// else if(itemProvider.type == assets::Item::Armor && itemProvider.slot == assets::Item::HeadArmor) {
	//	item = new HeadArmor(itemProvider, *anim);
	// }
	else {
		item = new mechanics::Item(itemProvider, *anim);
	}

	return item;
}

mechanics::Wildling *createWildling(std::string ID, int level)
{
	auto provider = assets::Wildling::list[ID];
	auto anim     = new graphics::Animation(*provider->animation);
	auto w		  = new mechanics::Wildling(*provider, *anim, level); 

	return w;
}

mechanics::Obstacle *createObstacle(const std::string &ID) 
{
	auto provider = assets::Obstacle::list[ID];
	auto anim     = new graphics::Animation(*provider->animation);
	auto obs      = new mechanics::Obstacle(*provider, *anim);
	return obs;
}


mechanics::TownBuilding *createTownBuilding(const std::string &ID) 
{
	auto provider = assets::TownBuilding::list[ID];
	auto anim     = new graphics::Animation(*provider->animation);
	auto tb		  = new mechanics::TownBuilding(*provider, *anim); 
	return tb;
}

mechanics::MilitaryBuilding *createMilitaryBuilding(const std::string &ID) 
{
	auto provider = assets::MilitaryBuilding::list[ID];
	auto anim     = new graphics::Animation(*provider->animation);

	switch(provider->type) {
	case assets::MilitaryBuilding::Storage:
		return new mechanics::StorageBuilding(*provider, *anim);
	case assets::MilitaryBuilding::Housing:
		return new mechanics::HousingBuilding(*provider, *anim);
	case assets::MilitaryBuilding::Tools:
		return new mechanics::ToolBuilding(*provider, *anim);
	case assets::MilitaryBuilding::MeleeTraining:
		return new mechanics::TrainingBuilding(false, *provider, *anim);
	case assets::MilitaryBuilding::RangedTraining:
		return new mechanics::TrainingBuilding(true ,*provider, *anim);
	default:
		return NULL;
	}
}

/*
mechanics::MilitaryBuilding *createMilitaryBuilding(const std::string &ID) 
{
	auto provider = assets::MilitaryBuilding::list[ID];
	auto anim     = new graphics::Animation(*provider->animation);
	auto mb       = new mechanics::MilitaryBuilding(*provider, *anim);
	return mb;
}*/

mechanics::Den *createDen(const std::string &ID) 
{
	auto provider = assets::Den::list[ID];
	auto anim     = new graphics::Animation(*provider->animation);
	auto den      = new mechanics::Den(*provider, *anim);
	return den;
}

mechanics::Object *instantiateMapEntity(const std::string ID, const ObjectType type)
{
	switch(type)
	{
	case Obstacle:
		return createObstacle(ID);

	case TownBuilding: {
		auto tb=createTownBuilding(ID);
		mechanics::PlayerGame::current().addTownBuilding(*tb);

		return tb;
	}
	case Monster: {
		auto w=createWildling(ID, 1);
		mechanics::PlayerGame::current().addWildling(*w);

		return w;
	}
	case MilitaryBuilding:
/*		return createMilitaryBuilding();*/

	case Den: {
		auto den=createDen(ID);
		mechanics::PlayerGame::current().addDen(*den);

		return den;
	}

	default:
		return NULL;
	}
}



void createMap(assets::Map &map)
{
	graphics::Map  &gmap = *new graphics::Map(map);
	mechanics::Map &mmap = *new mechanics::Map(map);
	
	graphics::Map::current(gmap);
	mechanics::Map::current(mmap);

	for(auto it = map.objects.begin(); it != map.objects.end() ; ++it)
	{
		mechanics::Object *obj = instantiateMapEntity(it->ID, it->type);
		if(obj)
			obj->locateTo(it->location);
	}

	for(auto it=map.events.begin();it!=map.events.end();++it) {
		if(it->type==Destroyed) {
			mmap.destroyedEvents.push_back(mechanics::DestroyedEvent());
			mechanics::DestroyedEvent &l=mmap.destroyedEvents.back();

			std::stringstream ss(it->condition);

			gge::utils::Point location;
			ss>>location;
			l.object=mmap.getPointInfoMap()(location).object;
			l.title=it->title;
			l.text=it->text;

			if(assets::AnimationProvider::list[it->imageID]) {
				l.animation=new graphics::Animation(*assets::AnimationProvider::list[it->imageID]);
			}
		}
	}

	mechanics::PlayerGame::current().mGold=map.startingGold;
	mechanics::PlayerGame::current().mPopulationLimit=map.populationLimit;

	for(int i=0;i<map.startingPopulation;i++)
		mechanics::PlayerGame::current().spawnTownsfolk();
}
