#pragma once

#include <utility>

#include "../Assets/Buildings.h"
#include "../Basic.h"
#include "../Instantiation.h"
#include "Building.h"
#include "Wildling.h"
#include "PlayerGame.h"

namespace mechanics
{
	class Den;

	class WildlingSpawner {
		friend class Den;
	public:
		WildlingSpawner(const assets::Den::SpawnInfo &spawninfo, Den &parent) : provider(spawninfo), parent(parent) {
			if(spawninfo.initialSpawnCD) {
				mNextSpawn=currentGameTime+spawninfo.initialSpawnCD*MINUTE/60;
			}
			else
				mNextSpawn=0;
		}


		void spawn();

	protected:
		const assets::Den::SpawnInfo &provider;
		unsigned mNextSpawn;
		std::vector<Wildling*> wildlings;
		Den &parent;
	};


	class Den : public Building
	{
	public:
		Den(const assets::Den &given_provider, Animator &given_animator):
		  mProvider(given_provider),
			  Building(given_animator, OwnerType::neutral)
		{
			given_animator.setAnimation(North, "Stand");
			if(given_provider.initialAttackCD) {
				mNextAttack=currentGameTime+given_provider.initialAttackCD*MINUTE/60;
			}
			else
				mNextAttack=0;

			for(auto it=given_provider.wildlings.begin(); it!=given_provider.wildlings.end(); ++it) {
				
				spawners.push_back(new WildlingSpawner(*it, *this));
			}

			mHP=provider().HP;
		}

		virtual float maximumHP() const {
			return provider().HP;
		}

		virtual float XPReward() const {
			return 1/10+(provider().size.Area()*2)/provider().HP;
		}

		virtual float CalculateDamageReduction(Attacker *attacker, BaseWeapon *weapon, float damage) {
			return damage-provider().armor;
		}

		virtual void Killed(Attacker *attacker, BaseWeapon *weapon);


		void checkSpawn() {
			for(auto it=spawners.begin();it!=spawners.end();++it) {
				if((*it)->mNextSpawn && (*it)->mNextSpawn<currentGameTime) {
					(*it)->spawn();
				}
			}

			if(currentGameTime>mNextAttack) {
				beginAttack();
			}
		}

		void killWilding(Wildling *w);

		void beginAttack();

		virtual void locateTo(const gge::utils::Point &given_location)
		{
			Object::locateTo(given_location);	
			Object::setNonPassable(mProvider);
			mArea=gge::utils::Bounds(given_location-gge::utils::Point(3,3),given_location+mProvider.size+gge::utils::Point(3,3));
		}

		virtual void removeFromMap()
		{
			setPassable(mProvider);
			Object::removeFromMap();
		}

		gge::utils::Bounds area() const {
			return mArea;
		}

		virtual gge::utils::Size size() const {
			return provider().size;
		}

		const assets::Den &provider() const {
			return mProvider;
		}

	private:
		const assets::Den &mProvider;

		unsigned mNextAttack;
		std::vector<WildlingSpawner*> spawners;

		gge::utils::Bounds mArea;
	};


	inline void WildlingSpawner::spawn() {
		for(int i=0;i<provider.spawnCount;i++) {
			if(wildlings.size()>=provider.maxMonster)
				return;

			auto w=createWildling(provider.ID, provider.monsterLevel);

			auto sz=Map::current().getPointInfoMap().GetSize();
			auto densize=parent.provider().size;

			gge::utils::Point p=gge::utils::Point(
				gge::utils::Random(std::max(0,parent.currentLocation().x-3), std::min(parent.currentLocation().x+3+densize.Width, sz.Width)),
				gge::utils::Random(std::max(0,parent.currentLocation().y-3), std::min(parent.currentLocation().y+3+densize.Height, sz.Height))
			);
			//!loop limit
			while(!Map::current().getPointInfoMap()(p).walkable) {
				p=gge::utils::Point(
					gge::utils::Random(std::max(0,parent.currentLocation().x-3), std::min(parent.currentLocation().x+3+densize.Width, sz.Width)),
					gge::utils::Random(std::max(0,parent.currentLocation().y-3), std::min(parent.currentLocation().y+3+densize.Height, sz.Height))
				);
			}

			PlayerGame::current().addWildling(*w);
			w->locateTo(p);
			w->mOwner=&parent;

			wildlings.push_back(w);
		}

		if(provider.regularSpawnCD) {
			mNextSpawn=currentGameTime+provider.regularSpawnCD*MINUTE/60;
		}
		else {
			mNextSpawn=0;
		}
	}

}
