#include <string.h>

#include "WimpLib:Desktop.h"
#include "WimpLib:Display.h"
#include "WimpLib:Exception.h"
#include "WimpLib:Keyboard.h"
#include "WimpLib:mem.h"
#include "WimpLib:Message.h"
#include "WimpLib:Task.h"
#include "WimpLib:Window.h"

#include "TreeList.h"

struct TreeNode
{
	TreeNode* pParent;
	TreeNode* pLeftBrother;
	TreeNode* pRightBrother;
	TreeNode* pChild;
	char*     pString;
	char*     pSprite;
	void*     pData;
	bool      bExpanded;
};

struct TreeList
{
	WListCore	m_Core;
	TreeNode*	m_pRootNode;
};

#define Cmd_ExpandOrCollapse     0
#define Cmd_ExplodeOrCollapse    1
#define Cmd_ExplodeAllOrCollapse 2

/*-------------------------------------------------------------------------*
 *--- TreeList Redraw handling --------------------------------------------*
 *-------------------------------------------------------------------------*/

#define Tree_Indent 40
#define Tree_Height 44

static void PlotItem(const void* pObject, const void* pOwner, const WPlotItem* pItem)
{
	const TreeList* This = pOwner;
	const TreeNode* pNode = pObject;
	const Mode_Info* Mode = Task_GetModeInfo();
	CSpriteFactors   scale = {1, 1, 1, 1};
	char   sprite[13] = "small_dir";
	const TreeNode* pTmp;
	const TreeNode* pBrother;
	CRect  box;
	CRect  box2;
	CPoint pt;
	CSize  size;
	int    bcol, fcol;
	int    i, x, y;
	bool bHasBox = TreeNode_AllowExpand(pNode);
	int level = TreeNode_GetLevel(pNode);

	box = RectToScreen(&pItem->m_box, &pItem->m_cvtinfo);
	RoundRectForPlot(&box);

	Desktop_SetStdColour(7);

	x = box.x0 + (level * Tree_Indent) - Tree_Indent/2;
	y = (box.y1 + box.y0)/2;

	// Set dash-line pattern
	Display_SetDashPattern(8, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa);

	pBrother = pNode->pLeftBrother;
	if (pBrother || TreeNode_GetParent(TreeNode_GetParent(pNode)))
	{
		Display_Plot(4, x, box.y1);
		Display_Plot(29, x, y + (bHasBox ? 8 : 0));
	}

	pBrother = pNode->pRightBrother;
	if (pBrother)
	{
		Display_Plot(4, x, y - (bHasBox ? 8 : 0));
		Display_Plot(29, x, box.y0);
	}
	Display_Plot(4, x + (bHasBox ? 8 : 0), y);
	Display_Plot(29, box.x0 - 1 + level * Tree_Indent, y);

	if (bHasBox)
	{
		Display_Plot(4, x - 8, y - 8);
		Display_Plot(5, x - 8, y + 8);
		Display_Plot(5, x + 8, y + 8);
		Display_Plot(5, x + 8, y - 8);
		Display_Plot(5, x - 8, y - 8);
		if ((pItem->m_index + 1) < WListCore_Count(&This->m_Core))
		{
			pTmp = WListCore_Get(&This->m_Core, pItem->m_index + 1);
			pTmp = TreeNode_GetParent(pTmp);
		}
		else
			pTmp = NULL;

		if (pTmp != pNode)
		{
			Display_Plot(4, x, y - 4);
			Display_Plot(5, x, y + 4);
		}
		Display_Plot(4, x - 4, y);
		Display_Plot(5, x + 4, y);
	}

	pTmp = TreeNode_GetParent(pNode);

	for (i = level - 2; i >= 0; i--)
	{
		x -= Tree_Indent;
		pBrother = pTmp->pRightBrother;
		if (pBrother)
		{
			Display_Plot(4, x, box.y1);
			Display_Plot(29, x, box.y0);
		}
		pTmp = TreeNode_GetParent(pTmp);
	}

	// Plot sprite
	if (pNode->pSprite)
	{
		strncpy(sprite, pNode->pSprite, 12);
		sprite[12] = 0;
		size = Desktop_GetNamedSpriteSize(sprite);
	}
	else size.cx = 0;

	if (size.cx > 0)
	{
		if ((size.cx > Tree_Indent * scale.xdiv)
		||  (size.cy > Tree_Height * scale.ydiv))
		{
			if (size.cx * Tree_Height * scale.ydiv < size.cy * Tree_Indent * scale.xdiv)
			{
				scale.xmag = scale.ymag = Tree_Height * scale.ydiv;
				scale.xdiv *= size.cy;
				scale.ydiv *= size.cy;
			}
			else
			{
				scale.xmag = scale.ymag = Tree_Indent * scale.xdiv;
				scale.xdiv *= size.cx;
				scale.ydiv *= size.cx;
			}
		}
		size.cx *= scale.xmag;
		size.cx /= scale.xdiv;
		size.cy *= scale.ymag;
		size.cy /= scale.ydiv;

		pt.x = box.x0 + level * Tree_Indent + (Tree_Indent - size.cx)/2;
		pt.y = box.y0 + (Tree_Height - size.cy)/2;
		Desktop_PlotNamedSprite(sprite, pt, &scale);
	}

	// Plot text
	switch (pItem->m_flags & (EWItem_Active | EWItem_Selected))
	{
		case EWItem_Active:
		{
			bcol = 1;
			fcol = 4;
		}
		break;
		case EWItem_Selected:
		{
			bcol = 7;
			fcol = 0;
		}
		break;
		case (EWItem_Active + EWItem_Selected):
		{
			bcol = 5;
			fcol = 0;
		}
		break;
		default:
		{
			bcol = 1;
			fcol = 7;
		}
	}

	box.x0 = pItem->m_box.x0 + level * Tree_Indent;
	box.y0 = pItem->m_box.y0;
	box.x1 = box.x0 + Desktop_GetTextWidth(pNode->pString) + 16;
	box.y1 = pItem->m_box.y1;
	box.x1 -= Mode->dx;
	box.y1 -= Mode->dy;
	box = RectToScreen(&box, &pItem->m_cvtinfo);

	if (pItem->m_flags & EWItem_Selected)
	{
		Desktop_SetStdColour(bcol);
		// Plot filled rectangle
		Display_Plot(4, box.x0, box.y0);
		Display_Plot(101, box.x1, box.y1);
	}

	// Plot text
	Desktop_SetStdColour(fcol);
	Desktop_SetStdColour(0x80 + bcol);
	box2 = box;
	box2.x0 += 8;
	box2.x1 -= 8;
	Desktop_PlotText(pNode->pString, &box2);

	// Plot focus
	if (pItem->m_flags & EWItem_HasFocus)
	{
		if (pItem->m_flags & EWItem_Selected)
			Desktop_SetStdColour(0);
		else
			Desktop_SetStdColour(7);

		// Set dash-line pattern
		Display_SetDashPattern(24, 0xdb, 0x6d, 0xb6, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc);
		// plot dashed rectangle
		Display_Plot(4, box.x0, box.y0);
		Display_Plot(21, box.x0, box.y1);
		Display_Plot(53, box.x1, box.y1);
		Display_Plot(53, box.x1, box.y0);
		Display_Plot(53, box.x0, box.y0);
	}
}

static void GetItemSize(const void* pObject, const void* pOwner, int* pSizes)
{
	const TreeNode* pNode = pObject;

	IGNORE(pOwner);

	pSizes[0] = TreeNode_GetLevel(pNode) * Tree_Indent + Desktop_GetTextWidth(pNode->pString) + 16;
}

static CSize GetItemBox(const void* pObject, const void* pOwner, const int* pSizes, const int* pMaxSizes)
{
	CSize size;

	IGNORE(pObject);
	IGNORE(pOwner);
	IGNORE(pSizes);

	size.cx = pMaxSizes[0];
	size.cy = Tree_Height;

	return size;
}

static void GetArea(const void* pObject, const void* pOwner, const WPlotItem* pItem, const Mouse* pMouse, WItemArea* pArea)
{
	const TreeNode* pNode = pObject;
	int x, y;
	int level = TreeNode_GetLevel(pNode);
	CPoint offset = {pMouse->pt.x - pItem->m_box.x0, pMouse->pt.y - pItem->m_box.y0};

	IGNORE(pOwner);
	pArea->rect = pItem->m_box;

	if (offset.x >= level * Tree_Indent)
	{
		if (offset.x <= (level + 1) * Tree_Indent + Desktop_GetTextWidth(pNode->pString) + 16)
			pArea->id = EWItemArea_Select;
		else
			pArea->id = EWItemArea_Outside;

		return;
	}

	x = level * Tree_Indent - Tree_Indent/2;
	y = (pItem->m_box.y1 - pItem->m_box.y0)/2;

	if (pNode->pChild
	&&  (offset.x >= x - 10)
	&&  (offset.x <= x + 10)
	&&  (offset.y >= y - 10)
	&&  (offset.y <= y + 10))
	{
		if (pMouse->but & (EBut_Menu|EBut_DragSelect||EBut_DragAdjust))
			pArea->id = EWItemArea_Inactive;
		else if ((pMouse->but == EBut_Select)
		     ||  (pMouse->but == EBut_ClickSelect))
			pArea->id = EWTreeItemArea_ExpandOrCollapse;
		else
			pArea->id = EWTreeItemArea_ExplodeOrCollapse;

		return;
	}

	pArea->id =  EWItemArea_Outside;
}

/*-------------------------------------------------------------------------*
 *--- TreeNode Routines ---------------------------------------------------*
 *-------------------------------------------------------------------------*/

int TreeNode_GetLevel(const TreeNode* pNode)
{
	int level = 0;

	while (pNode->pParent)
	{
		level++;
		pNode = pNode->pParent;
	}

	return level;
}

/**
 * Deletes the node and all its child nodes.
 */

static void Delete_TreeNode(TreeNode* This)
{
	if (This)
	{
		while (This->pChild)
			Delete_TreeNode(This->pChild);

		if (This->pLeftBrother)
			This->pLeftBrother->pRightBrother = This->pRightBrother;
		else if (This->pParent)
			This->pParent->pChild = This->pRightBrother;

		if (This->pRightBrother)
			This->pRightBrother->pLeftBrother = This->pLeftBrother;

		mem_free(This->pString);
		mem_free(This->pSprite);
		mem_free(This);
	}
}

/**
 * Returns the data associated to the node.
 *
 * @returns     Pointer to the data associated to the node.
 */

const void* TreeNode_GetData(const TreeNode* This)
{
	return This->pData;
}

/**
 * Returns the parent node to this node.
 *
 * @returns     Pointer to parent node.
 */

TreeNode* TreeNode_GetParent(const TreeNode* This)
{
	return This->pParent;
}

/**
 * Returns the firt child node to this node.
 *
 * @returns     Pointer to first child node or NULL if no child exists.
 */

TreeNode* TreeNode_GetFirstChild(const TreeNode* This)
{
	return This->pChild;
}

TreeNode* TreeNode_GetLastChild(const TreeNode* This)
{
	TreeNode* pNode = This->pChild;

	while (pNode && pNode->pRightBrother)
		pNode = pNode->pRightBrother;

	return pNode;
}

/**
 * Returns the predecessor to this node.
 *
 * @returns     Pointer to sibling node or NULL if no such sibling.
 */

TreeNode* TreeNode_GetPredecessor(const TreeNode* This)
{
	return This->pLeftBrother;
}

/**
 * Returns the successor to this node.
 *
 * @returns     Pointer to sibling node or NULL if no such sibling.
 */

TreeNode* TreeNode_GetSuccessor(const TreeNode* This)
{
	return This->pRightBrother;
}

/**
 * Checks if a node may be expanded to show its child nodes.
 *
 * @returns     true if the node may be expanded.
 */

bool TreeNode_AllowExpand(const TreeNode* This)
{
	if (!This->pChild)
		return false;

	return true;
}

/**
 * Determines if a node is expanded (displayed or memorized as being in such state).
 *
 * @returns     true if the node is expanded.
 */

bool TreeNode_IsExpanded(const TreeNode* This)
{
	return This->bExpanded;
}

/**
 * Marks a node as expanded (displayed or memorized as being in such state).
 */

void TreeNode_SetExpanded(TreeNode* This, bool bExpanded)
{
	This->bExpanded = bExpanded;
}

/*-------------------------------------------------------------------------*
 *--- TreeList management -------------------------------------------------*
 *-------------------------------------------------------------------------*/

/**
 * Determines the index in the list after a node and its childs.
 *
 * @param   index  Index of the node in the displayed list.
 *
 * @returns        Index after the node and its childs.
 */

static int TreeList_GetIndexAfterNode(TreeList* This, int index) throws(index)
{
	int max = WListCore_Count(&This->m_Core);
	TreeNode* pParent = WListCore_Get(&This->m_Core, index);
	TreeNode* pNext;

	index++;

	while(index < max)
	{
		pNext = WListCore_Get(&This->m_Core, index);
		if (pNext->pParent == pParent)
			index = TreeList_GetIndexAfterNode(This, index);
		else
			break;
	}

	return index;
}

/**
 * Collapse a node in the displayed list.
 *
 * @param   index  Index of the node in the displayed list or -1 to hide all nodes.
 * @param   b      True if child nodes are to be marked as not expanded.
 */

static void TreeList_Collapse(TreeList* This, int index) throws(index)
{
	TreeNode* pParent;
	int end;

	WListCore_AllowRefresh(&This->m_Core, false);

	if (index == -1)
	{
		pParent = This->m_pRootNode;
		end = WListCore_Count(&This->m_Core);
	}
	else
	{
		pParent = WListCore_Get(&This->m_Core, index);
		end = TreeList_GetIndexAfterNode(This, index);
	}

	WListCore_DelRange(&This->m_Core, index + 1, end);
	WListCore_Refresh(&This->m_Core, index, index);

	TreeNode_SetExpanded(pParent, false);

	WListCore_AllowRefresh(&This->m_Core, true);
}

/**
 * Expands a node in the displayed list.
 *
 * @param   index  Index of the node in the displayed list or -1 to show the root nodes.
 * @param   mode    0 if child nodes are not to be reopened.
 *                  1 if child nodes are to be reopened accordingly to their expanded state.
 *                  2 if child nodes are to be reopened.
 * @param   bShow  True if child nodes are to be reopened accordingly to their expanded state.
 *
 * @returns        Index after the node and its childs.
 */

int TreeList_Expand(TreeList* This, int index, int mode, bool bShow)
{
	TreeNode* pParent;
	TreeNode* pChild;
	int pred;
	int sindex;

	if (index >= 0)
	{
		sindex = index;
		pParent = WListCore_Get(&This->m_Core, index);
	}
	else
	{
		sindex = 0;
		pParent = This->m_pRootNode;
	}

	WListCore_AllowRefresh(&This->m_Core, false);

	TreeNode_SetExpanded(pParent, true);

	pChild = pParent->pChild;
	pred = index;
	index++;

	while(pChild)
	{
		if ((index >= WListCore_Count(&This->m_Core))
		||  (WListCore_Get(&This->m_Core, index) != pChild))
		{
			try
			{
				throw_WListCore_Insert(&This->m_Core, index, pChild);
			}
			catch
			{
				break;
			}
			catch_end

			// Refresh parent or sibling
			WListCore_Refresh(&This->m_Core, pred, index);
			pred = index;

			// Expand?
			if ((mode > 1) || (TreeNode_IsExpanded(pChild) && (mode != 0)))
				index = TreeList_Expand(This, index, mode, false);
			else
			{
				TreeNode_SetExpanded(pChild, false);
				index++;
			}
		}
		else
		{
			pred = index;
			// Expand?
			if ((mode > 1) || (TreeNode_IsExpanded(pChild) && (mode != 0)))
				index = TreeList_Expand(This, index, mode, false);
			else
				index = TreeList_GetIndexAfterNode(This, index);
		}
		pChild = pChild->pRightBrother;
	}

	WListCore_AllowRefresh(&This->m_Core, true);

	// Move all of it to the window's visible area if possible
	if (bShow)
	{
		WListCore_ShowItem(&This->m_Core, index);
		WListCore_ShowItem(&This->m_Core, sindex);
	}

	return index;
}

/**
 * Executes a command on the treelist.
 *
 * @param   cmd     Command to execute.
 */

static void TreeList_ExecCommand(TreeList* This, int cmd)
{
	switch(cmd)
	{
		case Cmd_ExpandOrCollapse:
		{
			int index = WListCore_GetFocus(&This->m_Core);
			TreeNode* pNode = WListCore_Get(&This->m_Core, index);

			if (TreeNode_IsExpanded(pNode))
				TreeList_Collapse(This, index);
			else if (TreeNode_AllowExpand(pNode))
				TreeList_Expand(This, index, 0, true);
		}
		break;
		case Cmd_ExplodeOrCollapse:
		{
			int index = WListCore_GetFocus(&This->m_Core);
			TreeNode* pNode = WListCore_Get(&This->m_Core, index);

			if (TreeNode_IsExpanded(pNode))
				TreeList_Collapse(This, index);
			else if (TreeNode_AllowExpand(pNode))
				TreeList_Expand(This, index, 1, true);
		}
		break;
		case Cmd_ExplodeAllOrCollapse:
		{
			int index = WListCore_GetFocus(&This->m_Core);
			TreeNode* pNode = WListCore_Get(&This->m_Core, index);

			if (TreeNode_IsExpanded(pNode))
				TreeList_Collapse(This, index);
			else if (TreeNode_AllowExpand(pNode))
				TreeList_Expand(This, index, 2, true);
		}
		break;
	}
}

/**
 * TreeList event handler.
 */

static EListenerAction TreeList_EventHandler(void* handle, const Event* e)
{
	TreeList* This = handle;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;
			WItemArea area;
			int item = WListCore_ItemAreaFromScreenPt(&This->m_Core, m, 0, &area);

			switch (area.id)
			{
				case EWTreeItemArea_ExpandOrCollapse:
				{
					WListCore_SetFocus(&This->m_Core, item, true);
					TreeList_ExecCommand(This, Cmd_ExpandOrCollapse);

					return EListenerAction_StopEvent;
				}
				break;
				case EWTreeItemArea_ExplodeOrCollapse:
				{
					WListCore_SetFocus(&This->m_Core, item, true);
					TreeList_ExecCommand(This, Cmd_ExplodeOrCollapse);

					return EListenerAction_StopEvent;
				}
				break;
				case EWTreeItemArea_ExplodeAllOrCollapse:
				{
					WListCore_SetFocus(&This->m_Core, item, true);
					TreeList_ExecCommand(This, Cmd_ExplodeAllOrCollapse);

					return EListenerAction_StopEvent;
				}
				break;
				case EWItemArea_Select:
				{
					WListCore_SetFocus(&This->m_Core, item, true);
				}
				break;
				case EWItemArea_Inactive:
				{
					return EListenerAction_StopEvent;
				}
				break;
			}
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			switch(key->code)
			{
				case 0x1cd: // Ins
				case 0x1dd: // Shift + Ins
				case 0x1ed: // Ctrl + Ins
				{
					// Intercept
					return EListenerAction_StopEvent;
				}
				break;
				case 0x00d: // Return
				case 0x10d: // Shift + Return
				case 0x12d: // Ctrl + Return
				{
					int index = WListCore_GetFocus(&This->m_Core);

					if ((index >= 0) && (index < WListCore_Count(&This->m_Core)))
					{
						TreeNode* pNode = WListCore_Get(&This->m_Core, index);

						if (TreeNode_AllowExpand(pNode))
						{
							TreeList_ExecCommand(This, Keyboard_PollCtrl() ? Cmd_ExplodeAllOrCollapse
							                          : Keyboard_PollShift() ? Cmd_ExplodeOrCollapse
							                          : Cmd_ExpandOrCollapse);

							return EListenerAction_StopEvent;
						}
					}
				}
				break;
				case 0x18c: // left arrow
				{
					int index = WListCore_GetFocus(&This->m_Core);

					if (index > 0)
					{
						TreeNode* pChild = WListCore_Get(&This->m_Core, index);
						TreeNode* pTarget;
						TreeNode* pPrevious;

						// goto predecessor
						pTarget = pChild->pLeftBrother;

						// if none, goto parent
						if (!pTarget)
						{
							// if none, move to previous entry
							if (pChild->pParent == This->m_pRootNode)
								pTarget = WListCore_Get(&This->m_Core, index - 1);
							else
								pTarget = pChild->pParent;
						}

						do
						{
							index--;
							pPrevious = WListCore_Get(&This->m_Core, index);
						}
						while(pPrevious != pTarget);

						WListCore_MoveFocus(&This->m_Core, index);
					}

					return EListenerAction_StopEvent;
				}
				break;
				case 0x18d: // right arrow
				{
					int index = WListCore_GetFocus(&This->m_Core);
					int max = WListCore_Count(&This->m_Core);

					if ((index + 1) < max)
					{
						TreeNode* pChild = WListCore_Get(&This->m_Core, index);
						TreeNode* pTarget;

						// goto successor
						pTarget = pChild->pRightBrother;

						// if none, goto successor of parent
						while (!pTarget && (pChild->pParent != This->m_pRootNode))
						{
							pChild = pChild->pParent;
							pTarget = pChild->pRightBrother;
						}

						// if none, goto next entry
						if (!pTarget)
							index++;
						else
						{
							index = WListCore_Find(&This->m_Core, index, pTarget);
							// just in case display is damaged
							if (index == -1)
								index = WListCore_GetFocus(&This->m_Core) + 1;
						}

						WListCore_MoveFocus(&This->m_Core, index);
					}

					return EListenerAction_StopEvent;
				}
				break;
				case 0x19c: // Shift + left arrow
				{
					int index = WListCore_GetFocus(&This->m_Core);

					if (index > 0)
					{
						TreeNode* pChild = WListCore_Get(&This->m_Core, index);
						TreeNode* pTarget;
						TreeNode* pPrevious;

						// goto parent if possible, else goto predecessor
						if (pChild->pParent != This->m_pRootNode)
							pTarget = pChild->pParent;
						else
					    	pTarget = pChild->pLeftBrother;

						if (!pTarget) return EListenerAction_StopEvent;

						do
						{
							index--;
							pPrevious = WListCore_Get(&This->m_Core, index);
						}
						while(pPrevious != pTarget);

						WListCore_MoveFocus(&This->m_Core, index);
					}

					return EListenerAction_StopEvent;
				}
				break;
				case 0x19d: // Shift + right arrow
				{
					int index = WListCore_GetFocus(&This->m_Core);
					int max = WListCore_Count(&This->m_Core);

					if ((index + 1) < max)
					{
						TreeNode* pChild = WListCore_Get(&This->m_Core, index);
						TreeNode* pTarget = NULL;

						// goto successor of parent if possible
						while (!pTarget && (pChild->pParent != This->m_pRootNode))
						{
							pChild = pChild->pParent;
							pTarget = pChild->pRightBrother;
						}

						// if none, goto successor
						if (!pTarget)
							pTarget = pChild->pRightBrother;

						// if none, goto next entry
						if (!pTarget)
							index++;
						else
						{
							index = WListCore_Find(&This->m_Core, index, pTarget);
							// just in case display is damaged
							if (index == -1)
								index = WListCore_GetFocus(&This->m_Core) + 1;
						}

						WListCore_MoveFocus(&This->m_Core, index);
					}

					return EListenerAction_StopEvent;
				}
				break;
			}
		}
		break;
		case EEvent_Message:
		case EEvent_MessageWantAck:
		{
			switch(((Msg*) e->pData)->hdr.action)
			{
				case EMsg_HelpRequest:
				{
					const Msg_HelpRequest* msg = e->pData;
					WItemArea area;
					int item = WListCore_ItemAreaFromScreenPt(&This->m_Core, &msg->m, 0, &area);

   					switch(area.id)
   					{
						case EWTreeItemArea_ExpandOrCollapse:
						case EWTreeItemArea_ExplodeOrCollapse:
						case EWTreeItemArea_ExplodeAllOrCollapse:
						{
							const TreeNode* pNode = WListCore_Get(&This->m_Core, item);
							const char* txt;

							if (TreeNode_IsExpanded(pNode))
								txt = "HlpTree-";
							else
								txt = "HlpTree+";
							Task_HelpReply(msg, Msg_Lookup(txt));

							return EListenerAction_StopEvent;
						}
						break;
					}
				}
				break;
			}
		}
		break;
	}
	return EListenerAction_ContinueEvent;
}

static WListCore_FWList FWList =
{
	  {
		  PlotItem
		, GetItemSize
		, GetItemBox
		, GetArea
	  }
	, WOwner_PlotBackGround_White
};

TreeList* throw_New_TreeList(unsigned int flags, CTemplate* t, TreeNode* pRootNode)
{
	TreeList* This = throw_mem_calloc(1, sizeof(*This));

	IGNORE(flags);
	IGNORE(pRootNode);

	try
	{
		This->m_pRootNode = throw_mem_calloc(sizeof(*This->m_pRootNode), 1);

		throw_WListCore_WListCore(&This->m_Core, EWList_Transparent | EWList_SelModeSingle, t, This, 1, &FWList);
		throw_Window_RegisterEventHandler(WListCore_GetWindow(&This->m_Core), WListCore_ListEventHandler, &This->m_Core, false);
		throw_Window_RegisterEventHandler(WListCore_GetWindow(&This->m_Core), TreeList_EventHandler, This, true);
	}
	catch
	{
		Delete_TreeList(This);
		throw_current();
	}
	catch_end

	return This;
}

void Delete_TreeList(TreeList* This)
{
	if (This)
	{
		Delete_TreeNode(This->m_pRootNode);
		Window_DeRegisterEventHandler(WListCore_GetWindow(&This->m_Core), TreeList_EventHandler, This);
		Window_DeRegisterEventHandler(WListCore_GetWindow(&This->m_Core), WListCore_ListEventHandler, &This->m_Core);
		WListCore_NotWListCore(&This->m_Core);
		mem_free(This);
	}
}

TreeNode* TreeList_GetWListItem(const TreeList* This, int index) throws(index)
{
	return WListCore_Get(&This->m_Core, index);
}

TreeNode* TreeList_GetRootNode(const TreeList* This)
{
	return This->m_pRootNode;
}

/**
 * Expands the parent node of a given node in the treelist to render
 * the node visible and returns the index of the node in the list.
 *
 * @param pNode  Pointer to node to make visible.
 *
 * @returns Index of the node in the list.
 */

int TreeList_ExpandParents(TreeList* This, const TreeNode* pNode)
{
	int index = -1;

	if (!pNode) pNode = This->m_pRootNode;

	if (pNode != This->m_pRootNode)
	{
		// Expand parent node
		index = TreeList_ExpandParents(This, pNode->pParent);
	}

	TreeList_Expand(This, index, 0, false);

	return WListCore_Find(&This->m_Core, index, pNode);
}

HWind TreeList_GetWindow(const TreeList* This)
{
	return WListCore_GetWindow(&This->m_Core);
}

WListCore* TreeList_GetWListCore(TreeList* This)
{
	return &This->m_Core;
}

TreeNode* throw_TreeList_InsertChild(TreeList* This
				, TreeNode* pParent
				, TreeNode* pLeftBrother
				, const char* pSprite
				, const char* pString
				, void* pData)
{
	TreeNode* pNode = throw_mem_calloc(sizeof(TreeNode), 1);

	try
	{
		pNode->pString = throw_mem_allocstring(pString);
		pNode->pSprite = throw_mem_allocstring(pSprite);
		pNode->pData = pData;
	}
	catch
	{
		mem_free(pNode->pString);
		mem_free(pNode->pSprite);
		throw_current();
	}
	catch_end

	if (!pParent) pParent = This->m_pRootNode;
	pNode->pParent = pParent;
	pNode->pChild = NULL;
	pNode->pLeftBrother = pLeftBrother;
	if (pLeftBrother)
	{
		pNode->pRightBrother = pLeftBrother->pRightBrother;
		pLeftBrother->pRightBrother = pNode;
		if (pNode->pRightBrother) pNode->pRightBrother->pLeftBrother = pNode;
	}
	else
	{
		pNode->pRightBrother = pParent->pChild;
		pParent->pChild = pNode;
	}

	return pNode;
}
