//	ModifierFactory.cpp - Class which handles which modifiers to apply when a
//						  player collects a pickup.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  Niall Moody
//	
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	----------------------------------------------------------------------------

#include "ModifierFactory.h"
#include "ModifierCreator.h"
#include "GamePlayState.h"
#include "Random.h"
#include "Tank.h"

//------------------------------------------------------------------------------
ModifierFactory::ModifierFactory()
{

}

//------------------------------------------------------------------------------
ModifierFactory::~ModifierFactory()
{

}

//------------------------------------------------------------------------------
const std::wstring ModifierFactory::pickupCollectedRandom(GamePlayState& state)
{
	int i;
	int index;
	std::wstring retval;

	index = Random::getGlobal().getRandomInt(available.size());
	modifiers[available[index]]->apply(state);
	retval = modifiers[available[index]]->getName();

	//Check whether there are any creators waiting for this creator to be
	//activated before they can be used.
	for(i=0;i<(int)pending.size();++i)
	{
		if(modifiers[pending[i]]->getDependency() == retval)
		{
			available.push_back(pending[i]);
			pending.erase(pending.begin()+i);
		}
	}

	modifiers[available[index]]->decrementNumUses();

	//If it's single use, remove it from available, add it to removed.
	if(modifiers[available[index]]->getNumUses() < 1)
	{
		removed.push_back(available[index]);
		available.erase(available.begin()+index);
	}

	return retval;
}

//------------------------------------------------------------------------------
const std::wstring ModifierFactory::pickupCollected(GamePlayState& state,
												    const std::wstring& pickupType)
{
	std::wstring retval;
	
	if(modifiers.find(pickupType) != modifiers.end())
	{
		modifiers[pickupType]->apply(state);
		retval = modifiers[pickupType]->getName();
	}

	return retval;
}

//------------------------------------------------------------------------------
const std::wstring ModifierFactory::getRandomAvailablePickup()
{
	int i;
	int index;
	std::wstring retval;

	if(!available.size())
		return L"";

	index = Random::getGlobal().getRandomInt(available.size());
	retval = available[index];

	//Check whether there are any creators waiting for this creator to be
	//activated before they can be used.
	for(i=(pending.size()-1);i>=0;--i)
	{
		if(modifiers[pending[i]]->getDependency() == retval)
		{
			available.push_back(pending[i]);
			pending.erase(pending.begin()+i);
		}
	}

	modifiers[available[index]]->decrementNumUses();

	//If it's single use, remove it from available, add it to removed.
	if(modifiers[available[index]]->getNumUses() < 1)
	{
		removed.push_back(available[index]);
		available.erase(available.begin()+index);
	}

	return retval;
}

//------------------------------------------------------------------------------
const std::wstring ModifierFactory::getGradedPickup(int grade)
{
	int i, j;
	int index;
	int numInGrade;
	std::wstring retval;
	std::vector<int> tempGrades;
	std::multimap<int, std::wstring>::iterator gradesIt;

	//First get hold of all the grades which are acceptable.
	for(i=0;i<=grade;++i)
	{
		numInGrade = availableGrades.count(i);

		//Doing it this way weights the RNG towards the grades w/the most
		//modifiers still waiting to be collected.
		for(j=0;j<numInGrade;++j)
			tempGrades.push_back(i);
	}

	if(!tempGrades.size())
		return retval;

	//Now randomly select one of those grades...
	i = tempGrades[Random::getGlobal().getRandomInt(tempGrades.size())];
	//...and then randomly select a modifier with that particular grade.
	index = Random::getGlobal().getRandomInt(availableGrades.count(i));
	gradesIt = availableGrades.lower_bound(i);
	std::advance(gradesIt, index);
	retval = gradesIt->second;

	modifiers[gradesIt->second]->decrementNumUses();

	//Remove the modifier from availableGrades if it was single use.
	if(modifiers[gradesIt->second]->getNumUses() < 1)
	{
		removedGrades.insert(*gradesIt);
		availableGrades.erase(gradesIt);
	}

	return retval;
}

//------------------------------------------------------------------------------
#ifdef OSX
void ModifierFactory::registerCreator(shared_ptr<ModifierCreator> creator)
#else
void ModifierFactory::registerCreator(shared_ptr<ModifierCreator>& creator)
#endif
{
	modifiers.insert(std::make_pair(creator->getName(), creator));

	if(creator->getDependency() != L"")
		pending.push_back(creator->getName());
	else
		available.push_back(creator->getName());

	availableGrades.insert(std::make_pair(creator->getGrade(),
										  creator->getName()));
}

//------------------------------------------------------------------------------
void ModifierFactory::reset()
{
	int i;
	std::map<std::wstring, shared_ptr<ModifierCreator> >::iterator it;
	std::multimap<int, std::wstring>::iterator it2;

	//First move any dependent creators back to pending.
	for(i=(available.size()-1);i>=0;--i)
	{
		if(modifiers[available[i]]->getDependency() != L"")
		{
			pending.push_back(available[i]);
			available.erase(available.begin()+i);
		}
	}

	//Now move all the removed creators to the correct list.
	for(i=(removed.size()-1);i>=0;--i)
	{
		if(modifiers[removed[i]]->getDependency() != L"")
		{
			pending.push_back(removed[i]);
			removed.erase(removed.begin()+i);
		}
		else
		{
			available.push_back(removed[i]);
			removed.erase(removed.begin()+i);
		}
	}

	//Move all removedGrades back to availableGrades.
	for(it2=removedGrades.begin();it2!=removedGrades.end();++it2)
		availableGrades.insert(*it2);
	removedGrades.clear();

	//Now reset all the creators.
	for(it=modifiers.begin();it!=modifiers.end();++it)
	{
		it->second->reset();
		it->second->resetNumUses();
	}
}
