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

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


// Ist die Delta-Spalte mit Bildchen oder mit Text?
#define SpriteNew   "!new"
#define SpriteNewer "!newer"
#define SpriteOld   "!old"
#define SpriteOlder "!older"
#define SpriteToDo  ""
#define SpriteSame  "!same"


// 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 = 200;
t_WimpPollBlock WimpPollBlockStorage;
t_WimpPollBlock *WimpPollBlock;
t_TaskHandle MyTaskHandle = 0;
t_MessageTrans_FileDescriptor *MyMessages;
t_SpriteArea *MySprites;


const char MyTaskName[]    = "AppUpdater";
const char MyVersion[]     = "1.11";


typedef struct {
	int Objecttype, Filetype;
	unsigned Length;
	t_OSTimestamp Timestamp;
	// t_DateTimeOrdinalBlock DateTime;
	// int Age;
	char *Name;
	char *Path;
	int Level;
} t_Directory_Entry;


typedef struct {
	t_IconHandle Icon;
	char *Name;
	char Path[];
} t_Directory_ArrayEntry;

typedef struct {
	t_IconHandle Icon;
	t_Directory_ArrayEntry *Old,*New;
	char Sprite[7];
} t_Directory_ArrayDelta;

typedef struct {
	int Used,Size;
	void *Array[];
} t_Dynamic_Pointer_Array;


t_IconHandle MyBarIcon = 0;
t_Menu *MyBarMenu;
t_Window *MyInfoWindow;
t_Window *MyMainWindow;
t_WindowHandle MyMainWindowHandle = -1;

t_Dynamic_Pointer_Array *MyOldArray = NULL;
t_Dynamic_Pointer_Array *MyNewArray = NULL;
t_Dynamic_Pointer_Array *MyArray = NULL;
t_Dynamic_Pointer_Array *MyDelta = NULL;
char MyPath[4096];

char MyIconSpriteOld[]   = SpriteOld;
char MyIconSpriteOlder[] = SpriteOlder;
char MyIconSpriteNew[]   = SpriteNew;
char MyIconSpriteNewer[] = SpriteNewer;
char MyIconSpriteSame[]  = SpriteSame;



// Window positioning support
int XY(char *Where) {
	int r = 0;
	int n;
	char *c = Where;
	char w;
	for (;*c!=0;c++) {
		w = *c++;
		if ((*c >= '0') && (*c <= '9')) {
			n = 0;
			while ((*c >= '0') && (*c <= '9')) { n = n*10 + (*c++-'0'); }
		}
		else {
			n = 1;
		}
		c--;
		switch (w) {
		case 'D':
			r += 4*n;
			break;
		case 'S':
			r += 20*n;
			break;
		case 'T':
			r += 44*n;
			break;
		case 'R':
			r += 48*n;
			break;
		case '+':
			r +=n;
			break;
		case '-':
			r -=n;
			break;
		}
	}
	return(r);
}


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

t_SpriteArea *SpritesLoad(char *FileName) {

	t_SpriteArea *SpriteArea;
	int Size;

	SWIRegs.r[0] = 17;
	SWIRegs.r[1] = (int)(FileName);
	if ((SWIX(OS_File)) != NULL) { return (NULL); }

	Size = SWIRegs.r[4]+4;
	SpriteArea = malloc(Size);
	if(SpriteArea == NULL) { SWIE("SpritesLoad malloc"); }

	SpriteArea->Size = Size;
	SpriteArea->First = 16;
	SWIRegs.r[0] = 9+256;
	SWIRegs.r[1] = (int)SpriteArea;
	SWI(OS_SpriteOp);
	
	SWIRegs.r[0] = 10+256;
	SWIRegs.r[1] = (int)SpriteArea;
	SWIRegs.r[2] = (int)FileName;
	SWI(OS_SpriteOp);

	return(SpriteArea);
}

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


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

t_Centiseconds Centiseconds(t_OSTimestamp *OSTimestamp) {
	t_Centiseconds c = 0;
	int i;

	for (i=4;i>=0;i--) {
		c = (c<<8) + (t_Centiseconds)(OSTimestamp->All[i]);
	}
	return(c);
}

int Time_AgeDays(t_OSTimestamp *OSTimestamp, int DaysNotHours) {

	t_Centiseconds f,n;
	t_OSTimestamp  Now;

	SWIRegs.r[0] = 14;
	Now.All[0] = 3;
	SWIRegs.r[1] = (int)(&Now);
	SWIX(OS_Word);
	n = Centiseconds(&Now);

	f = Centiseconds(OSTimestamp);
	if (DaysNotHours) {
		return ((int)((n-f) / (100 * 60 * 60 * 24)));
	}
	else {
		return ((int)((n-f) / (100 * 60 * 60)));
	}
}

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

// Function to create a simple menu
t_Menu *CreateMenu(char *Title, ...) {
	char *Entry;
	int Count = 0;
	t_MenuHeader *MenuHeader;
	t_MenuItem   *MenuItem;
	va_list Entries;

	// Count the number of entries needed

	va_start(Entries,Title); // Get the parameter list
	for(;;) {
		Entry = va_arg(Entries, char*); // Get next one
		if(Entry == NULL) { break; }
		Count++;
	}
	va_end(Entries); // Clean up

	// Allocate the space we need
	MenuHeader = malloc(sizeof(t_MenuHeader) + Count * sizeof(t_MenuItem));
	if(MenuHeader == NULL) { SWIE("Menu malloc"); }

	// Fill Header
	if (strlen(TXT(Title)) > sizeof(MenuHeader->Title)) {
		MenuHeader->Title.IconTextIndirect.Text = TXT(Title);
		MenuHeader->Title.IconTextIndirect.Validation = NULL;
		MenuHeader->Title.IconTextIndirect.Length = strlen(TXT(Title));
	}
	else {
		strncpy(MenuHeader->Title.IconText,TXT(Title),sizeof(MenuHeader->Title.IconText));
	}
	MenuHeader->TitleForeground = 7;
	MenuHeader->TitleBackground = 2;
	MenuHeader->BodyForeground = 7;
	MenuHeader->BodyBackground = 0;
	MenuHeader->Width = 0;
	MenuHeader->Height = 44;
	MenuHeader->Gap = 0;

	// Point to first menu entry
	// Since MenuHeader is a pointer to the menu header, adding 1 points just after it!
	MenuItem = (t_MenuItem*)(MenuHeader+1);

	va_start(Entries,Title); // Get the parameter list again
	for(;;) {
		Entry = va_arg(Entries, char*); // Get next one
		if(Entry == NULL) { break; }
		MenuItem->MenuFlags = MenuFlags_Standard;
		MenuItem->MenuLink.Warning = 0;
		if (strlen(TXT(Entry)) > sizeof(MenuItem->IconData.IconText)) {
			MenuItem->IconData.IconTextIndirect.Text = TXT(Entry);
			MenuItem->IconData.IconTextIndirect.Validation = NULL;
			MenuItem->IconData.IconTextIndirect.Length = strlen(TXT(Entry));
			MenuItem->IconFlags = IconFlags_MenuItem | IconFlags_Mask_Indirect;
		}
		else {
			strncpy(MenuItem->IconData.IconText,TXT(Entry),sizeof(MenuItem->IconData.IconText));
			MenuItem->IconFlags = IconFlags_MenuItem;
		}
		MenuItem++;
	}
	// Set flag in last menu item that it is the last one
	MenuItem--;
	MenuItem->MenuFlags = (MenuItem->MenuFlags | MenuFlags_Mask_Last);
	va_end(Entries); // Clean up

	// Set flag in first if title is indirected
	if (strlen(TXT(Title)) > sizeof(MenuHeader->Title)) {
		MenuItem = (t_MenuItem*)(MenuHeader+1);
		MenuItem->MenuFlags = MenuItem->MenuFlags | MenuFlags_Title_Indirect;
	}

	return ((t_Menu*)MenuHeader);

}


// Open Menu
void OpenMenu(t_Menu *Menu) {

	t_MenuHeader *MenuHeader;
	t_MenuItem *MenuItem;

	MenuHeader = (t_MenuHeader*)Menu;

	// X position is 64 to the left of the mouse
	// Y position depends: Iconbar or not
	SWIRegs.r[1] = (int)MenuHeader;
	SWIRegs.r[2] = WimpPollBlock->MouseClick.Mouse.X - 64;
	SWIRegs.r[3] = WimpPollBlock->MouseClick.Mouse.Y - 8;
	if (WimpPollBlock->MouseClick.WindowHandle == WindowIconbar) {
		SWIRegs.r[3] = 96 + MenuHeader->Height;
		MenuItem = (t_MenuItem*)(MenuHeader+1);
		do	{
			SWIRegs.r[3] += MenuHeader->Height;
			if ((MenuItem->MenuFlags & MenuFlags_Mask_Dashes) != 0) SWIRegs.r[3] += 24;
			MenuItem++;
		}
		while ((MenuItem->MenuFlags & MenuFlags_Mask_Last) == 0);
	}
	SWI(Wimp_CreateMenu);

}

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



// Function to put Icon on icon bar with SWI Wimp_CreateIcon
t_IconHandle WimpCreateIconBarIcon(char *Sprite, t_WindowHandle LeftRight) {
	t_WimpCreateIconBlock Block;
	Block.WindowHandle = LeftRight;
	Block.Icon.BoundingBox.MinX = 0;
	Block.Icon.BoundingBox.MinY = 0;
	Block.Icon.BoundingBox.MaxX = 69;
	Block.Icon.BoundingBox.MaxY = 68;
	Block.Icon.IconFlags = IconFlags_IconBar;
	Block.Icon.IconData.IconSpriteIndirect.Sprite = Sprite;
	Block.Icon.IconData.IconSpriteIndirect.Area = WimpSpriteArea;
	Block.Icon.IconData.IconSpriteIndirect.Length = strlen(Sprite);
	SWIRegs.r[0] = 0;
	SWIRegs.r[1] = (int)&Block;
	SWI(Wimp_CreateIcon);
	return(SWIRegs.r[0]);
}

void WimpDeleteWindowIcon(t_WindowHandle WindowHandle, t_IconHandle IconHandle) {
	t_WimpDeleteIconBlock Block;
	Block.WindowHandle = WindowHandle;
	Block.IconHandle = IconHandle;
	SWIRegs.r[0] = 0;
	SWIRegs.r[1] = (int)&Block;
	SWI(Wimp_DeleteIcon);
}

// Function to remove Icon  from icon bar with SWI Wimp_DeleteIcon
#define WimpDeleteIconBarIcon(IconHandle) WimpDeleteWindowIcon(WindowIconbar,Iconhandle)

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

// Create a Window Icon
t_IconHandle CreateWindowIcon(t_WindowHandle WindowHandle, unsigned Flags, char* Text, int Length, char *Validation, t_SpriteArea *Area, int ESG, int X, int Y, int Width, int Height) {
	t_WimpCreateIconBlock Block;
	Block.WindowHandle = WindowHandle;
	FillIcon(&Block.Icon,Flags,Text,Length,Validation,Area,ESG,X,Y,Width,Height);
	SWIRegs.r[0] = 0;
	SWIRegs.r[1] = (int)&Block;
	SWI(Wimp_CreateIcon);
	return ((t_IconHandle)SWIRegs.r[0]);
}


#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 FillEditIcon(Window,Icon,Text,Length,Validation,ESG,X,Y,Width) FillIcon(&Window->WindowIcon[Icon],IconFlags_Edit | IconFlags_Mask_Border,Text,Length,Validation,NULL,ESG,X,Y,Width,44)
#define FillButtonIcon(Window,Icon,Text,Validation,X,Y,Width,Height) FillIcon(&Window->WindowIcon[Icon],IconFlags_Button | IconFlags_Mask_Border,Text,0,Validation,NULL,0,X,Y,Width,Height)
#define FillOnOffIcon(Window,Icon,Validation,ESG,X,Y) FillIcon(&Window->WindowIcon[Icon],IconFlags_OnOff,Icon_NoText,1,Validation,NULL,ESG,X,Y,44,44)
#define FillClickIcon(Window,Icon,Validation,X,Y) FillIcon(&Window->WindowIcon[Icon],IconFlags_Click,Icon_NoText,1,Validation,NULL,0,X,Y,44,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"))
#define FillEditIconXY(Window,Icon,Text,Length,Validation,ESG,Xs,Xi,Ys,Yi,Width) FillIcon(&Window->WindowIcon[Icon],IconFlags_Edit | IconFlags_Mask_Border,Text,Length,Validation,NULL,ESG,XY(Xs)+Xi,XY(Ys)+Yi,Width,XY("T"))
#define FillButtonIconXY(Window,Icon,Text,Validation,Xs,Xi,Ys,Yi,Width,Height) FillIcon(&Window->WindowIcon[Icon],IconFlags_Button | IconFlags_Mask_Border,Text,0,Validation,NULL,0,XY(Xs)+Xi,XY(Ys)+Yi,Width,Height)
#define FillOnOffIconXY(Window,Icon,Validation,ESG,Xs,Xi,Ys,Yi) FillIcon(&Window->WindowIcon[Icon],IconFlags_OnOff,Icon_NoText,1,Validation,NULL,ESG,XY(Xs)+Xi,XY(Ys)+Yi,XY("T"),XY("T"))
#define FillClickIconXY(Window,Icon,Validation,Xs,Xi,Ys,Yi) FillIcon(&Window->WindowIcon[Icon],IconFlags_Click,Icon_NoText,1,Validation,NULL,0,XY(Xs)+Xi,XY(Ys)+Yi,XY("T"),XY("T"))
#define FillSpriteIconXY(Window,Icon,Flags,Sprite,Area,Xs,Xi,Ys,Yi,Width,Height) FillIcon(&Window->WindowIcon[Icon],Flags | IconFlags_Sprite,Sprite,0,NULL,Area,0,XY(Xs)+Xi,XY(Ys)+Yi,Width,Height)

#define CreateTextIconXY(Window,Flags,Text,Validation,Xs,Xi,Ys,Yi,Width) CreateWindowIcon(Window,Flags,Text,0,Validation,NULL,0,XY(Xs)+Xi,XY(Ys)+Yi,Width,XY("T"))
#define CreateEditIconXY(Window,Text,Length,Validation,ESG,Xs,Xi,Ys,Yi,Width) CreateWindowIcon(Window,IconFlags_Edit | IconFlags_Mask_Border,Text,Length,Validation,NULL,ESG,XY(Xs)+Xi,XY(Ys)+Yi,Width,XY("T"))
#define CreateButtonIconXY(Window,Text,Validation,Xs,Xi,Ys,Yi,Width,Height) CreateWindowIcon(Window,IconFlags_Button | IconFlags_Mask_Border,Text,0,Validation,NULL,0,XY(Xs)+Xi,XY(Ys)+Yi,Width,Height)
#define CreateOnOffIconXY(Window,Validation,ESG,Xs,Xi,Ys,Yi) CreateWindowIcon(WindowIcon,IconFlags_OnOff,Icon_NoText,1,Validation,NULL,ESG,XY(Xs)+Xi,XY(Ys)+Yi,XY("T"),XY("T"))
#define CreateClickIconXY(Window,Validation,Xs,Xi,Ys,Yi) CreateWindowIcon(Window,IconFlags_Click,Icon_NoText,1,Validation,NULL,0,XY(Xs)+Xi,XY(Ys)+Yi,XY("T"),XY("T"))
#define CreateSpriteIconXY(Window,Flags,Sprite,Area,Xs,Xi,Ys,Yi,Width,Height) CreateWindowIcon(Window,Flags | IconFlags_Sprite,Sprite,0,NULL,Area,0,XY(Xs)+Xi,XY(Ys)+Yi,Width,Height)

// Get Icon Text
char *GetIconText(unsigned IconFlags, t_IconData *IconData) {

	if ((IconFlags & IconFlags_Mask_Text) == 0) {
		IconTextBuffer[0] = 0;
		return ((char*)(&IconTextBuffer));
	}

	if ((IconFlags & IconFlags_Mask_Indirect) == 0) {
		strncpy(IconTextBuffer,IconData->IconText,sizeof(t_IconData));
		StringToC(IconTextBuffer);
		return ((char*)(&IconTextBuffer));
	}

	StringToC(IconData->IconTextIndirect.Text);
	return (IconData->IconTextIndirect.Text);
}

#define GetIconTextFD(IconFlags) GetIconText(IconFlags,(t_IconData*)(&IconFlags +1))

#define GetIconTextMenuEntry(Menu,Item) GetIconText(Menu->MenuItem[Item].IconFlags,&(Menu->MenuItem[Item].IconData))


void SetIconText(unsigned IconFlags,t_IconData *IconData, char *Text) {

	char *c;
	char x;

	if ((IconFlags & IconFlags_Mask_Text) == 0) {
		// No Text in the icon
		return;
	}

	if ((IconFlags & IconFlags_Mask_Indirect) == 0) {
		// Text directly in icon data, length limited
		strncpy(IconData->IconText,Text,sizeof(t_IconData));
		return;
	}

	if (strlen(Text)>=IconData->IconTextIndirect.Length) {
		// Text it a bit too long for the icon so we cut it off
		c = Text + IconData->IconTextIndirect.Length -1; // -1 to allow trailing zero
		x = *c;
		*c = 0;
		strcpy(IconData->IconTextIndirect.Text,Text);
		*c = x;
	}
	else {
		// Text fits nicely...
		strcpy(IconData->IconTextIndirect.Text,Text);
	}
}

#define SetIconTextFD(IconFlags,Text) SetIconText(IconFlags,(t_IconData*)(&IconFlags +1),Text)

int GetMenuEntryNumber(t_Menu *Menu, char *Token)  {
	char *Entry = Token;
	int Result = -1;
	int i;
	for (i=0;Result==-1;i++) {
		if(strcmp(Entry,GetIconText(Menu->MenuItem[i].IconFlags,&(Menu->MenuItem[i].IconData))) == 0) { Result = i; }
		if ((Menu->MenuItem[i].MenuFlags & MenuFlags_Mask_Last) != 0) { break; }
	}
	return(Result);
}


int GetWindowIconNumber(t_Window *Window, char *Token)  {
	char *Entry = Token;
	int Result = -1;
	int i;

	for (i=0;i<Window->WindowHeader.IconCount;i++) {
		if (strcmp(Entry,GetIconText(Window->WindowIcon[i].IconFlags,&(Window->WindowIcon[i].IconData))) == 0) { Result = i; }
	}
	return(Result);
}


// 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 big window
t_Window *CreateBigWindow(char *Title, unsigned Flags, int Icons, int MaxX, int MaxY, int VisX, int VisY) {
	t_WindowHeader *WindowHeader;
	WindowHeader = (t_WindowHeader*)CreateBasicWindow(Title, Flags, Icons, MaxX, MaxY);
	// Just set visible size - we move to center later...
	if (VisX != 0) { WindowHeader->Visible.MaxX = VisX; }
	if (VisY != 0) { WindowHeader->Visible.MaxY = VisY; }
	return((t_Window*)WindowHeader);
}

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


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

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

// Wimp open window
t_WindowHandle WimpOpenNormalWindow(t_Window *Window, t_WindowHandle WindowHandle, int Center) {

	t_WimpOpenWindowBlock Block;

	if (WindowHandle == -1) { Block.WindowHandle = WimpCreateWindow(Window); }
	else                    { Block.WindowHandle = WindowHandle; }
	Block.Visible.MinX = Window->WindowHeader.Visible.MinX;
	Block.Visible.MinY = Window->WindowHeader.Visible.MinY;
	Block.Visible.MaxX = Window->WindowHeader.Visible.MaxX;
	Block.Visible.MaxY = Window->WindowHeader.Visible.MaxY;
	Block.ScrollX      = Window->WindowHeader.ScrollX;
	Block.ScrollY      = Window->WindowHeader.ScrollY;
	Block.WindowStack  = Window->WindowHeader.WindowStack;

	if (Center) { MoveToCenter(&(Block.Visible)); }

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

	return(Block.WindowHandle);
}

// Wimp open a pane window
t_WindowHandle WimpOpenPaneWindow(t_Window *Pane, t_WindowHandle PaneHandle, t_WindowHandle ParentHandle, int X, int Y, int Xfree, int Yfree) {

	t_BoxenLuder ParentVisible;
	int i,j;

	if (ParentHandle == -1) { return PaneHandle; }

	// Where is the parent?
	WimpPollBlockStorage.GetWindowState.WindowHandle = ParentHandle;
	SWI(Wimp_GetWindowState);
	if (( WimpPollBlockStorage.GetWindowState.WindowFlags && WindowFlags_Mask_Open) == 0) { return PaneHandle; }

	ParentVisible.MinX = WimpPollBlockStorage.GetWindowState.Visible.MinX;
	ParentVisible.MaxX = WimpPollBlockStorage.GetWindowState.Visible.MaxX;
	ParentVisible.MinY = WimpPollBlockStorage.GetWindowState.Visible.MinY;
	ParentVisible.MaxY = WimpPollBlockStorage.GetWindowState.Visible.MaxY;

	if (PaneHandle == -1) { WimpPollBlockStorage.OpenWindow.WindowHandle = WimpCreateWindow(Pane); }
	else                  { WimpPollBlockStorage.OpenWindow.WindowHandle = PaneHandle; }
	WimpPollBlockStorage.OpenWindow.ScrollX      = Pane->WindowHeader.ScrollX;
	WimpPollBlockStorage.OpenWindow.ScrollY      = Pane->WindowHeader.ScrollY;
	WimpPollBlockStorage.OpenWindow.WindowStack  = Pane->WindowHeader.WindowStack;

	// Position the pane is relative to MinX, MaxY of the window
	// Get width of Pane
	i = Pane->WindowHeader.Workarea.MaxX - Pane->WindowHeader.Workarea.MinX;
	if (Xfree != -1) {
		// Adjust visible width ... to the one of the parent minus Xfree
		j = ParentVisible.MaxX - ParentVisible.MinX - Xfree;
		i = j < 1 ? 1 : ( j > i ? i:j );
	}
	WimpPollBlockStorage.OpenWindow.Visible.MinX = ParentVisible.MinX + X;
	WimpPollBlockStorage.OpenWindow.Visible.MaxX = WimpPollBlockStorage.OpenWindow.Visible.MinX + i;
	// Get height of Pane
	i = Pane->WindowHeader.Workarea.MinY - Pane->WindowHeader.Workarea.MaxY;
	if (Yfree != -1) {
		// Adjust visible height... to the one of the parent minus Yfree
		j = ParentVisible.MinY - ParentVisible.MaxY + Yfree;
		i = j > -1 ? -1 : ( j < i ? i:j);
	}
	WimpPollBlockStorage.OpenWindow.Visible.MaxY = ParentVisible.MaxY - Y;
	WimpPollBlockStorage.OpenWindow.Visible.MinY = WimpPollBlockStorage.OpenWindow.Visible.MaxY + i;

	SWIRegs.r[1] = (int)(&WimpPollBlockStorage);
	SWI(Wimp_OpenWindow);
	return(WimpPollBlockStorage.OpenWindow.WindowHandle);
}

// Wimp open a pane window attached to top left outside
#define WimpOpenPaneWindowTLO(Pane,PaneHandle,ParentHandle) WimpOpenPaneWindow(Pane,PaneHandle,ParentHandle,Pane->WindowHeader.Visible.MinX - Pane->WindowHeader.Visible.MaxX -2,0,-1,-1)

void WimpRedrawAllIcons(t_Window *Window,t_WindowHandle WindowHandle) {
	t_IconSetState 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_IconSetState 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;
	SWI(Wimp_GetWindowInfo);

	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 WimpResizeWindow(t_Window *Window, t_WindowHandle WindowHandle, int Width, int Height) {
	t_WimpGetWindowInfoShortBlock WindowInfo;
	t_WimpSetExtendBlock WimpExtend;

	// Get Info on my Window
	WindowInfo.WindowHandle = WindowHandle;
	SWIRegs.r[1] = (int)(&WindowInfo) +1;
	SWI(Wimp_GetWindowInfo);

	// Set work area as requested
	WimpExtend.Workarea.MinX = WindowInfo.WindowHeader.Workarea.MinX;
	WimpExtend.Workarea.MinY = WindowInfo.WindowHeader.Workarea.MinY;
	WimpExtend.Workarea.MaxX = WindowInfo.WindowHeader.Workarea.MaxX;
	WimpExtend.Workarea.MaxY = WindowInfo.WindowHeader.Workarea.MaxY;
	if (Width > 0)  { WimpExtend.Workarea.MaxX = WimpExtend.Workarea.MinX + Width;  }
	if (Height > 0) { WimpExtend.Workarea.MinY = WimpExtend.Workarea.MaxY - Height; }
	SWIRegs.r[0] = (int)(WindowHandle);
	SWIRegs.r[1] = (int)(&WimpExtend);
	SWI(Wimp_SetExtent);

	// Resize if needed
	if (Width > 0) {
		if (WindowInfo.WindowHeader.Visible.MaxX - WindowInfo.WindowHeader.Visible.MinX < Width) {
			WindowInfo.WindowHeader.Visible.MaxX = WindowInfo.WindowHeader.Visible.MinX + Width;
		}
	}
	if (Height > 0) {
		if (WindowInfo.WindowHeader.Visible.MinY - WindowInfo.WindowHeader.Visible.MaxY < Height) {
			WindowInfo.WindowHeader.Visible.MinY = WindowInfo.WindowHeader.Visible.MaxY - Height;
		}
	}
	SWIRegs.r[1] = (int)(&WindowInfo);
	SWI(Wimp_OpenWindow);

	// redraw
	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 WimpGetIcon(t_WindowHandle WindowHandle, t_IconHandle IconHandle, t_Icon *Icon) {
	t_WimpGetIconStateBlock Block;
	Block.WindowHandle = WindowHandle;
	Block.IconHandle = IconHandle;
	SWIRegs.r[1] = (int)(&Block);
	SWI(Wimp_GetIconState);
	memcpy(Icon,&(Block.Icon),sizeof(t_Icon));
}

int WimpGetIconSelected(t_WindowHandle WindowHandle, t_IconHandle IconHandle) {
	t_WimpGetIconStateBlock Block;
	Block.WindowHandle = WindowHandle;
	Block.IconHandle = IconHandle;
	SWIRegs.r[1] = (int)(&Block);
	SWI(Wimp_GetIconState);
	return((Block.Icon.IconFlags & IconFlags_Mask_Selected) == 0 ? False : True);
}

int WimpGetWindowIconCount(t_WindowHandle WindowHandle) {
	t_WimpGetWindowInfoShortBlock WindowInfo;
	WindowInfo.WindowHandle = WindowHandle;
	SWIRegs.r[1] = (int)(&WindowInfo) +1;
	SWI(Wimp_GetWindowInfo);
	return(WindowInfo.WindowHeader.IconCount);
}

int GetTerritoryNumber(void) {
	if (SWIX(Territory_Number) != NULL) { return(-1); }
	else                                { return(SWIRegs.r[0]); }
}

void GetTerritoryName(char *Buffer, int Length) {
	if (SWIX(Territory_Number) != NULL) {
		*Buffer = 0;
		return;
	}

	SWIRegs.r[1] = (int)Buffer;
	SWIRegs.r[2] = Length;
	if (SWIX(Territory_NumberToName) != NULL) { *Buffer = 0; }
}

int GetCountryNumber(void) {
	SWIRegs.r[0] = 70;  // Read/Write Country (0x46)
	SWIRegs.r[1] = 127; // Read
	if (SWIX(OS_Byte) != NULL) { return(-1); }
	else                       { return(SWIRegs.r[1]); }
}


void GetCountryName(char *Buffer, int Length) {
	SWIRegs.r[0] = 70;  // Read/Write Country (0x46)
	SWIRegs.r[1] = 127; // Read
	if (SWIX(OS_Byte) != NULL) {
		*Buffer = 0;
		return;
	}

	SWIRegs.r[3] = SWIRegs.r[1];
	SWIRegs.r[1] = 67;  // Service International (0x43)
	SWIRegs.r[2] = 2;   // Number to Name
	SWIRegs.r[4] = (int)Buffer;
	SWIRegs.r[5] = Length;
	if (SWIX(OS_ServiceCall) != NULL) { *Buffer = 0; }
}

int IsWimpScrap(char *p) {
	char s[256];
	if (StringCompare((char*)(&WimpScrap),p) == 0) return (True);
	SWIRegs.r[0] = (int)(&WimpScrapVar);
	SWIRegs.r[1] = (int)(&s);
	SWIRegs.r[2] = 255;
	SWIRegs.r[3] = 0;
	SWIRegs.r[4] = 3;
	SWI(OS_ReadVarVal);
	return((StringCompare((char*)(&s),p) == 0));
}


void HourglassOn(void) {
	SWIX(Hourglass_On);
}

void HourglassOff(void) {
	SWIX(Hourglass_Off);
}

typedef void t_Directory_NextFileFunc(t_Directory_Entry *Entry);

int Directory_ScanSub(char *Path, int Level, t_Directory_Entry* Entry, t_OSGBPB10Block *Block, char *Wildcard, int Filetype, int MinAge, int MaxAge, int FilterType, int FilterDown, int MaxDepth, t_Directory_NextFileFunc Func) {

	int Next = 0;
	char *c;
	int age;

	//BuggyText("Scanning",Path);

	// Loop through the whole thing
	while (Next != -1) {

		// get next entry...
		SWIRegs.r[0] = 10;
		SWIRegs.r[1] = (int)(Path);
		SWIRegs.r[2] = (int)(Block);
		SWIRegs.r[3] = 1;
		SWIRegs.r[4] = Next;
		SWIRegs.r[5] = sizeof(t_OSGBPB10Block);
		SWIRegs.r[6] = 0;
		if (SWIX(OS_GBPB) != NULL) { Next = -1; return(False); }
		Next = SWIRegs.r[4];

		if (SWIRegs.r[3] == 0) { continue; }
		//BuggyText("Found",(char*)&(Block->Name));

		// Simple things
		Entry->Path = Path;
		Entry->Level = Level;
		Entry->Length = Block->Length;
		Entry->Name = (char*)&(Block->Name);


		// Enhance type of object
		Entry->Objecttype = Block->Objecttype;
		// Image flag
		if (Entry->Objecttype == (FilerMask_TypeFolder | FilerMask_TypeFile)) {
			Entry->Objecttype =  Entry->Objecttype | FilerMask_TypeImage;
		}
		// Application flag
		if ((Entry->Objecttype & FilerMask_TypeFolder) == FilerMask_TypeFolder) {
			if (Entry->Name[0] == '!') {
				Entry->Objecttype = (Entry->Objecttype & ~FilerMask_TypeFolder) | FilerMask_TypeApplication;
			}
		}

		// LoadExec to Filetype and Timestamp
		if ((Block->Load & LoadExecMask) == LoadExecMask) {
			Entry->Filetype = (Block->Load >> 8) & 0xFFF;
			Entry->Timestamp.Parts.High = (char)(Block->Load & 0xff);
			Entry->Timestamp.Parts.Low  = Block->Exec;
			}
		else {
			Entry->Filetype = -1;
			Entry->Timestamp.Parts.High = 0;
			Entry->Timestamp.Parts.Low  = 0;
			}

		// Get age - no
		// Entry->Age = Time_AgeDays(&(Entry->Timestamp,True));

		// Do not format timestamp
		// SWIRegs.r[0] = -1;
		// SWIRegs.r[1] = (int)((char*)(&(Entry->Timestamp)));
		// SWIRegs.r[2] = (int)(&(Entry->DateTime));
		// SWIX(Territory_ConvertTimeToOrdinals);

		// Check if we want to deliver this one...
		do {

			// Name I cope with x* only
			if (StringMatchStar(Entry->Name,Wildcard) != 0) { continue; }

			// Check Filetype - if no good or not a file get next entry
			if (Filetype >= 0) {
				if ((Entry->Filetype != Filetype) || (Entry->Objecttype & FilerMask_TypeFile) == 0) { continue; }
			}

			// Check Objecttype
			if ((Entry->Objecttype & FilterType) != 0) { continue; }

			// Check age
			if ((MinAge != -1) || (MaxAge != -1)) {
				age = Time_AgeDays(&(Entry->Timestamp),True);
				if ((MinAge != -1) && (age < MinAge)) { continue; }
				if ((MaxAge != -1) && (age > MaxAge)) { continue; }
			}

			// I got one... that I care about... so tell about it
			Func(Entry);
		} while(False);

		// Question arising is if I first delve deeper or not...
		if ((MaxDepth >= 0) && (Level > MaxDepth)) { continue; }
		if ((Entry->Objecttype & FilterDown) != 0) { continue; }

		// We delve deeper?
		if ((Entry->Objecttype == FilerMask_TypeFolder) || (Entry->Objecttype == FilerMask_TypeApplication)) {
			strcat(Path,".");
			strcat(Path,Entry->Name);
			// Off we dive
			Directory_ScanSub(Path, Level+1, Entry, Block, Wildcard, Filetype, MinAge, MaxAge, FilterType, FilterDown, MaxDepth, Func);
			// Come back up
			c = strrchr(Path,'.');
			*c = 0;
		}

	}
	return(True);
}

#define Directory_ScanFull(Path,Func) Directory_Scan(Path, NULL,-1,-1,-1,0,0,-1,Func)
#define Directory_ScanFlat(Path,Func) Directory_Scan(Path, NULL,-1,-1,-1,0,0,0,Func)

int Directory_Scan(char *Path, char *Wildcard, int Filetype, int MinAge, int MaxAge, int FilterType, int FilterDown, int MaxDepth, t_Directory_NextFileFunc Func) {
	t_Directory_Entry Entry;
	t_OSGBPB10Block Block;
	int r;
	HourglassOn();
	r = Directory_ScanSub(Path,0,&Entry,&Block,Wildcard,Filetype,MinAge,MaxAge,FilterType,FilterDown,MaxDepth,Func);
	HourglassOff();
	return(r);
}

t_Dynamic_Pointer_Array *Dynamic_Pointer_Array_Add(t_Dynamic_Pointer_Array *Master, void *Pointer) {
	#define fifty 50
	if (Master == NULL) {
		Master = malloc(sizeof(t_Dynamic_Pointer_Array)+sizeof(void*)*fifty);
		Master->Size = fifty;
		Master->Used = 0;
	}
	if (Master->Used == Master->Size) {
		Master->Size += fifty;
		Master = realloc(Master,sizeof(t_Dynamic_Pointer_Array)+sizeof(void*)*(Master->Size));
		if(Master == NULL) { SWIE("AddToArray malloc"); }
	}
	Master->Array[Master->Used++] = Pointer;
	return (Master);
}

t_Dynamic_Pointer_Array *Dynamic_Pointer_Array_Clear(t_Dynamic_Pointer_Array *Master) {
	int i;
	void *p;
	if (Master == NULL) { Master = Dynamic_Pointer_Array_Add(Master,NULL); }
	for (i=0;i<Master->Used;i++) {
		p = Master->Array[i];
		if (p != NULL) free(p);
	}
	Master->Used = 0;
	return(Master);
}


void AddToMyArray(t_Directory_Entry *Entry) {

	// Allocate next list entry (addind space to be able to append ".!RunImage" = 10
	t_Directory_ArrayEntry *e = malloc(sizeof(t_Directory_ArrayEntry) + strlen(Entry->Path) + 1 + strlen(Entry->Name) + 1 + 10);
	if(e == NULL) { SWIE("AddToMyList malloc"); }

	// Put that entry into the array
	MyArray = Dynamic_Pointer_Array_Add(MyArray,e);

	// Fill that entry
	strcpy(e->Path,Entry->Path);
	strcat(e->Path,".");
	strcat(e->Path,Entry->Name);
	e->Name = strrchr(e->Path,'.') +1;
	//BuggyText("Add",e->Path);

}


typedef int qsorter(const void *a, const void *b);

int SortMyArrayN(t_Directory_ArrayEntry **a, t_Directory_ArrayEntry **b) {
	int i;
	if ((i = StringCompare((*a)->Name,(*b)->Name)) != 0) return (i);
	return (StringCompare((char*)(&((*a)->Path)),(char*)(&((*b)->Path))));
}
int SortMyArrayF(t_Directory_ArrayEntry **a, t_Directory_ArrayEntry **b) {
	return (StringCompare((char*)(&((*a)->Path)),(char*)(&((*b)->Path))));
}

void SortMyArray(char SortFeld) {
	if (MyArray->Used < 2) { return; }
	switch (SortFeld) {
	case 'N':
		qsort(MyArray->Array,MyArray->Used,sizeof(void*),(qsorter*)SortMyArrayN);
		break;
	case 'P':
		qsort(MyArray->Array,MyArray->Used,sizeof(void*),(qsorter*)SortMyArrayF);
		break;
	}
}



void Shower(int IconClean,char *Offset) {

	int    i,h;
	t_Directory_ArrayEntry *eOld,*eNew;
	t_Directory_ArrayDelta *eDelta;
	
	// Delete all superflous Icons
	h = WimpGetWindowIconCount(MyMainWindowHandle);
	for (i=h;i>=IconClean;i--) {
		WimpDeleteWindowIcon(MyMainWindowHandle,i);
	}

	// Present the things wanted...
	h = 0;
	for (i=0;i< MyDelta->Used; i++)  {
		eDelta = MyDelta->Array[i];
		if ((StringCompare(eDelta->Sprite,MyIconSpriteOld)   == 0)
		 || (StringCompare(eDelta->Sprite,MyIconSpriteOlder) == 0)
		 || (StringCompare(eDelta->Sprite,MyIconSpriteNew)   == 0)
		 || (StringCompare(eDelta->Sprite,MyIconSpriteNewer) == 0)
		 || (StringCompare(eDelta->Sprite,MyIconSpriteSame)  == 0)
		 || (StringCompare(eDelta->Sprite,"")                == 0)) { 
			eOld = eDelta->Old;
			eNew = eDelta->New;
			eDelta->Icon = CreateSpriteIconXY(MyMainWindowHandle,0,eDelta->Sprite,MySprites,"S2",300,Offset,h*44,44,44);
			if (eOld != NULL) { 
				eOld->Icon = CreateTextIconXY(MyMainWindowHandle,IconFlags_TextL | (IconButton_ClickOnce<<IconFlags_Shift_Button),eOld->Name,IconValidation_Text,"S",0,Offset,h*44,300);
			}
			if (eNew != NULL) { 
				eNew->Icon = CreateTextIconXY(MyMainWindowHandle,IconFlags_TextL | (IconButton_ClickOnce<<IconFlags_Shift_Button),eNew->Name,IconValidation_Text,"S3",300+44,Offset,h*44,300);
			}
			h++;
		}
	}

	// Resize Work area to allow for h lines
	h = XY(Offset) + XY("S")+ h*44;

	// Refresh
	WimpResizeWindow(MyMainWindow,MyMainWindowHandle,0,h);

}

void Filter(int Icon) {

	t_Icon *IconBlock;
	char   *Sprite;
	
	IconBlock = &MyMainWindow->WindowIcon[Icon];
	Sprite    = IconBlock->IconData.IconSpriteIndirect.Sprite;
	*Sprite = *Sprite == '!' ? '_' : '!';
	WimpRedrawIcon(MyMainWindowHandle, (t_IconHandle)Icon);
}


// Get datestamp of !RunImage
t_Centiseconds LoaderSub(t_Directory_ArrayEntry *Entry) {
	t_OSTimestamp t;
	_kernel_oserror *e;
	char *c;

	// that space is there!
	strcat(Entry->Path,".!RunImage");
	SWIRegs.r[0] = 17;
	SWIRegs.r[1] = (int)(&(Entry->Path));
	e = SWIX(OS_File);
	c = strrchr(Entry->Path,'.');
	*c = 0;

	if (e != NULL) { return (t_Centiseconds)0; }
	if ((SWIRegs.r[2] & LoadExecMask) != LoadExecMask) { return (t_Centiseconds)0; }
	t.Parts.High = (SWIRegs.r[2] >> 8) & 0xFFF;
	t.Parts.Low = SWIRegs.r[3];

	return (Centiseconds(&t));
}


void Loader(char* Path) {

	char Line[4096];
	int i,iOld,iNew;
	t_Directory_ArrayEntry *eOld,*eNew;
	t_Directory_ArrayDelta *eDelta;
	t_Centiseconds tOld,tNew;
	
	t_Directory_ArrayEntry *e;
	t_Directory_ArrayDelta *d;
	char *cOld,*cNew,*c;
	char Nix[] = "nix";
	char b[4096];

	FILE *config;

	// Clear my arrays
	MyOldArray = Dynamic_Pointer_Array_Clear(MyOldArray);
	MyNewArray = Dynamic_Pointer_Array_Clear(MyNewArray);
	MyDelta    = Dynamic_Pointer_Array_Clear(MyDelta);

	// Load the old stuff into MyOldArray (i.e. do all in <AppUpdater$Dir>.Config file)
	MyArray = MyOldArray;
	config = fopen("<AppUpdater$Dir>.Config","r");
	if (config) {
		while (!feof(config)) {
			if (fgets((char*)(&Line),sizeof(Line)-1,config) != NULL) {
				// remove CR/LF or LF
				if ((c = strrchr(Line,0x0d)) != NULL) { *c = 0; }
				if ((c = strrchr(Line,0x0a)) != NULL) { *c = 0; }
				// ignore comments
				if (Line[0] == '#') { Line[0] = 0; }
				// work on non-empty lines
				if (strlen(Line) > 0) {
					Directory_Scan((char*)&Line,NULL,-1,-1,-1,~FilerMask_TypeApplication,~FilerMask_TypeFolder,-1,AddToMyArray);
				}
			}
		}
		fclose(config);
	}
	SortMyArray('N');
	MyOldArray = MyArray;

	if (Path != NULL) { strncpy(MyPath,Path,sizeof(MyPath)-1); }
	// I want apps only and do not go into apps

	// Load the stuff dragged here into MyNewArray
	MyArray = MyNewArray;
	Directory_Scan((char*)&MyPath,NULL,-1,-1,-1,~FilerMask_TypeApplication,~FilerMask_TypeFolder,-1,AddToMyArray);
	SortMyArray('N');
	MyNewArray = MyArray;


	iOld = 0; iNew = 0;
	while ((iOld < MyOldArray->Used) && (iNew < MyNewArray->Used)) {
		eOld = MyOldArray->Array[iOld];
		eNew = MyNewArray->Array[iNew];
		if ((eDelta = malloc(sizeof(t_Directory_ArrayDelta))) == NULL) { SWIE("malloc Loader"); }
		MyDelta = Dynamic_Pointer_Array_Add(MyDelta,eDelta);
		eDelta->Old = NULL;
		eDelta->New = NULL;
		//BuggyText("oM",eOld->Name);
		//BuggyText("nM",eNew->Name);

		i = StringCompare((char*)(eOld->Name),(char*)(eNew->Name));

		if (i <  0) { strcpy(eDelta->Sprite,SpriteOld); }
		if (i >  0) { strcpy(eDelta->Sprite,SpriteNew); }
		if (i == 0) { 
			tOld = LoaderSub(eOld);
			tNew = LoaderSub(eNew);
			if (tOld <  tNew) { strcpy(eDelta->Sprite,SpriteOlder); }
			if (tOld >  tNew) { strcpy(eDelta->Sprite,SpriteNewer); }
			if (tOld == tNew) { strcpy(eDelta->Sprite,SpriteSame); }
			if (tOld == 0)    { strcpy(eDelta->Sprite,SpriteToDo); }
			if (tNew == 0)    { strcpy(eDelta->Sprite,SpriteToDo); }
		}
		if (i <= 0) {
			eDelta->Old = eOld;
			iOld++;
		}
		if (i >= 0) {
			eDelta->New = eNew;
			iNew++;
		}
	}

	//BuggyInt("..",iOld);
	//BuggyInt("..",MyOldArray->Used);
	//BuggyInt("..",iNew);
	//BuggyInt("..",MyNewArray->Used);

	// Present the remaining old things
	for (;iOld< MyOldArray->Used; iOld++)  {
		//BuggyText("oR",eOld->Name);
		eOld = MyOldArray->Array[iOld];
		if ((eDelta = malloc(sizeof(t_Directory_ArrayDelta))) == NULL) { SWIE("malloc Loader"); }
		MyDelta = Dynamic_Pointer_Array_Add(MyDelta,eDelta);
		strcpy(eDelta->Sprite,SpriteOld);
		eDelta->Old = eOld;
		eDelta->New = NULL;
	}

	// Present the remaining new things
	for (;iNew< MyNewArray->Used; iNew++)  {
		eNew = MyNewArray->Array[iNew];
		//BuggyText("nR",eNew->Name);
		if ((eDelta = malloc(sizeof(t_Directory_ArrayDelta))) == NULL) { SWIE("malloc Loader"); }
		MyDelta = Dynamic_Pointer_Array_Add(MyDelta,eDelta);
		strcpy(eDelta->Sprite,SpriteNew);
		eDelta->Old = NULL;
		eDelta->New = eNew;
	}


//Buggy stuff
	sprintf(b,"New AAAAAAAA III TTT FFF .................... - Path"); BuggyMessage(b);
	for (i=0;i<MyNewArray->Used; i++)  {
		e = MyNewArray->Array[i];
		sprintf(b," %02d %08X %03d %20s - %s",i,(int)e,e->Icon,e->Name,e->Path);BuggyMessage(b);
	}
	sprintf(b,"Old AAAAAAAA III TTT FFF .................... - Path"); BuggyMessage(b);
	for (i=0;i<MyOldArray->Used; i++)  {
		e = MyOldArray->Array[i];
		sprintf(b," %02d %08X %03d %20s - %s",i,(int)e,e->Icon,e->Name,e->Path);BuggyMessage(b);

	}
	sprintf(b,"Dlt AAAAAAAA III AAAAAAAA .................... AAAAAAAA ...................."); BuggyMessage(b);
	for (i=0;i< MyDelta->Used; i++)  {
		d = MyDelta->Array[i];
		eOld = d->Old;
		eNew = d->New;
		cOld = eOld == NULL ? (char*)(&Nix) : eOld->Name;
		cNew = eNew == NULL ? (char*)(&Nix) : eNew->Name;
		sprintf(b," %02d %08X %03d %08X %20s %08X %20s",i,(int)d,d->Icon,(int)eOld,cOld,(int)eNew,cNew); BuggyMessage(b);
	}
//Buggey stuffed

}

void Opener(int Icon, int Button) {

	int i;
	t_Directory_ArrayEntry *e;
	char cmd[4096];
	switch (Button) {
	case MouseButtonLeft:
		strcpy(cmd,"*Filer_Opendir ");  break;
	case MouseButtonRight:
		strcpy(cmd,"*Filer_Closedir "); break;
	default:
		return;
	}

	// Click on old?
	for (i=0;i <MyOldArray->Used; i++)  {
		e = MyOldArray->Array[i];
		if (e->Icon == Icon) {
			strcat(cmd,e->Path);
			if (KeyPressCheck(KeyShift)) { strcat(cmd,".^"); }
			SWIRegs.r[0] = (int)(&cmd);
			SWIX(OS_CLI);
			return;
		}
	}
	// Click on new?
	for (i=0;i<MyNewArray->Used; i++)  {
		e = MyNewArray->Array[i];
		if (e->Icon == Icon) {
			strcat(cmd,e->Path);
			if (KeyPressCheck(KeyShift)) { strcat(cmd,".^"); }
			SWIRegs.r[0] = (int)(&cmd);
			SWIX(OS_CLI);
			return;
		}
	}

}



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

	int ReasonCode;

	BuggyClear;

	WimpInitialise();
	WimpAddRemoveMessages(MessageCode_DataLoad);

	MyBarIcon = WimpCreateIconBarIcon("Switcher",IconBar_IconRight);

	// Load Messages & Sprites
	MessagesLoad("AppUpdaterRes:Messages");
	MySprites = SpritesLoad("AppUpdaterRes:Sprites");


	// The classic infobox
	MyInfoWindow = CreateBasicWindow(">I_TITLE",WindowFlags_Info,6,20+120+20+300+20,20+44+20+44+20+44+20);
	FillTextIconXY(MyInfoWindow,0,IconFlags_TextR,">I_NAME",           IconValidation_Flat,"S",     0,"S",   0,120);
	FillTextIconXY(MyInfoWindow,1,IconFlags_TextC,(char*)MyTaskName,   IconValidation_Text,"S2+120",0,"S",   0,300);
	FillTextIconXY(MyInfoWindow,2,IconFlags_TextR,">I_AUTHOR",         IconValidation_Flat,"S",     0,"S2T", 0,120);
	FillTextIconXY(MyInfoWindow,3,IconFlags_TextC,"Herbert zur Nedden",IconValidation_Text,"S2+120",0,"S2T", 0,300);
	FillTextIconXY(MyInfoWindow,4,IconFlags_TextR,">I_VERSION",        IconValidation_Flat,"S",     0,"S3T2",0,120);
	FillTextIconXY(MyInfoWindow,5,IconFlags_TextC,(char*)MyVersion,    IconValidation_Text,"S2+120",0,"S3T2",0,300);

	// A simple menu with just Info and Quit
	MyBarMenu = CreateMenu(">M_TITLE",">M_INFO",">M_QUIT",NULL);

	MyBarMenu->MenuItem[0].MenuLink.Window  = WimpCreateWindow(MyInfoWindow);
	int MenuEntry_Quit = 1;


	#define IconLoad        0
	#define IconFilterOld   2
	#define IconFilterOlder 3
	#define IconFilterSame  4
	#define IconFilterNewer 5
	#define IconFilterNew   6
	

	// Window
	MyMainWindow = CreateBigWindow((char*)MyTaskName,WindowFlags_AllTools,9,20+300+20+44+20+300+20,200,0,0);
	FillButtonIconXY(MyMainWindow,IconLoad,">W_LOAD",IconValidation_Button,                                                           "S",     0,  "S",0,120,44);
	FillTextIconXY(MyMainWindow,1,IconFlags_TextL,">W_FILTER",IconValidation_Flat,                                                    "S2+140",0,   "S",0,0);
	FillSpriteIconXY(MyMainWindow,IconFilterOld,  IconButton_ClickOnce<<IconFlags_Shift_Button,(char*)(&MyIconSpriteOld),  MySprites, "S2+240",0*44,"S",0,44,44);
	FillSpriteIconXY(MyMainWindow,IconFilterOlder,IconButton_ClickOnce<<IconFlags_Shift_Button,(char*)(&MyIconSpriteOlder),MySprites, "S2+240",1*44,"S",0,44,44);
	FillSpriteIconXY(MyMainWindow,IconFilterSame, IconButton_ClickOnce<<IconFlags_Shift_Button,(char*)(&MyIconSpriteSame), MySprites, "S2+240",2*44,"S",0,44,44);
	FillSpriteIconXY(MyMainWindow,IconFilterNewer,IconButton_ClickOnce<<IconFlags_Shift_Button,(char*)(&MyIconSpriteNewer),MySprites, "S2+240",3*44,"S",0,44,44);
	FillSpriteIconXY(MyMainWindow,IconFilterNew,  IconButton_ClickOnce<<IconFlags_Shift_Button,(char*)(&MyIconSpriteNew),  MySprites, "S2+240",4*44,"S",0,44,44);
	FillTextIconXY(MyMainWindow,7,IconFlags_TextL,">W_OLD",IconValidation_Flat,                                                       "S", 0,       "STS",0,0);
	FillTextIconXY(MyMainWindow,8,IconFlags_TextL,">W_NEW",IconValidation_Flat,                                                       "S3",300+44,  "STS",0,0);

	int IconFree = 9;

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

		// Handle the request
		switch(ReasonCode) {
		// Mouse click
		case ReasonCode_MouseClick:
			//BuggyBuffI("Mouse",WimpPollBlock,12);
			// Iconbar icon
			if ((WimpPollBlock->MouseClick.WindowHandle == WindowIconbar) && (WimpPollBlock->MouseClick.IconHandle == MyBarIcon)) {
				switch(WimpPollBlock->MouseClick.Buttons) {
				case MouseButtonLeft:
					MyMainWindowHandle = WimpOpenNormalWindow(MyMainWindow,MyMainWindowHandle,True);
					break;
				case MouseButtonMiddle: // Menu click
					OpenMenu(MyBarMenu);
					break;
				case MouseButtonRight:
					WimpCloseWindow(MyMainWindowHandle);
				}
			}
			if (WimpPollBlock->MouseClick.WindowHandle == MyMainWindowHandle) {
				switch(WimpPollBlock->MouseClick.Buttons) {
				case MouseButtonMiddle: // Menu click
					break;
				default:
					switch(WimpPollBlock->MouseClick.IconHandle) {
					case IconLoad:
						Loader(NULL); 
						Shower(IconFree,"STSTS"); 
						break;
					case IconFilterOld:
					case IconFilterOlder:
					case IconFilterSame:
					case IconFilterNewer:
					case IconFilterNew:
						Filter(WimpPollBlock->MouseClick.IconHandle); 
						Shower(IconFree,"STSTS"); 
						break;
					default:
						if (WimpPollBlock->MouseClick.IconHandle >= IconFree) {
							Opener(WimpPollBlock->MouseClick.IconHandle,WimpPollBlock->MouseClick.Buttons);
						}
					}
				break;
				}
			}
			break;

		// Key press
		case ReasonCode_KeyPress:
			//BuggyBuffI("Key",WimpPollBlock,12);
			break;

		// Menu selection
		case ReasonCode_MenuSelection:
			if (WimpPollBlock->MenuSelection.MenuItem[0] == MenuEntry_Quit) {
				WimpCloseDown();
				break;
			}
			if (MouseButtonCheck(MouseButtonRight)) { OpenMenu(MyBarMenu); }
			break;

		// Window stuff
		case ReasonCode_OpenWindow:
			//BuggyMessage("ReasonCode_OpenWindow");
			if (WimpPollBlock->OpenWindow.WindowHandle == MyMainWindowHandle) {
				WimpOpenWindow();
			}
			else {
				WimpOpenWindow();
			}
			break;
		case ReasonCode_CloseWindow:
			//BuggyMessage("ReasonCode_CloseWindow");
			if (WimpPollBlock->OpenWindow.WindowHandle == MyMainWindowHandle) {
				WimpCloseWindow(MyMainWindowHandle);
			}
			else {
				WimpCloseWindow(Window_WimpBlock);
			}
			break;

		// User message
		case ReasonCode_UserMessage:
		case ReasonCode_UserMessageRecorded:
		case ReasonCode_UserMessageAcknowledge:
			switch(WimpPollBlock->UserMessage.MessageCode) {
			case MessageCode_Quit:
				 WimpCloseDown();
				 break;
			case MessageCode_DataLoad:
				MyMainWindowHandle = WimpOpenNormalWindow(MyMainWindow,MyMainWindowHandle,True);
				Loader(WimpPollBlock->UserMessage.MessageData.DataLoad.FileName);
				Shower(IconFree,"STSTS"); 
				WimpMessageReply(ReasonCode,MessageCode_DataLoadAck);
				break;
			}
		}
	}

}

