#include "WimpLib:JobList.h"

#include "WimpLib:Array.h"
#include "WimpLib:Exception.h"
#include "WimpLib:mem.h"

typedef struct Job
{
	void*           pOwner;
	void*           pParams;
	Job_Exec        pFExec;
} Job;

struct JobList
{
	Array*          Jobs;
	unsigned int    ModifStamp;   // Used to track add/remove
};

JobList* New_JobList(void) throws(mem)
{
	JobList* This = throw_mem_alloc(sizeof(*This));

	try
	{
		This->ModifStamp = 0;
		This->Jobs = New_Array(sizeof(Job), 128);
	}
	catch
	{
		mem_free(This);
		throw_current();
	}
	catch_end

	return This;
}

void Delete_JobList(JobList* This)
{
	if (This)
	{
		Delete_Array(This->Jobs);
		mem_free(This);
	}
}

int JobList_Count(const JobList* This)
{
	return Array_Count(This->Jobs);
}

void JobList_Add(JobList* This, bool bReplace, void* pOwner, void* pParams, Job_Exec fn) throws(mem)
{
	Job job = {pOwner, pParams, fn};

	Array_Insert(This->Jobs, -1, &job);

	if (bReplace)
	{
		int index = Array_Find(This->Jobs, 0, &job);

		// Remove duplicates
		if ((index != -1) && ((index + 1) < Array_Count(This->Jobs)))
			Array_Remove(This->Jobs, index);
	}
}

void JobList_Remove(JobList* This, void* pOwner, void* pParams, Job_Exec fn)
{
	Job job = {pOwner, pParams, fn};
	int index = Array_Find(This->Jobs, 0, &job);

	if (index != -1)
	{
		fn(pOwner, EJobAction_Stop, pParams);
		Array_Remove(This->Jobs, index);
	}
}

void JobList_RemoveAll(JobList* This, void* pOwner)
{
	for (int i = Array_Count(This->Jobs) - 1; i >= 0; i--)
	{
		Job* pjob = Array_Get(This->Jobs, i);

		if (pjob->pOwner == pOwner)
		{
			pjob->pFExec(pjob->pOwner, EJobAction_Stop, pjob->pParams);
			Array_Remove(This->Jobs, i);
		}
	}
}

bool JobList_HasJobs(const JobList* This, const void* pOwner)
{
	for (int i = Array_Count(This->Jobs) - 1; i >= 0; i--)
	{
		const Job* pjob = Array_Get(This->Jobs, i);

		if (pjob->pOwner == pOwner)
			return true;
	}

	return false;
}

bool JobList_Exec(JobList* This)
{
	if (Array_Count(This->Jobs))
	{
		Job* pjob = Array_Get(This->Jobs, 0);
		EJobAction act = pjob->pFExec(pjob->pOwner, EJobAction_Continue, pjob->pParams);
		if (act == EJobAction_Stop)
			Array_Remove(This->Jobs, 0);

		return true;
	}

	return false;
}
