#include "MetaData.h"

#include <string.h>
#include "WimpLib:Array.h"
#include "WimpLib:Exception.h"
#include "WimpLib:mem.h"
#include "WimpLib:std.h"
#include "WimpLib:Utils.h"
#include "DigitalCD.h"

struct MetaList
{
	Array* a;
};

MetaList* throw_New_MetaList(void)
{
	MetaList* This = throw_mem_alloc(sizeof(*This));

	try
	{
		This->a = New_Array(sizeof(MetaData), 4);
	}
	catch
	{
		mem_free(This);
		throw_current();
	}
	catch_end

	return This;
}

MetaList* throw_New_MetaList_Copy(const MetaList* pOld)
{
	MetaList* This = throw_New_MetaList();
	int count = MetaList_Count(pOld);
	int i;

	try
	{
		for (i = 0; i < count; i++)
		{
			const MetaData* pmeta = MetaList_Get(pOld, i);

			throw_MetaList_SetData(This, pmeta->id, EMetaOrigin_User, pmeta->data, pmeta->size);
		}
	}
	catch
	{
		Delete_MetaList(This);
		throw_current();
	}
	catch_end

	return This;
}

void Delete_MetaList(MetaList* This)
{
	if (This)
	{
		MetaList_Clear(This);
		Delete_Array(This->a);
		mem_free(This);
	}
}

int MetaList_Count(const MetaList* This)
{
	return Array_Count(This->a);
}

void MetaList_Clear(MetaList* This)
{
	int count = Array_Count(This->a);
	int i;

	// Short ordered by id list, no optimisation
	for (i = 0; i < count; i++)
	{
		MetaData* pmeta = Array_Get(This->a, i);

		switch(pmeta->id)
		{
			case EMetaId_StreamCollective:
			{
				StrCol_Remove(DigitalCD.pCollectivesCol, pmeta->data);
			}
			break;
			case EMetaId_StreamArtist:
			{
				StrCol_Remove(DigitalCD.pArtistsCol, pmeta->data);
			}
			break;
			case EMetaId_StreamDate:
			{
				StrCol_Remove(DigitalCD.pDatesCol, pmeta->data);
			}
			break;
			case EMetaId_StreamBox:
			{
				StrCol_Remove(DigitalCD.pBoxesCol, pmeta->data);
			}
			break;
			case EMetaId_StreamAlbum:
			{
				StrCol_Remove(DigitalCD.pAlbumsCol, pmeta->data);
			}
			break;
			case EMetaId_StreamBroadcaster:
			{
				StrCol_Remove(DigitalCD.pBroadcastersCol, pmeta->data);
			}
			break;
			case EMetaId_StreamMimeType:
			{
				StrCol_Remove(DigitalCD.pMimeTypesCol, pmeta->data);
			}
			break;
			case EMetaId_StreamTrackNumber:
			{
				// pointer = integer
			}
			break;
			case EMetaId_ModuleName:
			{
				// pointer = pointer to constant string
			}
			break;
			case EMetaId_ModuleHandle:
			{
				// pointer = integer
			}
			break;
			default:
			{
				mem_free(pmeta->data);
			}
		}
	}

	Array_Clear(This->a);
}

bool throw_MetaList_Merge(MetaList* This, const MetaList* pOld)
{
	int count = MetaList_Count(pOld);
	int i;
	bool b = false;

	for (i = 0; i < count; i++)
	{
		const MetaData* pmeta = MetaList_Get(pOld, i);

		b |= throw_MetaList_SetData(This, pmeta->id, pmeta->origin, pmeta->data, pmeta->size);
	}

	return b;
}

const MetaData* MetaList_Find(const MetaList* This, EMetaId id)
{
	int count = Array_Count(This->a);
	int i;

	// Short ordered by id list, no optimisation
	for (i = 0; i < count; i++)
	{
		const MetaData* pmeta = Array_Get(This->a, i);
		if (pmeta->id == id)
			return pmeta;
		else if (pmeta->id > id)
			return NULL;
	}

	return NULL;
}

const MetaData* MetaList_Get(const MetaList* This, int index) throws(index)
{
	return Array_Get(This->a, index);
}

bool throw_MetaList_SetData
	( MetaList* This
	, EMetaId id
	, EMetaOrigin origin
	, const void* pdata
	, unsigned int size
	) throws(mem)
{
	MetaData* pcmeta = NULL;
	int count = Array_Count(This->a);
	int i;
	bool b = false;
	bool bi = false;

	// Short ordered by id list, no optimisation
	for (i = 0; i < count; i++)
	{
		MetaData* pmeta = Array_Get(This->a, i);
		if (pmeta->id == id)
		{
			pcmeta = pmeta;
			break;
		}
		else if (pmeta->id > id)
			break;
	}

	if (pcmeta)
	{
		// cannot replace data of higher precedence
		if (origin < pcmeta->origin)
			return false;
	}
	else
	{
		MetaData dummy = {id, EMetaOrigin_Default, NULL, 0};

		Array_Insert(This->a, i, &dummy);

		pcmeta = Array_Get(This->a, i);
		bi = true;
	}

	try
	{
		switch(id)
		{
			case EMetaId_StreamCollective:
			{
				b = throw_StrCol_Set(DigitalCD.pCollectivesCol, &pcmeta->data, pdata);
			}
			break;
			case EMetaId_StreamArtist:
			{
				b = throw_StrCol_Set(DigitalCD.pArtistsCol, &pcmeta->data, pdata);
			}
			break;
			case EMetaId_StreamDate:
			{
				b = throw_StrCol_Set(DigitalCD.pDatesCol, &pcmeta->data, pdata);
			}
			break;
			case EMetaId_StreamBox:
			{
				b = throw_StrCol_Set(DigitalCD.pBoxesCol, &pcmeta->data, pdata);
			}
			break;
			case EMetaId_StreamAlbum:
			{
				b = throw_StrCol_Set(DigitalCD.pAlbumsCol, &pcmeta->data, pdata);
			}
			break;
			case EMetaId_StreamBroadcaster:
			{
				b = throw_StrCol_Set(DigitalCD.pBroadcastersCol, &pcmeta->data, pdata);
			}
			break;
			case EMetaId_StreamMimeType:
			{
				b = throw_StrCol_Set(DigitalCD.pMimeTypesCol, &pcmeta->data, pdata);
			}
			break;
			case EMetaId_StreamTrackNumber:
			{
				// pointer = integer
				b = (pcmeta->data != pdata);
				pcmeta->data = pdata;
			}
			break;
			case EMetaId_ModuleName:
			{
				// pointer = pointer to constant string
				b = (pcmeta->data != pdata);
				pcmeta->data = pdata;
			}
			break;
			case EMetaId_ModuleHandle:
			{
				// pointer = integer
				b = (pcmeta->data != pdata);
				pcmeta->data = pdata;
			}
			break;
			default:
			{
				char* pnewdata = throw_mem_allocstring(pdata);
				String_StripBlanks(pnewdata);

				// replace old occurence
				if ((pcmeta->data == NULL) || strcmp(pcmeta->data, pnewdata))
				{
					mem_free(pcmeta->data);
					pcmeta->data = pnewdata;
					b = true;
				}
				else
				{
					mem_free(pnewdata);
				}
			}
		}
	}
	catch
	{
		if (bi) Array_Remove(This->a, i);
		throw_current();
	}
	catch_end

	// Force higher origin even if data unchanged
	if (pcmeta->origin != origin)
	{
		pcmeta->origin = origin;
		b = true;
	}
	pcmeta->size = size;

	return b;
}

bool throw_MetaList_SetText
	( MetaList* This
	, EMetaId id
	, EMetaOrigin o
	, const char* data
	)
{
	return throw_MetaList_SetData(This, id, o, data, 0);
}
