#include "Skin.h"

#include <string.h>

#include "mem.h"
#include "script.h"
#include "Exception.h"
#include "Throwback.h"

typedef struct
{
	const char*	name;
	int		value;
} vlist;

#define ck_opt		0x01
#define ck_multi	0x02
typedef struct
{
	const char*	name;
	gr_type		type;
	int		attributes;
} check;

static const char* gr_types[] =
{
	  "void"
	, "variable declaration"
	, "attribute"
	, "list"
	, "integer property"
	, "attribute property"
	, "string property"
};

static const _kernel_oserror script_error = {0, "ScriptErr: Error in Script"};

static unsigned char var_findvalue(const gr_node* p, const vlist* list)
{
	while (list->name)
	{
		if (!strcmp(list->name, p->pstring))
			return list->value;
		list++;
	}

	Throwback_Error(p->line, p->column
			, "illegal value '%s' for %s"
			, p->pstring, p->pname);

	return list->value;
}

static int checklist_findindex(const check* list, const char* name)
{
	int index = 0;

	while (list->name)
	{
		if (!strcmp(list->name, name))
			return index;

		index++;
		list++;
	}

	return -1;
}

static bool var_validateparams(const gr_node* pvar, const check* checklist)
{
	const gr_node* p;
	const check* pcheck;
	bool bValid = true;
	int mask = 0;
	int index;

	for (p = pvar->pparams; p; p = p->pnext)
	{
		// find param name in allowed list
		index = checklist_findindex(checklist, p->pname);

		if (index == -1)
		{
			// not found, report error
			if (p->type == gr_type_var)
				Throwback_Error(p->line, p->column
						, "'%s' variables are not allowed within %s"
						, p->pname, pvar->pname);
			else
				Throwback_Error(p->line, p->column
						, "'%s' is not a valid attribute within type %s"
						, p->pname, pvar->pname);
			bValid = false;
		}
		else
		{
			pcheck = &checklist[index];
			// found
			if (pcheck->type != p->type)
			{
				// parameter is not of expected type
				Throwback_Error(p->line, p->column
						, "%s should be of type '%s'"
						, p->pname, gr_types[pcheck->type]);
				bValid = false;
			}
			else if ((mask & (1<<index)) && ((pcheck->attributes & ck_multi) == 0))
			{
				// non multiple parameter is found more than once
				Throwback_Error(p->line, p->column
						, "%s is already defined for %s"
						, p->pname, pvar->pname);
				bValid = false;
			}
			// keep notice that parameter has been used
			mask |= 1<<index;
		}
	}

	for (index = 1, pcheck = checklist; pcheck->name; index <<= 1, pcheck++)
	{
		if (!(mask & index) && !(pcheck->attributes & ck_opt))
		{
			// mandatory parameter is missing
			Throwback_Error(pvar->line, pvar->column
					, "%s '%s' is missing"
					, gr_types[pcheck->type], pcheck->name);
			bValid = false;
		}
	}

	return bValid;
}

static check ckSize[] =
{
	  {"X",	gr_type_int_prop, 0}
	, {"Y",	gr_type_int_prop, 0}
	, {0, gr_type_none, 0}
};

static void SkSize_Fill(CSize* pSize, const gr_node* pNode)
{
	const gr_node* pParam;

	if (!var_validateparams(pNode, ckSize))
		return;

	// Loop on parameters
	for(pParam = pNode->pparams; pParam; pParam = pParam->pnext)
	{
		// find param name in allowed list
		switch(checklist_findindex(ckSize, pParam->pname))
		{
			case 0:
			{
				pSize->cx = pParam->ivalue;
			}
			break;
			case 1:
			{
				pSize->cy = pParam->ivalue;
			}
			break;
		}
	}
}

static check ckPoint[] =
{
	  {"X",	gr_type_int_prop, 0}
	, {"Y",	gr_type_int_prop, 0}
	, {0, gr_type_none, 0}
};

static void SkPoint_Fill(CPoint* pPoint, const gr_node* pNode)
{
	const gr_node* pParam;

	if (!var_validateparams(pNode, ckPoint))
		return;

	// Loop on parameters
	for(pParam = pNode->pparams; pParam; pParam = pParam->pnext)
	{
		// find param name in allowed list
		switch(checklist_findindex(ckPoint, pParam->pname))
		{
			case 0:
			{
				pPoint->x = pParam->ivalue;
			}
			break;
			case 1:
			{
				pPoint->y = pParam->ivalue;
			}
			break;
		}
	}
}

static check ckFont[] =
{
	  {"Name",	gr_type_str_prop, 0}
	, {"Size",	gr_type_attr, 0}
	, {0, gr_type_none, 0}
};

static void SkFont_Delete(SkFont* pFont)
{
	if (!pFont) return;

	mem_free(pFont->pId);
	mem_free(pFont->pName);
	if (pFont->Handle) Font_Lose(pFont->Handle);
	mem_free(pFont);
}

static _EXCEPTION SkFont* SkFont_New(const gr_node* pNode)
{
	SkFont* pFont;
	const gr_node* pParam;

	if (!var_validateparams(pNode, ckFont))
		throw_os(&script_error);

	pFont = throw_mem_alloc(sizeof(*pFont));

	pFont->pId = NULL;
	pFont->pName = NULL;
	pFont->Size.cx = 0;
	pFont->Size.cy = 0;
	pFont->Handle = 0;

	try
	{
		pFont->pId = throw_mem_allocstring(pNode->pname);

		// Loop on parameters
		for(pParam = pNode->pparams; pParam; pParam = pParam->pnext)
		{
			// find param name in allowed list
			switch(checklist_findindex(ckFont, pParam->pname))
			{
				case 0:
				{
					pFont->pName = throw_mem_allocstring(pParam->pstring);
				}
				break;
				case 1:
				{
					SkSize_Fill(&pFont->Size, pParam);
				}
				break;
			}
		}
	}
	catch
	{
		SkFont_Delete(pFont);
		throw_current();
	}
	catch_end

	return pFont;
}

static _EXCEPTION void SkFont_Validate(Skin* pSkin, SkFont* pFont)
{
	IGNORE(pSkin);
	IGNORE(pFont);
}

static check ckPointer[] =
{
	  {"Sprite",	gr_type_str_prop, 0}
	, {"Offset",	gr_type_attr, ck_opt}
	, {0, gr_type_none, 0}
};

static void SkPointer_Delete(SkPointer* pPointer)
{
	if (!pPointer) return;

	mem_free(pPointer->pId);
	mem_free(pPointer->pName);
	mem_free(pPointer);
}

static _EXCEPTION SkPointer* SkPointer_New(const gr_node* pNode)
{
	SkPointer* pPointer;
	const gr_node* pParam;

	if (!var_validateparams(pNode, ckPointer))
		throw_os(&script_error);

	pPointer = throw_mem_alloc(sizeof(*pPointer));
	pPointer->pId = NULL;
	pPointer->pName = NULL;
	pPointer->Offset.x = 0;
	pPointer->Offset.y = 0;
	pPointer->pSprite = NULL;

	try
	{
		pPointer->pId = throw_mem_allocstring(pNode->pname);

		// Loop on parameters
		for(pParam = pNode->pparams; pParam; pParam = pParam->pnext)
		{
			// find param name in allowed list
			switch(checklist_findindex(ckPointer, pParam->pname))
			{
				case 0:
				{
					pPointer->pName = throw_mem_allocstring(pParam->pstring);
				}
				break;
				case 1:
				{
					SkPoint_Fill(&pPointer->Offset, pParam);
				}
				break;
			}
		}
	}
	catch
	{
		SkPointer_Delete(pPointer);
		throw_current();
	}
	catch_end

	return pPointer;
}

static _EXCEPTION void SkPointer_Validate(Skin* pSkin, SkPointer* pPointer)
{
	IGNORE(pSkin);
	IGNORE(pPointer);
}

static void SkSprites_SkSprites(CPtrArray* pSprites)
{
	PtrArray_PtrArray(pSprites, 10);
}

static void SkSprites_NotSkSprites(CPtrArray* pSprites)
{
	SkSprite* pSprite;
	int i;

	for(i = PtrArray_Count(pSprites) - 1; i >= 0; i--)
	{
		pSprite = PtrArray_Get(pSprites, i);
		mem_free(pSprite->pName);
		mem_free(pSprite);
	}

	PtrArray_NotPtrArray(pSprites);
}

static _EXCEPTION void SkSprites_Validate(Skin* pSkin, CPtrArray* pSprites)
{
	IGNORE(pSkin);
	IGNORE(pSprites);
}

static _EXCEPTION SkSprite* SkSprites_Add(CPtrArray* pSprites, const char* pName)
{
	SkSprite* pSprite = throw_mem_alloc(sizeof(*pSprite));

	try
	{
		pSprite->pName = throw_mem_allocstring(pName);
		pSprite->pSprite = NULL;
		PtrArray_Insert(pSprites, pSprite, -1);
	}
	catch
	{
		mem_free(pSprite);
		throw_current();
	}
	catch_end

	return pSprite;
}

static _EXCEPTION void SkObject_SkObject(SkObject* pObject, SkObject* pParent, SkType Type, const VSkObject* pVFs)
{
	pObject->pVFs = pVFs;
	pObject->pId = NULL;
	pObject->Type = Type;
	pObject->Box.x0 = 0;
	pObject->Box.x1 = 0;
	pObject->Box.y0 = 0;
	pObject->Box.y1 = 0;
	pObject->pPointer = NULL;
	pObject->Action = sk_cmd_none;
	pObject->ActionParams = NULL;
	pObject->pParent = pParent;
	pObject->pFirstChild = NULL;
	pObject->pLastChild = NULL;
	pObject->pPrevious = NULL;
	pObject->pNext = NULL;
	if (pParent)
	{
		SkObject* pNode = pParent->pLastChild;
		if (pNode)
		{
			pNode->pNext = pObject;
			pObject->pPrevious = pNode;
		}
		else pParent->pFirstChild = pObject;
		pParent->pLastChild = pObject;
	}
}

static void SkObject_NotSkObject(SkObject* pObject)
{
	SkObject* pNode;

        for (pNode = pObject->pFirstChild; pNode; pNode = pNode->pNext)
		pNode->pVFs->Delete(pNode);

	mem_free(pObject->pId);
	mem_free(pObject->pPointer);
	mem_free(pObject->ActionParams);

	pNode = pObject->pParent;
	if (pNode)
	{
		if (pNode->pLastChild == pObject)
			pNode->pLastChild = pObject->pPrevious;
		if (pNode->pFirstChild == pObject)
			pNode->pFirstChild = pObject->pNext;
	}

	pNode = pObject->pPrevious;
	if (pNode) pNode->pNext = pObject->pNext;

	pNode = pObject->pNext;
	if (pNode) pNode->pPrevious = pObject->pPrevious;
}

static _EXCEPTION void SkObject_Validate(Skin* pSkin, SkObject* pObject)
{
	SkObject* pNode;

	if (pObject->pVFs) pObject->pVFs->Validate(pSkin, pObject);

	for (pNode = pObject->pFirstChild; pNode; pNode = pNode->pNext)
		SkObject_Validate(pSkin, pNode);
}

static check ckText[] =
{
	  {"X0",	gr_type_int_prop, 0}
	, {"X1",	gr_type_int_prop, 0}
	, {"Y0",	gr_type_int_prop, 0}
	, {"Font",	gr_type_attr_prop, 0}
	, {"Contents",	gr_type_attr_prop, 0}
	, {0, gr_type_none, 0}
};

static void SkText_Delete(SkText* pText)
{
	if (!pText) return;

	SkObject_NotSkObject(&pText->Object);
	mem_free(pText->pContents);
	mem_free(pText);
}

static const VSkObject VSkText =
{
	(VSkObject_Delete) SkText_Delete,
	(VSkObject_Validate) NULL
};

static _EXCEPTION SkText* SkText_New(Skin* pSkin, SkObject* pParent, const gr_node* pNode)
{
	SkText* pText;
	const gr_node* pParam;

	if (!var_validateparams(pNode, ckText))
		throw_os(&script_error);

	pText = throw_mem_alloc(sizeof(*pText));

	try
	{
		pText->pFont = NULL;
		SkObject_SkObject(&pText->Object, pParent, sk_type_text, &VSkText);

		pText->Object.pId = throw_mem_allocstring(pNode->pname);

		// Loop on parameters
		for(pParam = pNode->pparams; pParam; pParam = pParam->pnext)
		{
			// find param name in allowed list
			switch(checklist_findindex(ckText, pParam->pname))
			{
				case 0:
				{
					pText->Object.Box.x0 = pParam->ivalue;
				}
				break;
				case 1:
				{
					pText->Object.Box.x1 = pParam->ivalue;
				}
				break;
				case 2:
				{
					pText->Object.Box.y0 = pText->Object.Box.y1 = pParam->ivalue;
				}
				break;
				case 3:
				{
					pText->pFont = Skin_FindFont(pSkin, pParam->pstring);
				}
				break;
				case 4:
				{
					pText->pContents = throw_mem_allocstring(pParam->pstring);
				}
				break;
			}
		}
	}
	catch
	{
		SkText_Delete(pText);
		throw_current();
	}
	catch_end

	return pText;
}

static check ckButton[] =
{
	  {"Action",	gr_type_attr_prop, 0}
	, {"Pointer",	gr_type_attr_prop, ck_opt}
	, {"Sprites",	gr_type_list, 0}
	, {"X",		gr_type_int_prop, 0}
	, {"Y",		gr_type_int_prop, 0}
	, {0, gr_type_none, 0}
};

static void SkButton_Delete(SkButton* pButton)
{
	if (!pButton) return;

	SkObject_NotSkObject(&pButton->Object);
	SkSprites_NotSkSprites(&pButton->Sprites);
	mem_free(pButton);
}

static _EXCEPTION void SkButton_Validate(Skin* pSkin, SkButton* pButton)
{
	// check sprites
	SkSprites_Validate(pSkin, &pButton->Sprites);
}

static const VSkObject VSkButton =
{
	(VSkObject_Delete) SkButton_Delete,
	(VSkObject_Validate) SkButton_Validate
};

static _EXCEPTION SkButton* SkButton_New(Skin* pSkin, SkObject* pParent, const gr_node* pNode)
{
	SkButton* pButton;
	const gr_node* pParam;

	if (!var_validateparams(pNode, ckButton))
		throw_os(&script_error);

	pButton = throw_mem_alloc(sizeof(*pButton));

	try
	{
		SkSprites_SkSprites(&pButton->Sprites);
		SkObject_SkObject(&pButton->Object, pParent, sk_type_button, &VSkButton);

		pButton->Object.pId = throw_mem_allocstring(pNode->pname);

		// Loop on parameters
		for(pParam = pNode->pparams; pParam; pParam = pParam->pnext)
		{
			// find param name in allowed list
			switch(checklist_findindex(ckButton, pParam->pname))
			{
				case 0:
				{
					pButton->Object.Action = 0;
				}
				break;
				case 1:
				{
					pButton->Object.pPointer = Skin_FindPointer(pSkin, pParam->pstring);
				}
				break;
				case 2:
				{
					SkSprites_Add(&pButton->Sprites, pParam->pstring);
				}
				break;
				case 3:
				{
					pButton->Object.Box.x0 = pParam->ivalue;
				}
				break;
				case 4:
				{
					pButton->Object.Box.y0 = pParam->ivalue;
				}
				break;
			}
		}
	}
	catch
	{
		SkButton_Delete(pButton);
		throw_current();
	}
	catch_end

	return pButton;
}

static check ckToggle[] =
{
	  {"Action",	gr_type_attr_prop, 0}
	, {"Pointer",	gr_type_attr_prop, ck_opt}
	, {"Sprites",	gr_type_list, 0}
	, {"X",		gr_type_int_prop, 0}
	, {"Y",		gr_type_int_prop, 0}
	, {0, gr_type_none, 0}
};

static void SkToggle_Delete(SkToggle* pToggle)
{
	if (!pToggle) return;

	SkObject_NotSkObject(&pToggle->Object);
	SkSprites_NotSkSprites(&pToggle->Sprites);
	mem_free(pToggle);
}

static _EXCEPTION void SkToggle_Validate(Skin* pSkin, SkToggle* pToggle)
{
	// check sprites
	SkSprites_Validate(pSkin, &pToggle->Sprites);
}

static const VSkObject VSkToggle =
{
	(VSkObject_Delete) SkToggle_Delete,
	(VSkObject_Validate) SkToggle_Validate
};

static _EXCEPTION SkToggle* SkToggle_New(Skin* pSkin, SkObject* pParent, const gr_node* pNode)
{
	SkToggle* pToggle;
	const gr_node* pParam;

	if (!var_validateparams(pNode, ckToggle))
		throw_os(&script_error);

	pToggle = throw_mem_alloc(sizeof(*pToggle));

	try
	{
		PtrArray_PtrArray(&pToggle->Sprites, 10);
		SkObject_SkObject(&pToggle->Object, pParent, sk_type_toggle, &VSkToggle);

		pToggle->Object.pId = throw_mem_allocstring(pNode->pname);

		// Loop on parameters
		for(pParam = pNode->pparams; pParam; pParam = pParam->pnext)
		{
			// find param name in allowed list
			switch(checklist_findindex(ckToggle, pParam->pname))
			{
				case 0:
				{
					pToggle->Object.Action = 0;
				}
				break;
				case 1:
				{
					pToggle->Object.pPointer = Skin_FindPointer(pSkin, pParam->pstring);
				}
				break;
				case 2:
				{
					SkSprites_Add(&pToggle->Sprites, pParam->pstring);
				}
				break;
				case 3:
				{
					pToggle->Object.Box.x0 = pParam->ivalue;
				}
				break;
				case 4:
				{
					pToggle->Object.Box.y0 = pParam->ivalue;
				}
				break;
			}
		}
	}
	catch
	{
		SkToggle_Delete(pToggle);
		throw_current();
	}
	catch_end

	return pToggle;
}

static check ckSlider[] =
{
	  {"Action",	gr_type_attr_prop, 0}
	, {"Pointer",	gr_type_attr_prop, ck_opt}
	, {0, gr_type_none, 0}
};

static void SkSlider_Delete(SkSlider* pSlider)
{
	if (!pSlider) return;

	SkObject_NotSkObject(&pSlider->Object);
	mem_free(pSlider);
}

static _EXCEPTION void SkSlider_Validate(Skin* pSkin, SkSlider* pSlider)
{
	IGNORE(pSkin);
	IGNORE(pSlider);
	// check sprites
//	SkSprites_Validate(pSkin, &pSlider->Sprites);
}

static const VSkObject VSkSlider =
{
	(VSkObject_Delete) SkSlider_Delete,
	(VSkObject_Validate) SkSlider_Validate
};

static _EXCEPTION SkSlider* SkSlider_New(Skin* pSkin, SkObject* pParent, const gr_node* pNode)
{
	SkSlider* pSlider;
	const gr_node* pParam;

	if (!var_validateparams(pNode, ckSlider))
		throw_os(&script_error);

	pSlider = throw_mem_alloc(sizeof(*pSlider));

	try
	{
		SkObject_SkObject(&pSlider->Object, pParent, sk_type_slider, &VSkSlider);

		pSlider->Object.pId = throw_mem_allocstring(pNode->pname);

		// Loop on parameters
		for(pParam = pNode->pparams; pParam; pParam = pParam->pnext)
		{
			// find param name in allowed list
			switch(checklist_findindex(ckSlider, pParam->pname))
			{
				case 0:
				{
					pSlider->Object.Action = 0;
				}
				break;
				case 1:
				{
					pSlider->Object.pPointer = Skin_FindPointer(pSkin, pParam->pstring);
				}
				break;
			}
		}
	}
	catch
	{
		SkSlider_Delete(pSlider);
		throw_current();
	}
	catch_end

	return pSlider;
}

static check ckArea[] =
{
	  {"Action",	gr_type_attr_prop, 0}
	, {"Pointer",	gr_type_attr_prop, ck_opt}
	, {"Sprites",	gr_type_list, ck_opt}
	, {"X0",	gr_type_int_prop, 0}
	, {"Y0",	gr_type_int_prop, 0}
	, {"X1",	gr_type_int_prop, 0}
	, {"Y1",	gr_type_int_prop, 0}
	, {"Text",	gr_type_var, ck_opt | ck_multi}
	, {0, gr_type_none, 0}
};

static void SkArea_Delete(SkArea* pArea)
{
	if (!pArea) return;

	SkObject_NotSkObject(&pArea->Object);
	SkSprites_NotSkSprites(&pArea->Sprites);
	mem_free(pArea);
}

static _EXCEPTION void SkArea_Validate(Skin* pSkin, SkArea* pArea)
{
	// check sprites
	SkSprites_Validate(pSkin, &pArea->Sprites);
}

static const VSkObject VSkArea =
{
	(VSkObject_Delete) SkArea_Delete,
	(VSkObject_Validate) SkArea_Validate
};

static _EXCEPTION SkArea* SkArea_New(Skin* pSkin, SkObject* pParent, const gr_node* pNode)
{
	SkArea* pArea;
	const gr_node* pParam;

	if (!var_validateparams(pNode, ckArea))
		throw_os(&script_error);

	pArea = throw_mem_alloc(sizeof(*pArea));

	try
	{
		SkSprites_SkSprites(&pArea->Sprites);
		SkObject_SkObject(&pArea->Object, pParent, sk_type_area, &VSkArea);

		pArea->Object.pId = throw_mem_allocstring(pNode->pname);

		// Loop on parameters
		for(pParam = pNode->pparams; pParam; pParam = pParam->pnext)
		{
			// find param name in allowed list
			switch(checklist_findindex(ckArea, pParam->pname))
			{
				case 0:
				{
					pArea->Object.Action = 0;
				}
				break;
				case 1:
				{
					pArea->Object.pPointer = Skin_FindPointer(pSkin, pParam->pstring);
				}
				break;
				case 2:
				{
					SkSprites_Add(&pArea->Sprites, pParam->pstring);
				}
				break;
				case 3:
				{
					pArea->Object.Box.x0 = pParam->ivalue;
				}
				break;
				case 4:
				{
					pArea->Object.Box.y0 = pParam->ivalue;
				}
				break;
				case 5:
				{
					pArea->Object.Box.x1 = pParam->ivalue;
				}
				break;
				case 6:
				{
					pArea->Object.Box.y1 = pParam->ivalue;
				}
				break;
				case 7:
				{
					SkText_New(pSkin, &pArea->Object, pParam);
				}
				break;
			}
		}
	}
	catch
	{
		SkArea_Delete(pArea);
		throw_current();
	}
	catch_end

	return pArea;
}

static check ckPanel[] =
{
	  {"X0",	gr_type_int_prop, 0}
	, {"Y0",	gr_type_int_prop, 0}
	, {"X1",	gr_type_int_prop, 0}
	, {"Y1",	gr_type_int_prop, 0}
	, {"Text",	gr_type_var, ck_opt | ck_multi}
	, {"Button",	gr_type_var, ck_opt | ck_multi}
	, {"Toggle",	gr_type_var, ck_opt | ck_multi}
	, {"Slider",	gr_type_var, ck_opt | ck_multi}
	, {"Area",	gr_type_var, ck_opt | ck_multi}
	, {0, gr_type_none, 0}
};

static void SkPanel_Delete(SkPanel* pPanel)
{
	if (!pPanel) return;

	SkObject_NotSkObject(&pPanel->Object);
	mem_free(pPanel);
}

static const VSkObject VSkPanel =
{
	(VSkObject_Delete) SkPanel_Delete,
	(VSkObject_Validate) NULL
};

static _EXCEPTION SkPanel* SkPanel_New(Skin* pSkin, SkObject* pParent, const gr_node* pNode)
{
	SkPanel* pPanel;
	const gr_node* pParam;

	if (!var_validateparams(pNode, ckPanel))
		throw_os(&script_error);

	pPanel = throw_mem_alloc(sizeof(*pPanel));

	try
	{
		SkObject_SkObject(&pPanel->Object, pParent, sk_type_panel, &VSkPanel);

		pPanel->Object.pId = throw_mem_allocstring(pNode->pname);

		// Loop on parameters
		for(pParam = pNode->pparams; pParam; pParam = pParam->pnext)
		{
			// find param name in allowed list
			switch(checklist_findindex(ckPanel, pParam->pname))
			{
				case 0:
				{
					pPanel->Object.Box.x0 = pParam->ivalue;
				}
				break;
				case 1:
				{
					pPanel->Object.Box.y0 = pParam->ivalue;
				}
				break;
				case 2:
				{
					pPanel->Object.Box.x1 = pParam->ivalue;
				}
				break;
				case 3:
				{
					pPanel->Object.Box.y1 = pParam->ivalue;
				}
				break;
				case 4:
				{
					SkText_New(pSkin, &pPanel->Object, pParam);
				}
				break;
				case 5:
				{
					SkButton_New(pSkin, &pPanel->Object, pParam);
				}
				break;
				case 6:
				{
					SkToggle_New(pSkin, &pPanel->Object, pParam);
				}
				break;
				case 7:
				{
					SkSlider_New(pSkin, &pPanel->Object, pParam);
				}
				break;
				case 8:
				{
					SkArea_New(pSkin, &pPanel->Object, pParam);
				}
				break;
			}
		}
	}
	catch
	{
		SkPanel_Delete(pPanel);
		throw_current();
	}
	catch_end

	return pPanel;
}

static check ckSkin[] =
{
	  {"Font",	gr_type_var, ck_opt | ck_multi}
	, {"Pointer",	gr_type_var, ck_opt | ck_multi}
	, {"Panel",	gr_type_var, ck_opt | ck_multi}
	, {0, gr_type_none, 0}
};

static _EXCEPTION void Skin_Validate(Skin* pSkin)
{
	{
		SkObject* pPanel;

		while ((pPanel = PtrArray_Get(&pSkin->m_pPanels, 0)) != NULL)
		{
			SkObject_Validate(pSkin, pPanel);
		}
	}
	{
		SkFont* pFont;

		while ((pFont = PtrArray_Get(&pSkin->m_pFonts, 0)) != NULL)
		{
			SkFont_Validate(pSkin, pFont);
		}
	}
	{
		SkPointer* pPointer;

		while ((pPointer = PtrArray_Get(&pSkin->m_pPointers, 0)) != NULL)
		{
			SkPointer_Validate(pSkin, pPointer);
		}
	}
}

_EXCEPTION Skin* Skin_New(const char* pFilename)
{
	Skin* pSkin = throw_mem_alloc(sizeof(*pSkin));

	try
	{
		Skin_Skin(pSkin, pFilename);
	}
	catch
	{
		Skin_Delete(pSkin);
		throw_current();
	}
	catch_end

	return pSkin;
}

void Skin_Delete(Skin* pSkin)
{
	Skin_NotSkin(pSkin);
	mem_free(pSkin);
}

_EXCEPTION void Skin_Skin(Skin* pSkin, const char* pFilename)
{
	gr_tree * pTree;
	const gr_node* pParam;
	const gr_node* pNode;

	PtrArray_PtrArray(&pSkin->m_pPanels, 5);
	PtrArray_PtrArray(&pSkin->m_pFonts, 5);
	PtrArray_PtrArray(&pSkin->m_pPointers, 5);

	pTree = grtree_parse(pFilename);
	if (!pTree)
		throw_os(&script_error);
	pNode = pTree->m_ptop;

	Throwback_Start(pFilename);

	try
	{
		if (!var_validateparams(pNode, ckSkin))
			throw_os(&script_error);

//		pSkin->pId = throw_mem_allocstring(pNode->pname);

		// Loop on parameters
		for(pParam = pNode->pparams; pParam; pParam = pParam->pnext)
		{
			// find param name in allowed list
			switch(checklist_findindex(ckSkin, pParam->pname))
			{
				case 0:
				{
					SkFont_New(pParam);
				}
				break;
				case 1:
				{
					SkPointer_New(pParam);
				}
				break;
				case 2:
				{
					SkPanel_New(pSkin, NULL, pParam);
				}
				break;
			}
		}
	}
	catch
	{
		Throwback_Stop();
		Skin_NotSkin(pSkin);
		throw_current();
	}
	catch_end

	Throwback_Stop();
}

void Skin_NotSkin(Skin* pSkin)
{
	{
		SkPanel* pPanel;

		while ((pPanel = PtrArray_Get(&pSkin->m_pPanels, 0)) != NULL)
		{
			SkPanel_Delete(pPanel);
			PtrArray_Del(&pSkin->m_pPanels, 0);
		}
		PtrArray_NotPtrArray(&pSkin->m_pPanels);
	}
	{
		SkFont* pFont;

		while ((pFont = PtrArray_Get(&pSkin->m_pFonts, 0)) != NULL)
		{
			SkFont_Delete(pFont);
			PtrArray_Del(&pSkin->m_pFonts, 0);
		}
		PtrArray_NotPtrArray(&pSkin->m_pFonts);
	}
	{
		SkPointer* pPointer;

		while ((pPointer = PtrArray_Get(&pSkin->m_pPointers, 0)) != NULL)
		{
			SkPointer_Delete(pPointer);
			PtrArray_Del(&pSkin->m_pPointers, 0);
		}
		PtrArray_NotPtrArray(&pSkin->m_pPointers);
	}
}

SkFont* Skin_FindFont(const Skin* pSkin, const char* pName)
{
	return NULL;
}

SkPointer* Skin_FindPointer(const Skin* pSkin, const char* pName)
{
	return NULL;
}
