#include "Salamander.h"
#include "Application.h"

#include "PlatformEvent.h"

namespace Salamander
{

Application::Application() : 
	running_(false),
	delta_(0.0)
{
	// register for event handlers
	events().receive<Application, PlatformEvent>(this, &Application::onPlatformEvent);

	// Add the default managers in the correct order
	addManager(*(new PlatformManager()));
	addManager(*(new VisualManager()));
	addManager(*(new AssetManager()));
	addManager(*(new SceneManager()));
	addManager(*(new ScreenManager()));
	
}

Application::~Application()
{

}

Application& Application::application()
{
    static Application application_;
	return application_;
}

bool Application::startup()
{
	bool success = true;

	// Start all of our managers
	for(ManagerVectorIterator manager = managerVector_.begin();
		manager != managerVector_.end();
		++manager)
	{
		success &= (*manager)->startup();
	}

	// Notify the user
	if(!success)
	{
		fileio().warning("Application startup failed");
	}

	running_ = success;
	return success;
}

bool Application::shutdown()
{
    bool success = true;
	running_ = false;

	// Delete the managers in reverse order
	while(managerVector_.size())
	{
		IManager* manager = managerVector_.back();

		success &= manager->shutdown();
		delete manager;
		managerVector_.pop_back();
	}

	// Clean up the map as well
	managerMap_.empty();

	// Notify the user
	if(!success)
	{
		fileio().warning("Application shutdown failed");
	}

	return success;
}

void Application::run()
{
	// We can't run if we've not initialised
	if(!running_ && !startup())
	{
		fileio().error("Application could not run because startup failed");
		return;
	}

	// Game loop
	while(running_)
	{
		frame();
	}

	// End
	shutdown();
}

/* todo : do we limit framerate?
void Application::limit(float fps)
{
	unsigned int ticksPerFrame = unsigned int(1000.0f / fps);
	unsigned int ticksThisFrame = clock() - tickCount_;

	if(ticksThisFrame < ticksPerFrame)
		Sleep(ticksPerFrame - ticksThisFrame);
}*/

double Application::delta() const
{
	// return the precached delta value
	return delta_;
}

void Application::onPlatformEvent(const PlatformEvent& event)
{
	fileio().log("onPlatformEvent");
	switch(event.getAction())
	{
		case PlatformEvent::QUIT :
		{
			running_ = false;
		}
		break;
		default:
			// unhandled
		break;
	}
}

bool Application::addManager(IManager& manager)
{
	// Check for duplicates
	if(managerMap_[manager.typeId()])
	{
		return false;
	}

	// None found, so we now track a new manager
	managerMap_[manager.typeId()] = &manager;
	managerVector_.push_back(&manager);
	return true;
}

IManager& Application::getManager(const UniqueId& managerId)
{
	// Find the manager of the specified type
	return *managerMap_[managerId];
}

void Application::frame()
{
	// calculate and cache a delta value for framerate independence
	delta_ = std::min((clock() - clock_) / TICKS_PER_SECOND, MAXIMUM_DELTA);
	clock_ = clock();

	// update all manager frames
	for(ManagerVectorIterator manager = managerVector_.begin(); 
		manager != managerVector_.end();
		++manager)
	{
		(*manager)->frame();
	}
}

}
