#include "Rewind_Manager.h"
#include "Input.h"
#include "Collision_Manager.h"
#include "Global_Properties.h"
#include "Sound_Manager.h"

namespace Rewind
{

	// Singleton implimentation
	RewindManager* RewindManager::sInstance = 0;
	RewindManager& RewindManager::I()
	{
		if (sInstance == 0)
			sInstance = new RewindManager();

		return *sInstance;
	}

	RewindManager::RewindManager() : mTimeStamp(0), mFrameCounter(0), mIsRewinding(false)
	{}

	void RewindManager::Init()
	{
		SavePositions(); // Add everythings' starting positions as the first frame
	}

	void RewindManager::Update()
	{
		CheckInput();

		if (mIsRewinding) // Update rewind logic
		{
			// Stop rewinding if you hit the start
			if (mTimeStamp == 0)
				EndRewind();
			else
			{
				// Update rewindables movement
				if (mSavedTimeStamps.back() != 0 && mFrameCounter > 0)
				{
					float t = (mFrameCounter - (mTimeStamp - mSavedTimeStamps.back())) / (float)mFrameCounter;
					for (std::list<Rewindable*>::iterator i = mRewindables.begin(); i != mRewindables.end(); ++i)
						(**i).RewindMove(t);

				}

				// Check if you should go to the next point
				if (mTimeStamp == mSavedTimeStamps.back())
				{
					mSavedTimeStamps.pop_back();
					SetNextRewindPoint();
					mFrameCounter = mTimeStamp - mSavedTimeStamps.back();
				}

				--mTimeStamp;

			}

		}
		else // Update forward logic
		{
			++mTimeStamp;
			++mFrameCounter;

			// Check if you should save
			if (mFrameCounter == GlobalProperties::EVERY_SAVE_FRAME)
			{
				SavePositions();
				mFrameCounter = 0;
			}
		}

	}

	void RewindManager::SavePositions()
	{
		// Before saving, check if you have reached max save frames. If true, pop 
		bool removeFront = false;
		if (mSavedTimeStamps.size() >= GlobalProperties::MAX_SAVE_FRAMES)
		{
			removeFront = true;
			mSavedTimeStamps.pop_front();
		}

		mSavedTimeStamps.push_back(mTimeStamp);
		for (std::list<Rewindable*>::iterator i = mRewindables.begin(); i != mRewindables.end(); ++i)
			(**i).AddPosition(removeFront);
	}

	void RewindManager::SetFirstRewindPoint()
	{
		for (std::list<Rewindable*>::iterator i = mRewindables.begin(); i != mRewindables.end(); ++i)
			(**i).GoToNextPosition(false);
	}

	void RewindManager::SetNextRewindPoint()
	{
		for (std::list<Rewindable*>::iterator i = mRewindables.begin(); i != mRewindables.end(); ++i)
			(**i).GoToNextPosition(true);
	}

	void RewindManager::CheckInput()
	{
		// Press the rewind button
		if (Input::InputManager::I().GetSpaceKeyStateDown())
			RewindStart();

		// Release the rewind button
        if (Input::InputManager::I().GetSpaceKeyStateUp())
			if (mIsRewinding)
				EndRewind();
	}

	void RewindManager::RewindStart()
	{
		Sounds::PlayLooping(Sounds::BackgroundMusicBackwards);
		mIsRewinding = true;
		SetFirstRewindPoint();
		mFrameCounter = mTimeStamp - mSavedTimeStamps.back(); // mFrameCounter takes on a new role since we will not be saving when rewinding
	}

	void RewindManager::EndRewind()
	{
		Sounds::PlayLooping(Sounds::BackgroundMusic);
		mIsRewinding = false;
		mFrameCounter = 0;
		CollisionManager::I().RewindEnd();

		if (mTimeStamp - mSavedTimeStamps.back() > 1)
			SavePositions();
	}

}