#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include "kernel.h"
#include "swis.h"

#define Buggy "<Obey$Dir>.Buggy"
#include "Buggy.h" // my debugging code
#include "Wimpy.h" // some Wimp definitions

// Some global variables so I need not pass them on over and over again
// ... or create them over and over again for subroutine calls
_kernel_swi_regs SWIRegs;
//int WimpPollMask = PollMask_Standard;
int WimpPollMask = PollMask_Null;
int WimpPollWait = 1; // ASAP
t_WimpPollBlock WimpPollBlockStorage;
t_WimpPollBlock *WimpPollBlock;
t_TaskHandle MyTaskHandle = 0;
t_MessageTrans_FileDescriptor *MyMessages;
t_SpriteArea *MySprites;
t_Window *MyWindow;
t_WindowHandle MyWindowHandle = -1;
char MyStatus[200];

const char MyTaskName[]    = "TalkCmd";
const char MyVersion[]     = "1.00";


// Function for SWI Wimp_CloseDown
void WimpCloseDown(void) {
	// Call Wimp_CloseDown if I have a TaskHandle
	if (MyTaskHandle != 0) {
		SWIRegs.r[0] = TASK;
		SWIRegs.r[1] = (int)MyTaskHandle;
		_kernel_swi(Wimp_CloseDown,&SWIRegs,&SWIRegs);
	}
	// Exit
	_kernel_swi(OS_Exit,&SWIRegs,&SWIRegs);
	exit(0);
}

#define SWI(swi)  SWIcall(swi,True,#swi,__FILE__,__LINE__)
#define SWIX(swi) SWIcall(swi,False,#swi,__FILE__,__LINE__)
#define SWIE(swi) SWIcall(-1,True,#swi,__FILE__,__LINE__)

// Call an SWI and abort in case of error...
// Call with SWI NULL to just abort
_kernel_oserror *SWIcall(int SWIName, int AbortOnError, char *AbortMessage, char *File, int Line) {
	_kernel_oserror *OSError;

	//BuggyRegs(AbortMessage,4);

	// Call the SWI (if I have one)
	if (SWIName != -1) {
		OSError = _kernel_swi(SWIName,&SWIRegs,&SWIRegs);
		if (OSError == NULL) { return(OSError); }
	}

	// In case of error write debug log and optionally abort
	BuggyError(AbortMessage,File,Line);
	if (AbortOnError) {
		SWIRegs.r[0] = (int)OSError;
		SWIRegs.r[1] = 1;
		SWIRegs.r[2] = (int)AbortMessage;
		_kernel_swi(Wimp_ReportError,&SWIRegs,&SWIRegs);
		WimpCloseDown();
	}

	return(OSError);
}


// StringLength with delim 00, 0a and 0d
int StringLength(char *s) {
	int l = 0;
	for (;; s++) {
		if ((*s == (char)0) || (*s == (char)10) || (*s == (char)13)) { return l; }
		l++;
	}
}

// Change end of string
void StringToC(char *s) {
	for (;; s++) {
		if ((*s == 0) || (*s == 10) || (*s == 13)) { *s = 0; break; }
	}
}

// Case insensitive string compare
int StringCompare(char *a, char *b) {
	int d;
	for (;; a++, b++) {
		d = tolower(*a) - tolower(*b);
		if ((d != 0) || (*a == (char)0) || (*b == (char)0)) { return d; }
	}
}
int StringMatchStar(char *s, char *p) {
	int d;
	if (p == NULL) { return 0; }
	for (;; s++, p++) {
		if (*p == '*') { return 0; }
		d = tolower(*s) - tolower(*p);
		if ((d != 0) || (*s == 0) || (*p == 0)) { return d; }
	}
}


void MessagesLoad(char *FileName) {
	char *MessagesBuffer;
	char *s;
	int MessageBufferLength;
	SWIRegs.r[1] = (int)FileName;
	// Get file info; return NULL if not found
	if (SWIX(MessageTrans_FileInfo) != NULL) { return; }
	// Allocate the space we need to load the file
	MessageBufferLength = SWIRegs.r[2];
	MyMessages = malloc(sizeof(t_MessageTrans_FileDescriptor)+MessageBufferLength);
	if(MyMessages == NULL) { SWIE("MessageLoad malloc"); }
	MessagesBuffer = (char*)(MyMessages + 1); // sizeof(t_MessageTrans_FileDescriptor);

	SWIRegs.r[0] = (int)MyMessages;
	SWIRegs.r[1] = (int)FileName;
	SWIRegs.r[2] = (int)MessagesBuffer;
	SWI(MessageTrans_OpenFile);
	// Make stuff C friendly so that strings are strings (but skip first 4 bytes)!
	MessagesBuffer += 4; MessageBufferLength -= 4;
	for (s = MessagesBuffer;MessageBufferLength > 0; s++, MessageBufferLength--) {
		if (*s == (char)10) { *s = (char)0; }
	}
}

void MessagesClose(void) {
	if (MyMessages != NULL) {
		SWIRegs.r[0] = (int)MyMessages;
		SWI(MessageTrans_CloseFile);
		free(MyMessages);
		MyMessages = NULL;

	}
}

char *MessagesLookup(char *Token) {

	if (MyMessages != NULL) {
		// Initial version does not support variables
		SWIRegs.r[0] = (int)MyMessages;
		SWIRegs.r[1] = (int)(Token+1);
		SWIRegs.r[2] = 0; // no buffer for result
		SWIRegs.r[3] = 0; // length not important
		SWIRegs.r[4] = 0; // %0
		SWIRegs.r[5] = 0; // %1
		SWIRegs.r[6] = 0; // %2
		SWIRegs.r[7] = 0; // %3
		if (SWIX(MessageTrans_Lookup) == NULL) { return((char*)SWIRegs.r[2]); }
		else                                   { return(Token);               }

	}
	else {
		return(Token);
	}

}

char *MessagesTrans(char *Token, char *P0, char *P1, char *P2, char *P3) {
	int l;
	char *Buffer;

	// Get space I need
	l = strlen(MessagesLookup(Token));
	if (P0 != NULL) l += strlen(P0);
	if (P1 != NULL) l += strlen(P1);
	if (P2 != NULL) l += strlen(P2);
	if (P3 != NULL) l += strlen(P3);

	Buffer = malloc(l+1); // add one for terminating zero byte
	if(Buffer == NULL) { SWIE(#"MessagesTrans malloc#"); }

	if (MyMessages != NULL) {
		SWIRegs.r[0] = (int)MyMessages;
		SWIRegs.r[1] = (int)(Token+1);
		SWIRegs.r[2] = (int)Buffer;
		SWIRegs.r[3] = l;
		SWIRegs.r[4] = (int)P0;
		SWIRegs.r[5] = (int)P1;
		SWIRegs.r[6] = (int)P2;
		SWIRegs.r[7] = (int)P3;
		if (SWIX(MessageTrans_Lookup) != NULL) { strcpy(Buffer,Token);        }
	}
	else {
		strcpy(Buffer,Token);
	}

	return(Buffer);
}

char *MessagesFill(char *Token, char *P0, char *P1, char *P2, char *P3, char *Buffer, int BufferLen) {

	if (MyMessages != NULL) {
		SWIRegs.r[0] = (int)MyMessages;
		SWIRegs.r[1] = (int)Token;
		SWIRegs.r[2] = (int)Buffer;
		SWIRegs.r[3] = BufferLen;
		SWIRegs.r[4] = (int)P0;
		SWIRegs.r[5] = (int)P1;
		SWIRegs.r[6] = (int)P2;
		SWIRegs.r[7] = (int)P3;
		if (SWIX(MessageTrans_Lookup) != NULL) { strcpy(Buffer,Token); }
	}
	else {
		strcpy(Buffer,Token);
	}
	return(Buffer);
}



char *TXT(char *Text) {
	if (*Text == '>') { return(MessagesLookup(Text)); }
	else              { return(Text);                 }
}


void ErrorBox(char *Message) {
	t_OSError Err;
	Err.Number = 0;
	strncpy(Err.Message,TXT(Message),sizeof(Err.Message));

	SWIRegs.r[0] = (int)&Err;
	SWIRegs.r[1] = 1;
	SWIRegs.r[2] = (int)&MyTaskName;
	SWIX(Wimp_ReportError);
}

// Function for SWI Wimp_Initalise
void WimpInitialise(void) {
	SWIRegs.r[0] = 310;
	SWIRegs.r[1] = TASK;
	SWIRegs.r[2] = (int)&MyTaskName;
	SWIRegs.r[3] = (int)NULL;
	SWI(Wimp_Initialise);
	MyTaskHandle = SWIRegs.r[1];
}

// I like more or less messages
void WimpAddRemoveMessages(int MessageCode) {
	t_WimpAddRemoveMessages Block;
	SWIRegs.r[0] = (int)(&Block);
	Block.Delimiter = 0;
	if (MessageCode < 0) {
		Block.MessageCode = -MessageCode;
		SWI(Wimp_RemoveMessages);
	}
	else {
		Block.MessageCode = MessageCode;
		SWI(Wimp_AddMessages);
	}
}

// Send a reply - the original message being in the WimpPollBlock!
void WimpMessageReply(int ReasonCode, int MessageCode) {
	WimpPollBlock->UserMessage.MessageCode = MessageCode;
	WimpPollBlock->UserMessage.YourRef = WimpPollBlock->UserMessage.MyRef;
	SWIRegs.r[0] = ReasonCode;
	SWIRegs.r[1] = (int)(WimpPollBlock);
	SWIRegs.r[2] = WimpPollBlock->UserMessage.SenderHandle;
	SWI(Wimp_SendMessage);
}

// Function for SWI Wimp_Poll
int WimpPoll(void) {
	SWI(OS_ReadMonotonicTime);
	SWIRegs.r[2] = SWIRegs.r[0] + WimpPollWait;
	SWIRegs.r[0] = WimpPollMask;
	SWIRegs.r[1] = (int)&WimpPollBlockStorage;
	SWI(Wimp_PollIdle);
	// I would expect that the block I pass to Wimp_Poll is the one filled but the PRMs do not guarantee that!
	WimpPollBlock = (t_WimpPollBlock *)SWIRegs.r[1];
	return(SWIRegs.r[0]);
}

// Function to check if a key is pressed
int KeyPressCheck(int Key) {
	SWIRegs.r[0] = 129;
	SWIRegs.r[1] = Key ^ 0xff;
	SWIRegs.r[2] = 0xff;
	SWI(OS_Byte);
	return((SWIRegs.r[1] == 0xff));
}

// Function to check mouse buttons
int MouseButtonCheck(int Button) {
	SWIRegs.r[0] = (int)&WimpPollBlockStorage;
	SWI(Wimp_GetPointerInfo);
	return((WimpPollBlockStorage.MouseClick.Buttons == Button));
}

// Wimp create window
t_WindowHandle WimpCreateWindow(t_Window *Window) {
	SWIRegs.r[1] = (int)Window;
	SWI(Wimp_CreateWindow);
	return((t_WindowHandle)SWIRegs.r[0]);
}


// Create a basic window
t_Window *CreateBasicWindow(char *Title, unsigned Flags, int Icons, int MaxX, int MaxY) {

	 t_WindowHeader *WindowHeader;

	WindowHeader = malloc(sizeof(t_WindowHeader) + Icons * sizeof(t_Icon));
	if(WindowHeader == NULL) { SWIE("Window malloc"); }

	WindowHeader->Visible.MinX = 0;
	WindowHeader->Visible.MinY = 0;
	WindowHeader->Visible.MaxX = MaxX;
	WindowHeader->Visible.MaxY = MaxY;
	WindowHeader->ScrollX = 0;
	WindowHeader->ScrollY = 0;
	WindowHeader->WindowStack = Window_OpenInFront;
	WindowHeader->WindowFlags = Flags;
	WindowHeader->TitleForground = 7;
	WindowHeader->TitleBackground = 2;
	WindowHeader->WorkareaForground = 7;
	WindowHeader->WorkareaBackground = 1;
	WindowHeader->ScrollbarBackground = 3;
	WindowHeader->ScrollbarForeground = 1;
	WindowHeader->TitleFocusBackground = 12;
	WindowHeader->ExtraFlags = 0;
	WindowHeader->Workarea.MinX = 0;
	WindowHeader->Workarea.MinY = -MaxY;
	WindowHeader->Workarea.MaxX = MaxX;
	WindowHeader->Workarea.MaxY = 0;
	WindowHeader->TitleIconFlags = IconFlags_WindowTitle;
	WindowHeader->WorkareaButtonType = ButtonType_MenuOnly<<IconFlags_Shift_Button;
	WindowHeader->ToolSpriteArea = WimpSpriteArea;
	WindowHeader->MinArea = 0;
	WindowHeader->TitleIconData.IconTextIndirect.Text = TXT(Title);
	WindowHeader->TitleIconData.IconTextIndirect.Validation = NULL;
	WindowHeader->TitleIconData.IconTextIndirect.Length = strlen(TXT(Title));
	WindowHeader->IconCount = Icons;
	return((t_Window*)WindowHeader);
}

// Set up an icon...
void FillIcon(t_Icon *Icon, unsigned Flags, char* Text, int Length, char *Validation, t_SpriteArea *Area, int ESG, int X, int Y, int Width, int Height) {
	Icon->BoundingBox.MinX = X;
	Icon->BoundingBox.MinY = -Y-Height;
	Icon->BoundingBox.MaxX = X+Width;
	Icon->BoundingBox.MaxY = -Y;
	Icon->IconFlags = Flags | IconFlags_Mask_Indirect | ((ESG & 0x1f)<<IconFlags_Shift_ESG);
	if (*Validation == 'R') { Icon->IconFlags |= IconFlags_Mask_Border; }
	if (Area == NULL) {
		Icon->IconData.IconTextIndirect.Text = TXT(Text);
		Icon->IconData.IconTextIndirect.Validation = Validation;
		if (Length == 0) { Icon->IconData.IconTextIndirect.Length = strlen(TXT(Text)) +1; }
		else             { Icon->IconData.IconTextIndirect.Length = Length;               }
	}
	else {
		Icon->IconData.IconSpriteIndirect.Sprite = TXT(Text);
		Icon->IconData.IconSpriteIndirect.Area = Area;
		if (Length == 0) { Icon->IconData.IconSpriteIndirect.Length = strlen(TXT(Text)) +1; }
		else             { Icon->IconData.IconSpriteIndirect.Length = Length;               }

	}
	//BuggyIconI("Icon",Icon);
}

#define FillTextIcon(Window,Icon,Flags,Text,X,Y,Validation,Width) FillIcon(&Window->WindowIcon[Icon],Flags,Text,0,Validation,NULL,0,X,Y,Width,44)
#define FillTextIconXY(Window,Icon,Flags,Text,Validation,Xs,Xi,Ys,Yi,Width) FillIcon(&Window->WindowIcon[Icon],Flags,Text,0,Validation,NULL,0,XY(Xs)+Xi,XY(Ys)+Yi,Width,XY("T"))

// Wimp open window
void WimpOpenWindow(void) {
	SWIRegs.r[1] = (int)(WimpPollBlock);
	SWI(Wimp_OpenWindow);
}

// Wimp close window
void WimpCloseWindow(t_WindowHandle WindowHandle) {
	if (WindowHandle != (t_WindowHandle)-1) {
		if (WindowHandle != Window_WimpBlock) {
			WimpPollBlockStorage.OpenWindow.WindowHandle = WindowHandle;
		}
		SWIRegs.r[1] = (int)(&WimpPollBlockStorage);
		SWI(Wimp_CloseWindow);
	}
}


// Move to center
void MoveToCenter(t_BoxenLuder *Box) {
	int X,Y;
	// Current screen mode
	SWIRegs.r[0] = -1;
	// Read X and Y resolution
	SWIRegs.r[1] = 11; SWI(OS_ReadModeVariable); X = ++(SWIRegs.r[2]);
	SWIRegs.r[1] = 12; SWI(OS_ReadModeVariable); Y = ++(SWIRegs.r[2]);
	// Shift resolution by Eigfactor
	SWIRegs.r[1] = 4; SWI(OS_ReadModeVariable); X = X * (++(SWIRegs.r[2]));
	SWIRegs.r[1] = 5; SWI(OS_ReadModeVariable); Y = Y * (++(SWIRegs.r[2]));
	// Get offset to shift to middle X
	X = (X - (Box->MaxX - Box->MinX)) / 2;
	Box->MinX +=X;
	Box->MaxX +=X;
	// Get offset to shift to middle Y
	Y = (Y - (Box->MaxY - Box->MinY)) / 2;
	Box->MinY +=Y;
	Box->MaxY +=Y;
}

void MoveToMouse(t_BoxenLuder *Box, int X, int Y) {

	Box->MaxX = X + Box->MaxX - Box->MinX;
	Box->MinX = X;
	Box->MinY = Y + Box->MinY - Box->MaxY;
	Box->MaxY = Y;
}

// Move to bottom right
void MoveToBottomRight(t_BoxenLuder *Box) {
	int X,Y;
	// Current screen mode
	SWIRegs.r[0] = -1;
	// Read X resolution and shoft by Eigfactor
	SWIRegs.r[1] = 11; SWI(OS_ReadModeVariable); X = ++(SWIRegs.r[2]);
	SWIRegs.r[1] = 4; SWI(OS_ReadModeVariable); X = X * (++(SWIRegs.r[2]));
	// Get offset to shift X
	X = X - 20 - Box->MaxX;
	Box->MinX +=X;
	Box->MaxX +=X;
	// Get offset to shift Y
	Y = Box->MinY +160; 
	Box->MinY +=Y;
	Box->MaxY +=Y;
}

// Wimp open window
t_WindowHandle WimpOpenNormalWindow(t_Window *Window, t_WindowHandle WindowHandle, char Pos) {

	t_WimpGetWindowInfoShortBlock WindowInfo;
	t_WimpOpenWindowBlock WindowOpen;

	if (WindowHandle == -1) {
		// First opening we use template's visible area
		WindowOpen.WindowHandle = WimpCreateWindow(Window);
		WindowOpen.Visible.MinX = Window->WindowHeader.Visible.MinX;
		WindowOpen.Visible.MinY = Window->WindowHeader.Visible.MinY;
		WindowOpen.Visible.MaxX = Window->WindowHeader.Visible.MaxX;
		WindowOpen.Visible.MaxY = Window->WindowHeader.Visible.MaxY;
		WindowOpen.ScrollX      = Window->WindowHeader.ScrollX;
		WindowOpen.ScrollY      = Window->WindowHeader.ScrollY;
		WindowOpen.WindowStack  = -1;
		if (Pos == 'C') { MoveToCenter(&(WindowOpen.Visible)); }
		if (Pos == 'B') { MoveToBottomRight(&(WindowOpen.Visible)); }
	}
	else {
		// Reopening we use current visible area
		WindowInfo.WindowHandle = WindowHandle;
		SWIRegs.r[1] = (int)(&WindowInfo) +1;
		SWI(Wimp_GetWindowInfo);
		WindowOpen.WindowHandle = WindowHandle;
		WindowOpen.Visible.MinX = WindowInfo.WindowHeader.Visible.MinX;
		WindowOpen.Visible.MaxX = WindowInfo.WindowHeader.Visible.MaxX;
		WindowOpen.Visible.MinY = WindowInfo.WindowHeader.Visible.MinY;
		WindowOpen.Visible.MaxY = WindowInfo.WindowHeader.Visible.MaxY;
		WindowOpen.ScrollX      = WindowInfo.WindowHeader.ScrollX;
		WindowOpen.ScrollY      = WindowInfo.WindowHeader.ScrollY;
		WindowOpen.WindowStack  = -1;
	}

	SWIRegs.r[1] = (int)(&WindowOpen);
	SWI(Wimp_OpenWindow);

	return(WindowOpen.WindowHandle);
}

void WimpRedrawAllIcons(t_Window *Window,t_WindowHandle WindowHandle) {
	t_WimpSetIconStateBlock Block;
	Block.WindowHandle = WindowHandle;
	Block.EORword = 0;
	Block.CLRword = 0;
	for (Block.IconHandle=0;Block.IconHandle<Window->WindowHeader.IconCount;Block.IconHandle++) {
		SWIRegs.r[1] = (int)(&Block);
		SWI(Wimp_SetIconState);
	}
}

void WimpRedrawIcon(t_WindowHandle WindowHandle, t_IconHandle IconHandle) {
	t_WimpSetIconStateBlock Block;
	Block.WindowHandle = WindowHandle;
	Block.IconHandle   = IconHandle;
	Block.EORword = 0;
	Block.CLRword = 0;
	SWIRegs.r[1] = (int)(&Block);
	SWI(Wimp_SetIconState);
}

void WimpRedrawWholeWindow(t_WindowHandle WindowHandle) {
	t_WimpGetWindowInfoShortBlock WindowInfo;

	// Get Info on my Window
	WindowInfo.WindowHandle = WindowHandle;
	SWIRegs.r[1] = (int)(&WindowInfo) +1;
	BuggyRegs("!",2);
	BuggyInt("!",sizeof(WindowInfo))
	BuggyBuffI("?",&WindowInfo,22)
	SWI(Wimp_GetWindowInfo);
	BuggyBuffI("!",&WindowInfo,22)

	SWIRegs.r[0] = (int)WindowHandle;
	SWIRegs.r[1] = WindowInfo.WindowHeader.Workarea.MinX;
	SWIRegs.r[2] = WindowInfo.WindowHeader.Workarea.MinY;
	SWIRegs.r[3] = WindowInfo.WindowHeader.Workarea.MaxX;
	SWIRegs.r[4] = WindowInfo.WindowHeader.Workarea.MaxY;
	SWI(Wimp_ForceRedraw);
}




void Laber(char* Bla) {
	FILE *f;
	if (StringCompare(Bla,"Starting") == 0) { f = fopen("<Obey$Dir>.Bla","w"); }
	else                                    { f = fopen("<Obey$Dir>.Bla","a"); }
	fprintf(f,"%s\n",Bla);
	fclose(f);
}

void WaitingStatus(int i) {
	sprintf(MyStatus,"Wating %d",i);
	WimpRedrawIcon(MyWindowHandle,0);
}

void Wait(int cs) {
	WimpPollWait = cs;
	int ReasonCode;

	while (True) {
		ReasonCode = WimpPoll();

		// Handle the request
		switch(ReasonCode) {

		// Nullevent
		case ReasonCode_Null:
			return;
			break;

		// Window stuff
		case ReasonCode_OpenWindow:
			//BuggyMessage("ReasonCode_OpenWindow");
			WimpOpenWindow();
			break;
		case ReasonCode_CloseWindow:
			//BuggyMessage("ReasonCode_CloseWindow");
			WimpCloseWindow(Window_WimpBlock);
			break;

		// User message
		case ReasonCode_UserMessage:
		case ReasonCode_UserMessageRecorded:
		case ReasonCode_UserMessageAcknowledge:
			switch(WimpPollBlock->UserMessage.MessageCode) {
			case MessageCode_Quit:
				 WimpCloseDown();
				 break;
			}
			break;
		}
	}
}

void DoTheWork(void) {
	FILE *f;
	int flg;
	int i = 0;

	// Starting something
	Laber("Starting");
	if (remove("<Obey$Dir>.Done") == 0) { Laber("Good to go"); }
	else                                { Laber("Done not deleted - was perhaps not there"); }
	flg = True;
	while (flg) {
		Wait(200);
		Laber("Waiting");
		WaitingStatus(i++);
		f = fopen("<Obey$Dir>.Done", "r");
		if (f) {
			fclose(f);
			flg = False; // Done
		}
	}
	// I am happy
	Laber("Done");
}


int main (int argc, char *argv[]) {


	BuggyClear;

	WimpInitialise();
	MyWindow = CreateBasicWindow("",WindowFlags_ContentOnly,1,20+400+20,20+44+20);
	FillTextIcon(MyWindow,0,IconFlags_TextL,(char*)(&MyStatus),20,20,IconValidation_Flat,400);
	MyWindowHandle = WimpOpenNormalWindow(MyWindow,MyWindowHandle,'B');
	
	DoTheWork();

	WimpCloseDown();

}

