#include "WimpLib:Template.h"
#include <string.h>

#include "kernel.h"

#include <stdbool.h>
#include "WimpLib:List.h"
#include "WimpLib:Exception.h"
#include "WimpLib:File.h"
#include "WimpLib:Font.h"
#include "WimpLib:mem.h"
#include "WimpLib:Task.h"
#include "WimpLib:Utils.h"

static List* Templates = NULL;

void throw_Templates_Templates(void)
{
	Templates = New_List();

	// Try to open file Templates from application directory
	throw_os(Templates_Load(throw_File_FromRes("Templates")));
}

void Templates_NotTemplates(void)
{
	if (Templates)
	{
		while (List_Count(Templates))
		{
			CTemplate* pt = List_Get(Templates, 0);

			mem_free(pt->window);
			mem_free(pt->workspace);
			mem_free(pt);
			List_Remove(Templates, 0);
		}

		Delete_List(Templates);
		Templates = NULL;
	}
}

const _kernel_oserror* Templates_Load(const char* filename)
{
	_kernel_swi_regs regs;
	const _kernel_oserror* err;
	int  pos;
	char string[] = "************";
	char defstr[] = "************";
	CTemplate* volatile pt = NULL;
	CWind* pw;
	CIcon* pi;
	int  i, j;
	char fonts[256];
	int  count = List_Count(Templates);

	/*
	 * Init font reference array
	 */
	for (i = 1; i < 256; i++)
		fonts[i] = 0;

	// Wimp_OpenTemplate
	regs.r[1] = (int) filename;
	if ((err = _kernel_swi(0x600d9, &regs, &regs)) != NULL) return err;

	// First pass reserve the memory required by each template
	try
	{
		pos = 0;

		do
		{
			memcpy(string, defstr, 12);

			// Wimp_LoadTemplate
			regs.r[1] = -1;
			regs.r[2] = -1;
			regs.r[3] = -1;
			regs.r[4] = (int) fonts;
			regs.r[5] = (int) string;
			regs.r[6] = pos;
			throw_os(_kernel_swi(0x600db, &regs, &regs));

			pos = regs.r[6];

			if (pos != 0)
			{
				pt = throw_mem_calloc(sizeof(CTemplate), 1);
				pt->window = throw_mem_alloc(regs.r[1] + 4);
				pt->workspace = throw_mem_alloc(regs.r[2]);
				pt->workspacesize = regs.r[2];
				memcpy(pt->name, (const char*) regs.r[5], 12);
				for(i = 0; i < 12; i++)
				{
					if (pt->name[i] < 32) pt->name[i] = 0;
				}

				List_InsertBefore(Templates, NULL, pt);
			}
		}
		while (pos != 0);
	}
	catch
	{
		const exception* e = exception_current();

		err = &e->error;
		if (pt)
		{
			mem_free(pt->window);
			mem_free(pt->workspace);
			mem_free(pt);
		}
	}
	catch_end

	// Second pass, load each template and update the sprite area pointers
	pos = 0;

	for (i = count; i < List_Count(Templates); i++)
	{
		pt = List_Get(Templates, i);

		// Wimp_LoadTemplate
		memcpy(string, defstr, 12);
		regs.r[1] = 4 + (int) pt->window;
		regs.r[2] = (int) pt->workspace;
		regs.r[3] = (int) (pt->workspace + pt->workspacesize);
		regs.r[4] = (int) fonts;
		regs.r[5] = (int) string;
		regs.r[6] = pos;

		if ((err = _kernel_swi(0x600db, &regs, &regs)) == NULL)
		{
			pw = Template_GetWindow(pt);

			pw->sprite_area = Task_GetSpriteArea();
			pi = (CIcon*) (pw + 1);
			for (j = 0; j < pw->nicons; j++, pi++)
			{
				if ((pi->flags & 0x103) == 0x102)
					pi->data.is.sprite_area = pw->sprite_area;
			}

			pos = regs.r[6];
		}
	}

	// Close template file
	_kernel_swi(0x600da, &regs, &regs);

	/*
	 * Loose all fonts since has to be setup manually
	 * to work on mode changes
	 */
	for (i = 1; i < 256; i++)
	{
		for (j = 0; j < fonts[i]; j++)
			Font_Lose(i);
	}

	return err;
}

CTemplate* Templates_Find(const char* title)
{
	CTemplate* t;
	ListNode* pNode = NULL;

	if (strlen(title) > 12) return NULL;

	while ((pNode = List_GetSuccessor(Templates, pNode)) != NULL)
	{
		t = List_GetNodeData(Templates, pNode);

		if (!strncmp(title, t->name, 12))
			return t;
	}

	return NULL;
}

CTemplate* throw_Templates_Copy(const char* title)
{
	CTemplate* t = Templates_Find(title);
	CTemplate* nt;
	CIcon* pi;
	int size;
	int delta;
	int i;

	if (t == NULL) throw_string("Template '%s' not found", title);

	nt = throw_mem_alloc(sizeof(*nt));
	nt->window = NULL;
	nt->workspace = NULL;

	try
	{
		size = sizeof(CWind) + t->window->nicons * sizeof(CIcon);
		nt->window = throw_mem_alloc(size);
		nt->workspace = throw_mem_alloc(t->workspacesize);

		memcpy(nt->window, t->window, size);
		memcpy(nt->workspace, t->workspace, t->workspacesize);
		delta = nt->workspace - t->workspace;
		nt->workspacesize = t->workspacesize;
		nt->name[0] = 0;

		if (nt->window->title_flags & EIcon_Indirect)
		{
			switch (nt->window->title_flags & (EIcon_Text | EIcon_Sprite))
			{
				case EIcon_Text:
				case (EIcon_Text + EIcon_Sprite):
				{
					if ((nt->window->title_data.it.buffer)
					&&  (nt->window->title_data.it.buffer != (char*) -1))
						nt->window->title_data.it.buffer += delta;
					if ((nt->window->title_data.it.validation)
					&&  (nt->window->title_data.it.validation != (char*) -1))
						nt->window->title_data.it.validation += delta;
				}
				break;
				case EIcon_Sprite:
				{
					if ((nt->window->title_data.is.sprite_name)
					&&  (nt->window->title_data.is.sprite_name != (char*) -1))
						nt->window->title_data.is.sprite_name += delta;
				}
				break;
			}
		}

		pi = (CIcon*) (nt->window + 1);
		for (i = 0; i < nt->window->nicons; i++, pi++)
		{
			if (pi->flags & EIcon_Indirect)
			{
				switch (pi->flags & (EIcon_Text | EIcon_Sprite))
				{
					case EIcon_Text:
					case (EIcon_Text + EIcon_Sprite):
					{
						if ((pi->data.it.buffer)
						&&  (pi->data.it.buffer != (char*) -1))
							pi->data.it.buffer += delta;
						if ((pi->data.it.validation)
						&&  (pi->data.it.validation != (char*) -1))
							pi->data.it.validation += delta;
					}
					break;
					case EIcon_Sprite:
					{
						if ((pi->data.is.sprite_name)
						&&  (pi->data.is.sprite_name != (char*) -1))
							pi->data.is.sprite_name += delta;
					}
					break;
				}
			}
		}

		List_InsertBefore(Templates, NULL, nt);
	}
	catch
	{
		mem_free(nt->window);
		mem_free(nt->workspace);
		mem_free(nt);
		throw_current();
	}
	catch_end

	return nt;
}

static const CWind Model = {{{HWind_None, {{0, 0, 600, 400}, {0, 0}}, HWind_None}, 0x0 /* flags*/ }
                     , 0x07, 0x02, 0x07, 0xff /* don't redraw background */, 0x03, 0x01, 0x0c
                     , 0x0 /* exflags */, {0, -400, 600, 0}
                     , EIcon_Indirect | EIcon_Filled | EIcon_VCentre | EIcon_HCentre | EIcon_Border | EIcon_Text
                     , EIcon_ButtonType_DoubleClickDrag
                     , (CSpriteArea*) 1, 1, 1
                     , {{NULL, NULL, 0}}
                     , 0};

CTemplate* throw_Templates_Blank(int length, unsigned int flags, int minx, int miny)
{
	CTemplate* volatile nt;

	nt = throw_mem_alloc(sizeof(*nt));
	nt->window = NULL;
	nt->workspace = NULL;

	try
	{
		CWind* info = throw_mem_alloc(sizeof(*info));

		nt->window = info;
		memcpy(info, &Model, sizeof(*info));

		if (length < 1) length = 1;
		nt->workspace = throw_mem_alloc(length);

		nt->workspace[0] = 0;
		nt->workspacesize = length;
		nt->name[0] = 0;

		info->o.flags = EWind_NewFormat | flags;
		info->title_data.it.buffer = nt->workspace;
		info->title_data.it.buf_size = length;

		info->min_width = minx;
		info->min_height = miny;
		info->o.o.cvt.box.x0 = 400;
		info->o.o.cvt.box.x1 = info->o.o.cvt.box.x0 + minx;
		info->o.o.cvt.box.y0 = 400;
		info->o.o.cvt.box.y1 = info->o.o.cvt.box.y0 + miny;
        info->ex.x1 = minx;
		info->ex.y0 = -miny;

		List_InsertBefore(Templates, NULL, nt);
	}
	catch
	{
		mem_free(nt->window);
		mem_free(nt->workspace);
		mem_free(nt);
		throw_current();
	}
	catch_end

	return nt;
}

CWind* Template_GetWindow(const CTemplate* t)
{
	return t->window;
}

void Templates_Remove(CTemplate* t)
{
	if (t)
	{
		int i = List_Find(Templates, 0, t);

		if (i < 0) return;

		mem_free(t->window);
		mem_free(t->workspace);
		mem_free(t);

		List_Remove(Templates, i);
	}
}
