#include "WimpLib:WList.h"

#include <stdlib.h>
#include <string.h>

#include "WimpLib:Coords.h"
#include "WimpLib:mem.h"
#include "WimpLib:Display.h"
#include "WimpLib:Desktop.h"
#include "WimpLib:DragDrop.h"
#include "WimpLib:Exception.h"
#include "WimpLib:Task.h"
#include "WimpLib:Template.h"
#include "WimpLib:Window.h"

#define tick_width 32

static void WList_PlotItem(const void* pObject, const void* pOwner, const WPlotItem* pItem)
{
	const WList* This = pOwner;
	CRect box;
	CRect box2;
	int fg, bg;

	if (pItem->m_flags & EWItem_Hover)
	{
		bg = 7;
		fg = 0;
	}
	else
	{
		switch (pItem->m_flags & (EWItem_Active | EWItem_Selected))
		{
			case EWItem_Active:
			{
				bg = 0;
				fg = 4;
			}
			break;
			case EWItem_Selected:
			{
				bg = 7;
				fg = 0;
			}
			break;
			case (EWItem_Active + EWItem_Selected):
			{
				bg = 5;
				fg = 0;
			}
			break;
			default:
			{
				bg = 0;
				fg = 7;
			}
		}
	}

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

	// Plot filled rectangle
	Desktop_SetStdColour(bg);
	Display_Plot(4, box.x0, box.y0);
	Display_Plot(101, box.x1, box.y1);

	// Plot mark
	if ((WListCore_GetFlags(&This->m_Core) & EWList_ShowMarks)
	&&  (pItem->m_flags & EWItem_Marked))
	{
		const char tick[2] = {128,0};
		const CSpriteFactors scale = {1, 1, 1, 1};
		CPoint pt;
		CSize size;

		size = Desktop_GetNamedSpriteSize(tick);
		pt.x = box.x0 + 4;
		pt.y = box.y0 + ((box.y1 - box.y0 - size.cy) >> 1);

		if (Desktop_PlotNamedSprite(tick, pt, &scale))
		{
			Desktop_SetStdColour(fg);
			Desktop_SetStdColour(0x80 + bg);
			box2 = box;
			box2.x0 += 8;
			box2.x1 = box2.x0 + tick_width;
			Desktop_PlotText(tick, &box2);
		}
	}
	// Plot text
	Desktop_SetStdColour(fg);
	Desktop_SetStdColour(0x80 + bg);
	box2 = box;
	box2.x0 += 8;
	box2.x1 -= 8;
	if (WListCore_GetFlags(&This->m_Core) & EWList_ShowMarks)
		box2.x0 += tick_width;
	Desktop_PlotText(This->m_pGetItemText(This->m_pHandle, pObject), &box2);

	// Plot focus
	if (pItem->m_flags & EWItem_HasFocus)
	{
		Desktop_SetStdColour(fg ? 7 : 0);

		// Set dash-line pattern, as could be changed by PCPro for example
		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 WList_GetItemSize(const void* pObject, const void* pOwner, int* pSizes)
{
	const WList* This = pOwner;
	const char* text = (char*) This->m_pGetItemText(This->m_pHandle, pObject);

	pSizes[0] = Desktop_GetTextWidth(text);
}

static CSize WList_GetItemBox(const void* pObject, const void* pOwner, const int* pSizes, const int* pMaxSizes)
{
	const WList* This = pOwner;
	CSize  size;

	IGNORE(pObject);
	IGNORE(pSizes);

	if (WListCore_GetFlags(&This->m_Core) & EWList_ShowMarks)
		size.cx = tick_width + 16 + pMaxSizes[0];
	else
		size.cx = 16 + pMaxSizes[0];

	size.cy = 40;

	return size;
}

static WListCore_FWList WList_FWList =
{
	{ WList_PlotItem
	, WList_GetItemSize
	, WList_GetItemBox
	, NULL
	, NULL
	}
	, WOwner_PlotBackGround_White
};

void throw_WList_WList(WList* This, unsigned int flags, CTemplate* t0, void* pHandle, FWList_GetItemText pGetItemText)
{
	CTemplate* t = t0;

	memset(This, 0, sizeof(*This));

	if (t == NULL)
		t = throw_Templates_Blank(256, EWind_IconWindow | EWind_Scroll_1, 0, 0);

	This->m_pGetItemText = pGetItemText;
	This->m_pHandle = pHandle;

	throw_WListCore_WListCore(&This->m_Core, flags, t, This, 1, &WList_FWList);
	throw_Window_RegisterEventHandler(WList_GetWindow(This), WListCore_ListEventHandler, &This->m_Core, false);
}

void throw_WList_WListEx(WList* This, unsigned int flags, CTemplate* t0, void* pHandle, WListCore_FWList* pFWList)
{
	CTemplate* t = t0;

	memset(This, 0, sizeof(*This));

	if (t == NULL)
		t = throw_Templates_Blank(256, EWind_IconWindow | EWind_Scroll_1, 0, 0);

	This->m_pHandle = pHandle;

	throw_WListCore_WListCore(&This->m_Core, flags, t, This->m_pHandle, 1, pFWList);
	throw_Window_RegisterEventHandler(WList_GetWindow(This), WListCore_ListEventHandler, &This->m_Core, false);
}

void WList_NotWList(WList* This)
{
	Window_DeRegisterEventHandler(WList_GetWindow(This), WListCore_ListEventHandler, &This->m_Core);
	WListCore_NotWListCore(&This->m_Core);
}
