#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;


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

char MyWorkStatus = 'S';

// 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));
}


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 DoSomeWork(void) {
	FILE *f;
	switch(MyWorkStatus) {
	case 'S':
		// Starting something
		Laber("Starting");
		if (remove("<Obey$Dir>.Done") == 0) { Laber("Good to go"); }
		else                                { Laber("Done not deleted - was perhaps not there"); }
		MyWorkStatus = 'W';
		WimpPollWait = 200; // 2 seconds;
		break;
	case 'W':
		// I waited a moment
		Laber("Waiting");
		f = fopen("<Obey$Dir>.Done", "r");
		if (f) {
			fclose(f);
			// I am happy
			Laber("Done");
			WimpCloseDown();
		}
		break;
	}
}


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

	int ReasonCode;


	BuggyClear;

	WimpInitialise();
	WimpPollWait = 1;

	while (True) {
		ReasonCode = WimpPoll();
		//BuggyInt("ReasonCode",ReasonCode);
		//BuggyBuffI("Poll",WimpPollBlock,12);

		// Handle the request
		switch(ReasonCode) {

		// Nullevent
		case ReasonCode_Null:
			BuggyChar("Null",MyWorkStatus);
			DoSomeWork();
			break;

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

	}

}

