/*
*Copyright(c)2018, Jeffrey Lee
*Allrightsreserved.
*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met: 
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <kernel.h>
#include <swis.h>
#include <string.h>

typedef struct {
	int b[64];
	_kernel_swi_regs r;
	int task;
	void *mem; // Pointer to most recent memory block
	int memsize;
} limp_obj;

limp_obj limp;

// Plain WIMP SWI interface...

void Limp_Initialise(char *name)
{
	int b;
	_kernel_oserror *err;
	b=0;
	limp.r.r[0]=310;
	limp.r.r[1]=0x4B534154;
	limp.r.r[2]=(int) name;
	limp.r.r[3]=(int) &b; // Pointer to 0 means accept all messages
	err = _kernel_swi(Wimp_Initialise,&limp.r,&limp.r);
	if (err)
	{
		_kernel_raise_error(err);
		exit(1);
	}
	limp.task=limp.r.r[1];
	limp.mem=NULL;
	limp.memsize=0;
}

void Limp_OpenTemplate(char *name)
{
	limp.r.r[1]=(int) name;
	_kernel_swi(Wimp_OpenTemplate,&limp.r,&limp.r);
}

int Limp_LoadTemplate(char *name,void *fonts)
{
	char *buff;
	int size;
	char n[12];
	strcpy(n,name);
	limp.r.r[1]=0;
	limp.r.r[4]=(int) fonts;
	limp.r.r[5]=(int) n;
	limp.r.r[6]=0;
	_kernel_swi(Wimp_LoadTemplate,&limp.r,&limp.r);
	limp.mem=(char *) malloc(limp.r.r[2]);
	limp.memsize=limp.r.r[2];
	size=limp.r.r[2];
	buff=(char *) malloc(limp.r.r[1]);
	strcpy(n,name);
	limp.r.r[1]=(int) buff;
	limp.r.r[2]=(int) limp.mem;
	limp.r.r[3]=limp.r.r[2]+size;
	limp.r.r[5]=(int) n;
	limp.r.r[6]=0;
	_kernel_swi(Wimp_LoadTemplate,&limp.r,&limp.r);
	limp.r.r[1]=(int) buff;
	_kernel_swi(Wimp_CreateWindow,&limp.r,&limp.r);
	free(buff);
	return limp.r.r[0];
}

void Limp_CloseTemplate()
{
	_kernel_swi(Wimp_CloseTemplate,&limp.r,&limp.r);
}

int Limp_CreateWindow(void *block)
{
	limp.r.r[1]=(int) block;
	_kernel_swi(Wimp_CreateWindow,&limp.r,&limp.r);
	return limp.r.r[0];
}

int Limp_CreateIcon(int r0,void *block)
{
	limp.r.r[0]=r0;
	limp.r.r[1]=(int) block;
	_kernel_swi(Wimp_CreateIcon,&limp.r,&limp.r);
	return limp.r.r[0];
}

void Limp_DeleteWindow(int h)
{
	limp.b[0]=h;
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_DeleteWindow,&limp.r,&limp.r);
}

void Limp_DeleteIcon(int w,int h)
{
	limp.b[0]=w;
	limp.b[1]=h;
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_DeleteIcon,&limp.r,&limp.r);
}

void Limp_OpenWindow(int h,int vmin_x,int vmin_y,int vmax_x,int vmax_y,int sx,int sy,int pos)
{
	limp.b[0]=h;
	limp.b[1]=vmin_x;
	limp.b[2]=vmin_y;
	limp.b[3]=vmax_x;
	limp.b[4]=vmax_y;
	limp.b[5]=sx;
	limp.b[6]=sy;
	limp.b[7]=pos;
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_OpenWindow,&limp.r,&limp.r);
}

void Limp_CloseWindow(int h)
{
	limp.b[0]=h;
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_CloseWindow,&limp.r,&limp.r);
}

int Limp_Poll(int mask,void *w)
{
	limp.r.r[0]=mask;
	limp.r.r[1]=(int) limp.b;
	limp.r.r[3]=(int) w;
	_kernel_swi(Wimp_Poll,&limp.r,&limp.r);
	return limp.r.r[0];
}

int Limp_RedrawWindow(int h)
{
	limp.b[0]=h;
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_RedrawWindow,&limp.r,&limp.r);
	return limp.r.r[0];
}

int Limp_UpdateWindow(int h,int minx,int miny,int maxx,int maxy)
{
	limp.b[0]=h;
	limp.b[1]=minx;
	limp.b[2]=miny;
	limp.b[3]=maxx;
	limp.b[4]=maxy;
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_UpdateWindow,&limp.r,&limp.r);
	return limp.r.r[0];
}

int Limp_GetRectangle()
{
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_GetRectangle,&limp.r,&limp.r);
	return limp.r.r[0];
}

void Limp_GetWindowState(int h)
{
	limp.b[0]=h;
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_GetWindowState,&limp.r,&limp.r);
}

void Limp_GetWindowInfo(int f,void *b,int h)
{
	limp.r.r[1]=((int) b) | f;
	*((int *) b)=h;
	_kernel_swi(Wimp_GetWindowInfo,&limp.r,&limp.r);
}

void Limp_SetIconState(int w,int h,int e,int c)
{
	limp.b[0]=w;
	limp.b[1]=h;
	limp.b[2]=e;
	limp.b[3]=c;
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_SetIconState,&limp.r,&limp.r);
}

int *Limp_GetIconState(int w,int h)
{
	limp.b[0]=w;
	limp.b[1]=h;
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_GetIconState,&limp.r,&limp.r);
	return &limp.b[2];
}

void Limp_GetPointerInfo()
{
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_GetPointerInfo,&limp.r,&limp.r);
}

void Limp_DragBox(void *b)
{
	limp.r.r[1]=(int) b;
	_kernel_swi(Wimp_DragBox,&limp.r,&limp.r);
}

void Limp_ForceRedraw(int w,int minx,int miny,int maxx,int maxy)
{
	limp.r.r[0]=w;
	limp.r.r[1]=minx;
	limp.r.r[2]=miny;
	limp.r.r[3]=maxx;
	limp.r.r[4]=maxy;
	_kernel_swi(Wimp_ForceRedraw,&limp.r,&limp.r);
}

void Limp_SetCaretPosition(int w,int i,int x,int y,int h,int p)
{
	limp.r.r[0]=w;
	limp.r.r[1]=i;
	limp.r.r[2]=x;
	limp.r.r[3]=y;
	limp.r.r[4]=h;
	limp.r.r[5]=p;
	_kernel_swi(Wimp_SetCaretPosition,&limp.r,&limp.r);
}

void Limp_GetCaretPosition()
{
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_GetCaretPosition,&limp.r,&limp.r);
}

void Limp_CreateMenu(void *b,int x,int y)
{
	limp.r.r[1]=(int) b;
	limp.r.r[2]=x;
	limp.r.r[3]=y;
	_kernel_swi(Wimp_CreateMenu,&limp.r,&limp.r);
}

void Limp_DecodeMenu(void *m,void *l,void *b)
{
	limp.r.r[1]=(int) m;
	limp.r.r[2]=(int) l;
	limp.r.r[3]=(int) b;
	_kernel_swi(Wimp_DecodeMenu,&limp.r,&limp.r);
}

void Limp_WhichIcon(int w,void *b,int m,int f)
{
	limp.r.r[0]=w;
	limp.r.r[1]=(int) b;
	limp.r.r[2]=m;
	limp.r.r[3]=f;
	_kernel_swi(Wimp_WhichIcon,&limp.r,&limp.r);
}

void Limp_SetExtent(int h,int minx,int miny,int maxx,int maxy)
{
	limp.r.r[0]=h;
	limp.r.r[1]=(int) limp.b;
	limp.b[0]=minx;
	limp.b[1]=miny;
	limp.b[2]=maxx;
	limp.b[3]=maxy;
	_kernel_swi(Wimp_SetExtent,&limp.r,&limp.r);
}

void Limp_SetPointerShape(int n,void *d,int w,int h,int x,int y)
{
	limp.r.r[0]=n;
	limp.r.r[1]=(int) d;
	limp.r.r[2]=w;
	limp.r.r[3]=h;
	limp.r.r[4]=x;
	limp.r.r[5]=y;
	_kernel_swi(Wimp_SetPointerShape,&limp.r,&limp.r);
}

void Limp_ProcessKey(int c)
{
	limp.r.r[0]=c;
	_kernel_swi(Wimp_ProcessKey,&limp.r,&limp.r);
}

void Limp_CloseDown()
{
	limp.r.r[0]=limp.task;
	limp.r.r[1]=0x4B534154;
	_kernel_swi(Wimp_CloseDown,&limp.r,&limp.r);
}

void Limp_StartTask(char *cmd)
{
	limp.r.r[0]=(int) cmd;
	_kernel_swi(Wimp_StartTask,&limp.r,&limp.r);
}

void Limp_ReportError(int num,char *msg,int flags,char *title)
{
	union {char c[1024];int i[256];} e;
	e.i[0]=num;
	strcpy(&e.c[4],msg);
	limp.r.r[0]=(int) &e;
	limp.r.r[1]=flags;
	limp.r.r[2]=(int) title;
	_kernel_swi(Wimp_ReportError,&limp.r,&limp.r);
}

void Limp_GetWindowOutline(int w)
{
	limp.b[0]=w;
	limp.r.r[1]=(int) limp.b;
	_kernel_swi(Wimp_GetWindowOutline,&limp.r,&limp.r);
}

void Limp_PollIdle(int m,int t,void *b)
{
	limp.r.r[0]=m;
	limp.r.r[1]=(int) limp.b;
	limp.r.r[2]=t;
	limp.r.r[3]=(int) b;
	_kernel_swi(Wimp_PollIdle,&limp.r,&limp.r);
}

void Limp_PlotIcon(void *b)
{
	limp.r.r[1]=(int) b;
	_kernel_swi(Wimp_PlotIcon,&limp.r,&limp.r);
}

void Limp_SetMode(int m)
{
	limp.r.r[0]=m;
	_kernel_swi(Wimp_SetMode,&limp.r,&limp.r);
}

void Limp_SetPalette(void *b)
{
	limp.r.r[1]=(int) b;
	_kernel_swi(Wimp_SetPalette,&limp.r,&limp.r);
}

void Limp_ReadPalette(void *b)
{
	limp.r.r[1]=(int) b;
	_kernel_swi(Wimp_ReadPalette,&limp.r,&limp.r);
}

void Limp_SetColour(int c)
{
	limp.r.r[0]=c;
	_kernel_swi(Wimp_SetColour,&limp.r,&limp.r);
}

void Limp_SendMessage(int code,void *b,int h,int i)
{
	limp.r.r[0]=code;
	limp.r.r[1]=(int) b;
	limp.r.r[2]=h;
	limp.r.r[3]=i;
	_kernel_swi(Wimp_SendMessage,&limp.r,&limp.r);
}

void Limp_CreateSubMenu(void *b,int x,int y)
{
	limp.r.r[1]=(int) b;
	limp.r.r[2]=x;
	limp.r.r[3]=y;
	_kernel_swi(Wimp_CreateSubMenu,&limp.r,&limp.r);
}

void Limp_SpriteOp(int code,char *name)
{
	limp.r.r[0]=code;
	limp.r.r[2]=(int) name;
	_kernel_swi(Wimp_SpriteOp,&limp.r,&limp.r);
}

int Limp_ROMBaseOfSprites()
{
	_kernel_swi(Wimp_BaseOfSprites,&limp.r,&limp.r);
	return limp.r.r[0];
}

int Limp_RMABaseOfSprites()
{
	_kernel_swi(Wimp_BaseOfSprites,&limp.r,&limp.r);
	return limp.r.r[1];
}

void Limp_BlockCopy(int h,int minx,int miny,int maxx,int maxy,int dx,int dy)
{
	limp.r.r[0]=h;
	limp.r.r[1]=minx;
	limp.r.r[2]=miny;
	limp.r.r[3]=maxx;
	limp.r.r[4]=maxy;
	limp.r.r[5]=dx;
	limp.r.r[6]=dy;
	_kernel_swi(Wimp_BlockCopy,&limp.r,&limp.r);
}

void Limp_SlotSize(int cur,int next)
{
	limp.r.r[0]=cur;
	limp.r.r[1]=next;
	_kernel_swi(Wimp_SlotSize,&limp.r,&limp.r);
}

void Limp_ReadPixTrans(int r0,int r1,void *r2,void *r6,void *r7)
{
	limp.r.r[0]=r0;
	limp.r.r[1]=r1;
	limp.r.r[2]=(int) r2;
	limp.r.r[6]=(int) r6;
	limp.r.r[7]=(int) r7;
	_kernel_swi(Wimp_ReadPixTrans,&limp.r,&limp.r);
}

void Limp_ClaimFreeMemory(int o,int a)
{
	limp.r.r[0]=o;
	limp.r.r[1]=a;
	_kernel_swi(Wimp_ClaimFreeMemory,&limp.r,&limp.r);
}

void Limp_CommandWindow(int o)
{
	limp.r.r[0]=o;
	_kernel_swi(Wimp_CommandWindow,&limp.r,&limp.r);
}

void Limp_TextColour(int c)
{
	limp.r.r[0]=c;
	_kernel_swi(Wimp_TextColour,&limp.r,&limp.r);
}

void Limp_TransferBlock(int sh,void *sb,int dh,void *db,int l)
{
	limp.r.r[0]=sh;
	limp.r.r[1]=(int) sb;
	limp.r.r[2]=dh;
	limp.r.r[3]=(int) db;
	limp.r.r[4]=l;
	_kernel_swi(Wimp_TransferBlock,&limp.r,&limp.r);
}

int Limp_ReadSysInfo(int i)
{
	limp.r.r[0]=i;
	_kernel_swi(Wimp_ReadSysInfo,&limp.r,&limp.r);
	return limp.r.r[0];
}

void Limp_SetFontColours(int b,int f)
{
	limp.r.r[1]=b;
	limp.r.r[2]=f;
	_kernel_swi(Wimp_SetFontColours,&limp.r,&limp.r);
}

void Limp_GetMenuState(int f,void *b,int w,int i)
{
	limp.r.r[0]=f;
	limp.r.r[1]=(int) b;
	limp.r.r[2]=w;
	limp.r.r[3]=i;
	_kernel_swi(Wimp_GetMenuState,&limp.r,&limp.r);
}

void Limp_RegisterFilter(int c,int a,int r)
{
	limp.r.r[0]=c;
	limp.r.r[1]=a;
	limp.r.r[2]=r;
	_kernel_swi(Wimp_RegisterFilter,&limp.r,&limp.r);
}

void Limp_AddMessages(void *b)
{
	limp.r.r[0]=(int) b;
	_kernel_swi(Wimp_AddMessages,&limp.r,&limp.r);
}

void Limp_RemoveMessages(void *b)
{
	limp.r.r[0]=(int) b;
	_kernel_swi(Wimp_RemoveMessages,&limp.r,&limp.r);
}

void Limp_SetColourMapping(void *p,void *a,void *b,void *c)
{
	limp.r.r[1]=(int) p;
	limp.r.r[2]=(int) a;
	limp.r.r[3]=(int) b;
	limp.r.r[4]=(int) c;
	limp.r.r[5]=limp.r.r[6]=limp.r.r[7]=0;
	_kernel_swi(Wimp_SetColourMapping,&limp.r,&limp.r);
}

// Some more handy functions

void Limp_SetIconText(int w,int h,char *t)
{
	Limp_GetCaretPosition();
	if ((limp.r.r[0]=w) && (limp.r.r[1]=h) && (limp.r.r[5] < strlen(t)))
		Limp_SetCaretPosition(w,h,limp.r.r[2],limp.r.r[3],limp.r.r[4],strlen(t)); // Make sure caret is inside
	Limp_GetIconState(w,h); // Icon block starts at limp.b[2]
	if (limp.b[6] & 256) // Indirected?
	{
		strcpy((char *) limp.b[7],t);
		Limp_SetIconState(w,h,0,0); // Redraw the icon
	}
	else
	{
		strcpy((char *) &limp.b[7],t);
		limp.b[1] = limp.b[0]; // Copy window handle
		Limp_CreateIcon(h,&limp.b[1]); // Will this work with the iconbar?
	}
}

char *Limp_GetIconText(int w,int h)
{
	Limp_GetIconState(w,h);
	if (limp.b[6] & 256)
		return (char *) limp.b[7];
	return (char *) &limp.b[7];
}

void Limp_SelectESGIcon(int w,int esg,int h,int c)
{
	int i;
	int b[c+1];
	Limp_WhichIcon(w,(void *) b,0x001F0000,esg*0x10000);
	// Now deselect all...
	i=0;
	while (b[i] != -1)
	{
		Limp_SetIconState(w,b[i],0,0x200000);
		i++;
	}
	Limp_SetIconState(w,h,0x200000,0x200000);
}

int Limp_GetSelectedESGIcon(int w,int esg)
{
	Limp_WhichIcon(w,(void *) limp.b,0x003F0000,(esg*0x10000)+0x200000);
	return limp.b[0];
}


char *Limp_GetIconValidation(int w,int h)
{
	Limp_GetIconState(w,h);
	if ((limp.b[6] & 257) == 257)
		return (char *) limp.b[8];
	return "";
}

void Limp_SetIconValidation(int w,int h,char *v)
{
	Limp_GetIconState(w,h);
	if ((limp.b[6] & 257) == 257)
		strcpy((char *) limp.b[8],v);
}

char *Limp_GetIconSprite(int w,int h)
{
	char *s;
	int p,pp;
	char *n;
	Limp_GetIconState(w,h);
	if ((limp.b[6] & 3) < 2)
		return "";
	if ((limp.b[6] & 256) == 0)
		return (char *) &limp.b[7]; // Watch for corruption!
	if ((limp.b[6] & 1) == 0)
	{
		if (limp.b[9] != 0)
			return (char *) limp.b[7];
	}
	else
	{
		n=(char *) malloc(13);
		// Get from validation string...
		s = (char *) limp.b[8];
		p = 0;
		while ((s[p] != 'S') && (s[p] < limp.b[9]))
		{
			do
			{
				if (s[p] == '/')
					p++; // Skip following char
				p++; // Skip normally
			}
			while (s[p] != ';');
			p++;
		}
		if (s[p] == 'S')
		{
			p++;
			pp = 0;
			do
			{
				n[pp]=s[p];
				p++;
				pp++;
			}
			while ((s[p] != ';') && (s[p] >= 32));
			n[pp]=0;
			return n;
		}
	}
	return "";
}
