//	DefaultGenerator.cpp - The basic ToneGenerator.
//  ----------------------------------------------------------------------------
//	This file is part of 'GameTemplate', template code for a game.
//	Copyright (C) 2011  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 "DefaultGenerator.h"
#include "../ToneFactory.h"
#include "Random.h"

using namespace std;

//------------------------------------------------------------------------------
DefaultGenerator::DefaultGenerator():
ToneGenerator(),
counter(0),
countLength(30.0f)
{

}

//------------------------------------------------------------------------------
DefaultGenerator::~DefaultGenerator()
{

}

//------------------------------------------------------------------------------
vector<TonePtr>& DefaultGenerator::tick(const vector<TonePtr>& tonesInPlay,
										const map<wstring, int>& toneTypesInPlay,
									    vector<PickupPtr>& pickupsInPlay,
										int numWarnings,
										int& numRails,
										float globalSpeed,
										int maxRailStart,
										shared_ptr<TonesSounder>& sounder)
{
	int firstRail;
	int railIndex;
	int firstCreator;
	int creatorIndex;

	tickReturn.clear();

	--counter;
	if(counter <= 0)
	{
		TonePtr newTone;
		const vector<shared_ptr<ToneCreator> >& creators = ToneFactory::getInstance().getNormalToneCreators();

		//Add new Tones.
		{
			//Pick a random ToneCreator.
			firstCreator = (int)(Random::getGlobal().getRandomFloat() *
								 ((float)creators.size() - 0.001f));
			creatorIndex = firstCreator;

			//Loop through each ToneCreator in creators until we've generated a new Tone.
			while(!newTone)
			{
				bool nextCreator = false;
				map<wstring, int>::const_iterator typesIt;

				//Check there aren't too many warnings for this Tone type to
				//be generated.
				if((numWarnings >= creators[creatorIndex]->getMaxNumWarnings()) &&
				   (creators[creatorIndex]->getMaxNumWarnings() > -1))
				{
					nextCreator = true;
				}
				else
				{
					//Loop through the tone types currently in play and check there aren't
					//too many according to ToneCreator::getMaxNumOtherTones().
					for(typesIt=toneTypesInPlay.begin();
						typesIt!=toneTypesInPlay.end();
						++typesIt)
					{
						int maxOther = creators[creatorIndex]->getMaxNumOtherTones(typesIt->first);

						if((maxOther > -1) && (typesIt->second >= maxOther))
						{
							nextCreator = true;
							break;
						}
					}
				}

				if(!nextCreator)
				{
					//Pick a random rail.
					firstRail = (int)(Random::getGlobal().getRandomFloat() *
									  ((float)maxRailStart - 0.001f));
					railIndex = firstRail;

					//This bit's slightly different depending on which side of
					//the screen the Tone's supposed to start from.
					if(creators[creatorIndex]->getStartingSide()) //Right.
					{
						vector<TonePtr>::const_iterator toneIt;
						int minDistance = (int)((1.0f/globalSpeed) *
												(float)creators[creatorIndex]->getMinDistanceToOtherTones());
						if(minDistance > 800)
							minDistance = 800;

						//Loop through each rail until we find one we can use.
						while(!newTone)
						{
							bool nextRail = false;

							//Go through all the Tones in play and break if we
							//find one on this rail that's too close to the
							//start point.
							for(toneIt=tonesInPlay.begin();
								toneIt!=tonesInPlay.end();
								++toneIt)
							{
								const TonePtr& tempTone = *toneIt;
								int tempDist = minDistance;

								//Tones travelling left-right require a larger
								//gap or it's too unfair.
								if(tempTone->getSpeed() < 0.0f)
									tempDist += (int)((float)minDistance * 4.0f * globalSpeed);

								//If this Tone is within minDistance we can't
								//use this rail.
								if(((tempTone->getRail() == railIndex) ||
									(tempTone->getNewRail() == railIndex)) &&
								   ((int)(1280.0f - tempTone->getPosition().x) < tempDist))
								{
									nextRail = true;
									break;
								}
							}

							//If we haven't set nextRail, we can create a new
							//Tone and return it.
							if(!nextRail)
							{
								newTone = creators[creatorIndex]->createTone(railIndex,
																			 numRails,
																			 sounder);
							}
							else
							{
								++railIndex;
								railIndex %= maxRailStart;
								if(railIndex == firstRail)
									break;
							}
						}
					}
					else //Left.
					{
						vector<TonePtr>::const_iterator toneIt;
						int minDistance = (int)((1.0f/globalSpeed) *
												(float)creators[creatorIndex]->getMinDistanceToOtherTones());
						if(minDistance > 800)
							minDistance = 800;

						//Loop through each rail until we find one we can use.
						while(!newTone)
						{
							bool nextRail = false;

							//Go through all the Tones in play and break if we
							//find one on this rail that's too close to the
							//start point.
							for(toneIt=tonesInPlay.begin();
								toneIt!=tonesInPlay.end();
								++toneIt)
							{
								const TonePtr& tempTone = *toneIt;

								//If this Tone is within minDistance we can't
								//use this rail.
								if(((tempTone->getRail() == railIndex) ||
									(tempTone->getNewRail() == railIndex)) &&
								   (tempTone->getPosition().x < minDistance))
								{
									nextRail = true;
									break;
								}
							}

							//If we haven't set nextRail, we can create a new
							//Tone and return it.
							if(!nextRail)
							{
								newTone = creators[creatorIndex]->createTone(railIndex,
																			 numRails,
																			 sounder);
							}
							else
							{
								++railIndex;
								railIndex %= maxRailStart;
								if(railIndex == firstRail)
									break;
							}
						}
					}
				}

				//If we get to this stage we need to try the next ToneCreator along.
				++creatorIndex;
				creatorIndex %= creators.size();
				if(creatorIndex == firstCreator)
					break;
			}

			//Add newTone to our return vector.
			if(newTone)
				tickReturn.push_back(newTone);
		}

		//Update counter and countLength.
		counter = (int)(Random::getGlobal().getRandomFloat() * 29.999f) +
				  (int)(countLength * (1.0f/globalSpeed));
		//countLength *= 0.998f;
	}

	return tickReturn;
}
