// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "job_manager.h"

class CFinishedJob: public CJobManager::IJob
{
public:
	bool finished()									{ return true; }
	std::string getShortStatus()					{ return _ShortStatus; }
	std::string getStatus()							{ return _Status; }
	void display(NLMISC::CLog* log=NLMISC::InfoLog) { log->displayNL("%s",_Status.c_str()); }
	void update()									{}

	CFinishedJob(CJobManager::IJob* theFinishedJob)
	{
		if (theFinishedJob==NULL)
			return;
		_Status=theFinishedJob->getStatus();
		_ShortStatus=theFinishedJob->getShortStatus();
	}

private:
	std::string _Status;
	std::string _ShortStatus;

};

CJobManager* CJobManager::getInstance()
{
	static CJobManager* mgr=NULL;
	if (mgr==NULL)
	{
		mgr=new CJobManager;
	}
	return mgr;
}

CJobManager::CJobManager()
{
	_Paused=false;
	_JobUpdatesPerUpdate=1;
}

void CJobManager::serviceUpdate()
{
	if (_Paused)
		return;

	for (uint32 count=0;count<_JobUpdatesPerUpdate &&!_UnfinishedJobs.empty();++count)
	{
		nlassert(_UnfinishedJobs.front()<_Jobs.size());
		NLMISC::CSmartPtr<IJob>& theJob= _Jobs[_UnfinishedJobs.front()];
		if (theJob->finished())
		{
			// delete the job and replace it with a light weight 'finished job' marker
			theJob= new CFinishedJob(theJob);

			// remove this job from the list of unfinished jobs
			_UnfinishedJobs.pop_front();

			// decrement the updates counter to counteract the auto incrment
			--count;
		}
		else
		{
			theJob->update();
		}
	}
}


uint32 CJobManager::addJob(NLMISC::CSmartPtr<CJobManager::IJob> job)
{
	nlassert(job!=NULL);
	uint32 id= (uint32)_Jobs.size();
	_UnfinishedJobs.push_back(id);
	_Jobs.push_back(job);
	return id;
}

void CJobManager::promoteJob(uint32 idx)
{
	TUnfinishedJobs::iterator it;
	for (it=_UnfinishedJobs.begin(); it!=_UnfinishedJobs.end(); ++it)
	{
		if (*it==idx)
		{
			_UnfinishedJobs.erase(it);
			_UnfinishedJobs.push_front(idx);
			return;
		}
	}
	nlwarning("Failed to promote job with ID %d as not found in unfinished jobs list",idx);
}

void CJobManager::pause()
{
	_Paused= true;
}

void CJobManager::resume()
{
	_Paused= false;
}

void CJobManager::setJobUpdatesPerUpdate(uint32 count)
{
	_JobUpdatesPerUpdate= count;
	if (count==0 || count>100)
		nlwarning("Suspicious value of JobUpdatesPerUpdate: %d",count);
}

uint32 CJobManager::getJobUpdatesPerUpdate()
{
	return _JobUpdatesPerUpdate;
}

std::string CJobManager::getStatus()
{
	std::string result;

	if (_Paused) result+="[Paused] ";
	
	if (!_UnfinishedJobs.empty())
	{
		uint32 idx=_UnfinishedJobs.front();
		nlassert(idx<_Jobs.size());
		result+=_Jobs[idx]->getStatus();
	}

	result+=NLMISC::toString(" [Updates per cycle: %d]",_JobUpdatesPerUpdate);

	return result;
}

void CJobManager::listJobs(NLMISC::CLog* log)
{
	for (uint32 i=0;i< _Jobs.size(); ++i)
	{
		if (!_Jobs[i]->finished())
			nlinfo("%4d*: %s",i,_Jobs[i]->getStatus().c_str());
	}
	nlinfo("%d unfinished jobs (%d  in total)",_UnfinishedJobs.size(),_Jobs.size());
}

void CJobManager::listJobHistory(NLMISC::CLog* log)
{
	for (uint32 i=0;i< _Jobs.size(); ++i)
	{
		nlinfo("%4d%c: %s",i,_Jobs[i]->finished()? ' ': '*',_Jobs[i]->getStatus().c_str());
	}
	nlinfo("%d unfinished jobs (%d  in total)",_UnfinishedJobs.size(),_Jobs.size());
}

void CJobManager::displayCurrentJob(NLMISC::CLog* log)
{
	if (!_UnfinishedJobs.empty())
		displayJob(_UnfinishedJobs.front(),log);
}

void CJobManager::displayJob(uint32 jobId,NLMISC::CLog* log)
{
	nlassert(jobId<_Jobs.size());
	_Jobs[jobId]->display(log);
}