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

#include "swis.h"

#include "WimpLib:Font.h"
#include "WimpLib:Mem.h"
#include "WimpLib:Task.h"
#include "WimpLib:Utils.h"

void Desktop_SetStdColour(int c)
{
	_swi(Wimp_SetColour, _IN(0), c);
}

static const CSpriteHdr* Desktop_GetUserSprite(const char* pSpriteName)
{
	const CSpriteHdr* pSprite = NULL;

	// User Area, Select sprite
	if (!_swix(OS_SpriteOp, _INR(0,2)|_OUT(2)
			, 0x100 + 24, Task_GetSpriteArea(), pSpriteName
			, &pSprite))
		return pSprite;

	return NULL;
}

CSize Desktop_GetSpriteSize(const CSpriteHdr* pSprite)
{
	const Mode_Info* Mode = Task_GetModeInfo();
	CSpriteFactors   scale;
	CSize size = {0, 0};
	int   x, y;

	// Get wimp scale factors
	if (_swix(Wimp_ReadPixTrans, _INR(0,2)|_IN(6)|_IN(7), 0x200, 0x100, pSprite, &scale, 0))
		return size;

	// Get sprite pixel sizes
	if (_swix(OS_SpriteOp, _INR(0,2)|_OUTR(3,4), 0x200 + 40, 0x100, pSprite, &x, &y))
		return size;

	// Return size in OS coordinates
	size.cx = (Mode->dx * x * scale.xmag) / scale.xdiv;
	size.cy = (Mode->dy * y * scale.ymag) / scale.ydiv;

	return size;
}

CSize Desktop_GetNamedSpriteSize(const char* pSpriteName)
{
	const CSpriteHdr* pSprite = Desktop_GetUserSprite(pSpriteName);
	const Mode_Info*  Mode = Task_GetModeInfo();
	CSpriteFactors    scale;
	CSize size = {0, 0};
	int   x, y;

	if (pSprite) return Desktop_GetSpriteSize(pSprite);

	// Get wimp scale factors
	if (_swix(Wimp_ReadPixTrans, _INR(0,2)|_IN(6)|_IN(7), 0x100, 1, pSpriteName, &scale, 0))
		return size;

	// Get sprite pixel sizes
	if (_swix(Wimp_SpriteOp, _IN(0)|_IN(2)|_OUTR(3,4), 40, pSpriteName, &x, &y))
		return size;

	// Return size in OS coordinates
	size.cx = (Mode->dx * x * scale.xmag) / scale.xdiv;
	size.cy = (Mode->dy * y * scale.ymag) / scale.ydiv;

	return size;
}

const _kernel_oserror* Desktop_PlotSprite(const CSpriteHdr* pSprite, CPoint pt, const CSpriteFactors* pfactors)
{
	const _kernel_oserror* err;
	CSpriteFactors  scale;
	int mode;
	int palette;
	int log2bpp;
	int ptable[256];

	if (!pSprite) return NULL;

	// Get wimp scale factors and translation table from Wimp_ReadPixTrans
	err = _swix(Wimp_ReadPixTrans, _INR(0,2)|_IN(6)|_IN(7), 0x200, 0x100, pSprite, &scale, ptable);
	if (err) return err;

	// Read palette info
	err = _swix(OS_SpriteOp, _INR(0,3)|_OUTR(4,5), 0x200 + 37, 0x100, pSprite, -1, &palette, &mode);
	if (err) return err;

	// Call OS_ReadModeVariable for log2 number of bits per pixel
	err = _swix(OS_ReadModeVariable, _INR(0,1)|_OUT(2), mode, 9, &log2bpp);

	// Has a palette or has more than 16 colours ?
	if (palette
	||  (log2bpp > 2))
	{
		// Get translation table from ColourTrans_SelectTable
		// Do not use bit 4 (0x10), OS_SpriteOp does not know about multibyte colors
		err = _swix(ColourTrans_SelectTable, _INR(0,5), 0x100, pSprite, -1, -1, ptable, 0x01);
	}

	// Update scale factors
	if (pfactors)
	{
		scale.xmag *= pfactors->xmag;
		scale.ymag *= pfactors->ymag;
		scale.xdiv *= pfactors->xdiv;
		scale.ydiv *= pfactors->ydiv;
	}

	// Plot sprite
	err = _swix(OS_SpriteOp, _INR(0,7), 0x200 + 52, 0x100, pSprite, pt.x, pt.y, 0x18, &scale, ptable);

	return err;
}

const _kernel_oserror* Desktop_PlotNamedSprite(const char* pSpriteName, CPoint pt, const CSpriteFactors* pfactors)
{
	const CSpriteHdr* pSprite = Desktop_GetUserSprite(pSpriteName);
	const _kernel_oserror* err;
	CSpriteFactors scale;
	int mode;
	int palette;
	int log2bpp;
	int ptable[256];

	if (pSprite)
		return Desktop_PlotSprite(pSprite, pt, pfactors);

	// Get wimp scale factors and translation table from Wimp_ReadPixTrans
	err = _swix(Wimp_ReadPixTrans, _INR(0,2)|_IN(6)|_IN(7), 0x100, 1, pSpriteName, &scale, ptable);
	if (err) return err;

	// Read palette info
	err = _swix(Wimp_SpriteOp, _IN(0)|_INR(2,3)|_OUTR(4,5), 0x100 + 37, pSpriteName, -1, &palette, &mode);
	if (err) return err;

	// Call OS_ReadModeVariable for log2 number of bits per pixel
	err = _swix(OS_ReadModeVariable, _INR(0,1)|_OUT(2), mode, 9, &log2bpp);

	// Has a palette or has more than 16 colours ?
	if (palette
	||  (log2bpp > 2))
	{
		int rma, rom;

		if (!_swix(Wimp_BaseOfSprites, _OUTR(0,1), &rom, &rma))
		{
			// Get translation table from ColourTrans_SelectTable
			// Do not use bit 4 (0x10), OS_SpriteOp does not know about multibyte colors
			if (_swix(ColourTrans_SelectTable, _INR(0,5), rma, pSpriteName, -1, -1, ptable, 0x00))
			{
				err = _swix(ColourTrans_SelectTable, _INR(0,5), rom, pSpriteName, -1, -1, ptable, 0x00);
			}
		}
	}

	// Update scale factors
	if (pfactors)
	{
		scale.xmag *= pfactors->xmag;
		scale.ymag *= pfactors->ymag;
		scale.xdiv *= pfactors->xdiv;
		scale.ydiv *= pfactors->ydiv;
	}

	// Plot sprite
	err = _swix(Wimp_SpriteOp, _IN(0)|_INR(2,7),  52, pSpriteName, pt.x, pt.y, 0x18, &scale, ptable);

	return err;
}

int Desktop_GetTextWidth(const char* ptext)
{
	int len, f;

	if (ptext == NULL) return 0;

	if (_swix(Wimp_ReadSysInfo, _IN(0)|_OUT(0), 8, &f))
		f = 0;

	if (!f) return 16 * strlen(ptext);

	// Wimp_TextOp 1: get full string width (Select >= 2)
	if (_swix(Wimp_TextOp, _INR(0,2)|_OUT(0), 1 + (int)0x80000000, ptext, 0, &len))
	{
		// Wimp_TextOp 1: get string width
		_swix(Wimp_TextOp, _INR(0,2)|_OUT(0), 1, ptext, 0, &len);
	}

	return len + 4;
}

void Desktop_PlotText(const char* preftext, const CRect* prct)
{
	int x, y, miny, maxy, len, newlen;
	CRect rect = *prct;
	char* ptext;

	if (preftext == NULL) return;

	rect.x1 += 1;
	RoundUpRect(&rect);

	if (_swix(Wimp_ReadSysInfo, _IN(0)|_OUT(0), 8, &x))
		x = 0;

	ptext = (char*) preftext;
	len = strlen(ptext);
	newlen = Font_GetSplitPosition(x, ptext, rect.x1 - rect.x0);
	if (newlen < len)
	{
		ptext = SPrintf(ptext);

		do
		{
			ptext[newlen] = 0;
			len = newlen;
			if (len > 0) ptext[len - 1] = '';
			newlen = Font_GetSplitPosition(x, ptext, rect.x1 - rect.x0);
		}
		while (len && (newlen < len));
	}

	// Wimp_ReadSysInfo,8 to get desktop font
	if (!x)
	{
		// OS_Plot 4: move cursor absolute
		_swix(OS_Plot, _INR(0,2), 4, rect.x0, rect.y1 - (rect.y1 - rect.y0 - 32)/2);

		// OS_WriteN
		_swix(OS_WriteN, _INR(0,1), ptext, len);
	}
	else
	{
		_swix(Font_ReadInfo, _IN(0)|_OUT(2)|_OUT(4), x, &miny, &maxy);
		y = (rect.y1 - rect.y0 - maxy + miny)/2;
		y += rect.y0 - miny;
		// Wimp_TextOp 2: plot string
		_swix(Wimp_TextOp, _INR(0,5), 2, ptext, -1, -1, rect.x0, y);
	}
}

void Desktop_RightPlotText(const char* preftext, const CRect* prct)
{
	int x, y, miny, maxy, len, newlen;
	CRect rect = *prct;
	char* ptext;

	if (preftext == NULL) return;

	rect.x1 += 1;
	RoundUpRect(&rect);

	if (_swix(Wimp_ReadSysInfo, _IN(0)|_OUT(0), 8, &x))
		x = 0;

	ptext = (char*) preftext;
	len = strlen(ptext);
	newlen = Font_GetSplitPosition(x, ptext, rect.x1 - rect.x0);
	if (newlen < len)
	{
		ptext = SPrintf(ptext);

		do
		{
			ptext[newlen] = 0;
			len = newlen;
			if (len > 0) ptext[len - 1] = '';
			newlen = Font_GetSplitPosition(x, ptext, rect.x1 - rect.x0);
		}
		while (len && (newlen < len));
	}

	// Wimp_ReadSysInfo,8 to get desktop font
	if (!x)
	{
		newlen = Desktop_GetTextWidth(ptext);
		// OS_Plot 4: move cursor absolute
		_swix(OS_Plot, _INR(0,2), 4, rect.x1 - newlen, rect.y1 - (rect.y1 - rect.y0 - 32)/2);

		// OS_WriteN
		_swix(OS_WriteN, _INR(0,1), ptext, len);
	}
	else
	{
		_swix(Font_ReadInfo, _IN(0)|_OUT(2)|_OUT(4), x, &miny, &maxy);
		y = (rect.y1 - rect.y0 - maxy + miny)/2;
		y += rect.y0 - miny;
		// Wimp_TextOp 2 + right flag: plot string
		_swix(Wimp_TextOp, _INR(0,5), 0x80000002, ptext, -1, -1, rect.x1, y);
	}
}

static const MouseShape* CurrentShape = NULL;

const MouseShape* Desktop_GetPointer(void)
{
	return CurrentShape;
}

void Desktop_SetPointer(const MouseShape* shape)
{
	// Try a user sprite
	if (_swix(OS_SpriteOp, _INR(0,7)
	         , 0x100 + 36, Task_GetSpriteArea()
	         , shape->name, 2, shape->x, shape->y, 0, 0))
	{
		// Try a system sprite
		_swi(Wimp_SpriteOp, _IN(0)|_INR(2,7), 36
		     , shape->name, 2, shape->x, shape->y, 0, 0);
	}

	CurrentShape = shape;
}

void Desktop_ResetPointer(void)
{
	static const char* shape = "ptr_default";

	_swi(Wimp_SpriteOp, _IN(0)|_INR(2,7), 36, shape, 1, 0, 0, 0, 0);

	CurrentShape = NULL;
}
