#include <stdio.h>
#include <stdlib.h>
#include <string.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;
t_WimpPollBlock WimpPollBlockStorage;
t_WimpPollBlock *WimpPollBlock;
t_TaskHandle MyTaskHandle = 0;
int MyCounter = 0;

const char MyTaskName[] = "FreedUp";
const char MyVersion[]  = "Version 1.00";
t_WindowHandle MyMainWindowHandle = -1;
char RedrawKind = 'P';

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

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

	// 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
	if (AbortMessage == NULL) {
		BuggyError("Oops");
		}
	else {
		BuggyError(AbortMessage);
		SWIRegs.r[0] = (int)OSError;
		SWIRegs.r[1] = 1;
		SWIRegs.r[2] = (int)AbortMessage;
		_kernel_swi(Wimp_ReportError,&SWIRegs,&SWIRegs);
		WimpCloseDown();
	}
	return(OSError);
}


// 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,"Wimp_Initialise");
	MyTaskHandle = SWIRegs.r[1];
}

// Function for SWI Wimp_Poll
int WimpPoll(void) {
	SWIRegs.r[0] = WimpPollMask;
	SWIRegs.r[1] = (int)&WimpPollBlockStorage;
	SWI(Wimp_Poll,"Wimp_Poll");
	// 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 put Icon on icon bar with SWI Wimp_CreateIcon
t_IconHandle WimpCreateIconBarIcon(char *Sprite, t_WindowHandle LeftRight) {
	t_WimpCreateIconBlock WimpCreateIconBlock;
	WimpCreateIconBlock.Window = LeftRight;
	WimpCreateIconBlock.Icon.BoundingBox.MinX = 0;
	WimpCreateIconBlock.Icon.BoundingBox.MinY = 0;
	WimpCreateIconBlock.Icon.BoundingBox.MaxX = 69;
	WimpCreateIconBlock.Icon.BoundingBox.MaxY = 68;
	WimpCreateIconBlock.Icon.IconFlags = IconFlags_IconBar;
	WimpCreateIconBlock.Icon.IconData.IconSpriteIndirect.Sprite = Sprite;
	WimpCreateIconBlock.Icon.IconData.IconSpriteIndirect.Area = WimpSpriteArea;
	WimpCreateIconBlock.Icon.IconData.IconSpriteIndirect.Length = strlen(Sprite);
	SWIRegs.r[0] = 0;
	SWIRegs.r[1] = (int)&WimpCreateIconBlock;
	SWI(Wimp_CreateIcon,"Wimp_CreateIcon");
	return(SWIRegs.r[0]);
}

// Function to remove Icon  from icon bar with SWI Wimp_DeleteIcon
void WimpDeleteIconBarIcon(t_IconHandle Icon) {
	t_WimpDeleteIconBlock WimpDeleteIconBlock;
	WimpDeleteIconBlock.Window = WindowIconbar;
	WimpDeleteIconBlock.Icon = Icon;
	SWIRegs.r[0] = 0;
	SWIRegs.r[1] = (int)&WimpDeleteIconBlock;
	SWI(Wimp_DeleteIcon,"Wimp_DeleteIcon");
}

// 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,"OS_Byte 129");
	return((SWIRegs.r[1] == 0xff));
}

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

// Function to create a simple menu
t_Menu *CreateSimpleMenu(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) { SWI(-1,"Menu malloc"); }

	// Fill Header
	strncpy(MenuHeader->Title.Text,Title,sizeof(MenuHeader->Title));
	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;
		MenuItem->IconFlags = IconFlags_TextWhiteDirect;
		strncpy(MenuItem->IconData.IconText,Entry,sizeof(MenuItem->IconData.IconText));
		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

	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.MouseX - 64;
	SWIRegs.r[3] = WimpPollBlock->MouseClick.MouseY - 8;
	if (WimpPollBlock->MouseClick.Window == 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,"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) { SWI(-1,"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_WindowTitleIndirect;
	WindowHeader->WorkareaButtonType = 0; // Ignore all clicks
	WindowHeader->ToolSpriteArea = WimpSpriteArea;
	WindowHeader->MinArea = 0;
	WindowHeader->TitleIconData.IconTextIndirect.Text = Title;
	WindowHeader->TitleIconData.IconTextIndirect.Validation = NULL;
	WindowHeader->TitleIconData.IconTextIndirect.Length = strlen(Title);
	WindowHeader->IconCount = Icons;

	//BuggyBuffI(Title,WindowHeader,sizeof(t_WindowHeader)/4);
	//BuggyBits(Title,WindowHeader->WindowFlags);
	//BuggyBits(Title,WindowHeader->TitleIconFlags);
	return((t_Window*)WindowHeader);
}

// 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...
	WindowHeader->Visible.MaxX = VisX;
	WindowHeader->Visible.MaxY = VisY;
	return((t_Window*)WindowHeader);
}


// Fill text icon
void FillTextIcon(t_Icon *Icon, char* Text, int White, int X, int Y, int Width, int Height) {
	Icon->BoundingBox.MinX = X;
	Icon->BoundingBox.MinY = -Y;
	Icon->BoundingBox.MaxX = X+Width;
	Icon->BoundingBox.MaxY = -Y-Height;
	if (White) { Icon->IconFlags = IconFlags_TextWhiteIndirect; }
	else       { Icon->IconFlags = IconFlags_TextTransparentIndirect; }
	Icon->IconData.IconTextIndirect.Text = Text;
	Icon->IconData.IconTextIndirect.Validation = NULL;
	Icon->IconData.IconTextIndirect.Length = strlen(Text);
}

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

// Wimp close window
void WimpCloseWindow(void) {
	SWIRegs.r[1] = (int)(WimpPollBlock);
	SWI(Wimp_CloseWindow,"Wimp_CloseWindow");
}

// Wimp open window
void WimpOpenWindow(void) {
	SWIRegs.r[1] = (int)(WimpPollBlock);
	//BuggyBuffI("Open",&(WimpPollBlock->OpenWindow.Visible),sizeof(t_WindowHeader)/4);
	SWI(Wimp_OpenWindow,"Wimp_OpenWindow");
}

void RedrawContent(t_WindowHandle Window, int X, int Y) {
	char B1[] = "Fangen wir mal langsam an";
	char B2[] = "...und machen weiter";
	char B3[] = "Hey, mal eine superlange Zeile, warum auch nicht, schlielich will ich was sehen, auch wenn es inhaltlich nicht besonders informativ ist, aber dafr viel Zeit kostet, bis man es gelesen hat, wenn man denn meint, man msse das tun, mit dem Lesen";
	char B4[] = "Noch 'ne Zeile";
	char B5[] = "Nu kommt eine Leerzeile";
	char B6[] = "";
	char B7[] = "Das langt!";

	BuggyInt("X",X);
	BuggyInt("Y",Y);
	if ((Window == MyMainWindowHandle) & (RedrawKind == 'F')) {
		SWIRegs.r[0] =  0; //Font Handle;
		SWIRegs.r[1] = (int)(&B1);
		SWIRegs.r[2] = 0x10;  // OS Units
		SWIRegs.r[3] = X;
		SWIRegs.r[4] = Y;
		SWIRegs.r[5] = 0;
		SWIRegs.r[6] = 0;
		SWIRegs.r[7] = 0;

		SWIRegs.r[4] -= 44;
		SWIRegs.r[1] = (int)(&B1);
		SWI(Font_Paint,"Font_Paint");
		SWIRegs.r[4] -= 44;
		SWIRegs.r[1] = (int)(&B2);
		SWI(Font_Paint,"Font_Paint");
		SWIRegs.r[4] -= 44;
		SWIRegs.r[1] = (int)(&B3);
		SWI(Font_Paint,"Font_Paint");
		SWIRegs.r[4] -= 44;
		SWIRegs.r[1] = (int)(&B4);
		SWI(Font_Paint,"Font_Paint");
		SWIRegs.r[4] -= 44;
		SWIRegs.r[1] = (int)(&B5);
		SWI(Font_Paint,"Font_Paint");
		SWIRegs.r[4] -= 44;
		SWIRegs.r[1] = (int)(&B6);
		SWI(Font_Paint,"Font_Paint");
		SWIRegs.r[4] -= 44;
		SWIRegs.r[1] = (int)(&B7);
		SWI(Font_Paint,"Font_Paint");
	} 
	if ((Window == MyMainWindowHandle) & (RedrawKind == 'P'))  {
		SWIRegs.r[0] = 4;
		SWIRegs.r[1] = X;
		SWIRegs.r[2] = Y-0*44;
		SWI(OS_Plot,"OS_Plot");
		printf("%s\n",B1);
		SWIRegs.r[0] = 196;
		SWIRegs.r[1] = X;
		SWIRegs.r[2] = Y-1*44;
		SWI(OS_Plot,"OS_Plot");
		printf("%s\n",B2);
		SWIRegs.r[0] = 196;
		SWIRegs.r[1] = X;
		SWIRegs.r[2] = Y-2*44;
		SWI(OS_Plot,"OS_Plot");
		printf("%s\n",B3);
		SWIRegs.r[0] = 196;
		SWIRegs.r[1] = X;
		SWIRegs.r[2] = Y-3*44;
		SWI(OS_Plot,"OS_Plot");
		printf("%s\n",B4);
		SWIRegs.r[0] = 196;
		SWIRegs.r[1] = X;
		SWIRegs.r[2] = Y-4*44;
		SWI(OS_Plot,"OS_Plot");
		printf("%s\n",B5);
		SWIRegs.r[0] = 196;
		SWIRegs.r[1] = X;
		SWIRegs.r[2] = Y-5*44;
		SWI(OS_Plot,"OS_Plot");
		printf("%s\n",B6);
		SWIRegs.r[0] = 196;
		SWIRegs.r[1] = X;
		SWIRegs.r[2] = Y-6*44;
		SWI(OS_Plot,"OS_Plot");
		printf("%s\n",B7);
	}

}

// Wimp redraw window
void WimpRedrawWindow(void) {
	int Looping;
	int OriginX, OriginY;
	SWIRegs.r[1] = (int)(WimpPollBlock);
	SWI(Wimp_RedrawWindow,"Wimp_RedrawWindow");
	// Note that Wimp_RedrawWindow implicitly calls the first Wimp_GetRectangle for me...
	Looping = SWIRegs.r[0];
	while(Looping != 0) {
		//BuggyBuffI("Rectangle",WimpPollBlock,7);
		// Get On-Screen position of top left corner of my work area (MinX,MaxY)
		OriginX = WimpPollBlock->RedrawWindow.Visible.MinX - WimpPollBlock->RedrawWindow.ScrollX;
		OriginY = WimpPollBlock->RedrawWindow.Visible.MaxY - WimpPollBlock->RedrawWindow.ScrollY;
		RedrawContent(WimpPollBlock->RedrawWindow.WindowHandle,OriginX,OriginY);
		SWIRegs.r[1] = (int)(WimpPollBlock);
		SWI(Wimp_GetRectangle,"Wimp_GetRectangle");
		Looping = SWIRegs.r[0];
	}
}

// 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,"OS_ReadModeVariable"); X = ++(SWIRegs.r[2]);
	SWIRegs.r[1] = 12; SWI(OS_ReadModeVariable,"OS_ReadModeVariable"); Y = ++(SWIRegs.r[2]);
	//BuggyInt("X",X); BuggyInt("Y",Y);
	// Shift resolution by Eigfactor
	SWIRegs.r[1] = 4; SWI(OS_ReadModeVariable,"OS_ReadModeVariable"); X = X * (++(SWIRegs.r[2]));
	SWIRegs.r[1] = 5; SWI(OS_ReadModeVariable,"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;
	//BuggyBuffI("Center",Box,4);

}

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

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

	if (Center) { MoveToCenter(&(WimpPollBlockStorage.OpenWindow.Visible)); }

	SWIRegs.r[1] = (int)(&WimpPollBlockStorage);
	SWI(Wimp_OpenWindow,"Wimp_OpenWindow");

	return(WimpPollBlockStorage.OpenWindow.WindowHandle);
}

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

	t_IconHandle MyBarIcon = 0;
	t_Menu *MyBarMenu;
	t_Window *MyInfoWindow;
	t_Window *MyMainWindow;

	int ReasonCode;

	BuggyClear;

	WimpInitialise();

	MyBarIcon = WimpCreateIconBarIcon("Switcher",IconBar_IconRight);

	// The classic infobox
	MyInfoWindow = CreateBasicWindow("Ego",WindowFlags_InfoWindow,2,323,20+44+20+44+20);
	FillTextIcon(&(MyInfoWindow->WindowIcon[0]),(char*)MyTaskName,False,20,20+44+20,323-20-20,44);
	FillTextIcon(&(MyInfoWindow->WindowIcon[1]),(char*)MyVersion,False, 20,20,323-20-20,44);

	// A simple menu with just Info and Quit
	MyBarMenu = CreateSimpleMenu((char*)MyTaskName,"Ego","Tsch",NULL);
	MyBarMenu->MenuItem[0].MenuLink.Window  = WimpCreateWindow(MyInfoWindow);

	// I need a big window... with no icons...
	MyMainWindow = CreateBigWindow((char*)MyTaskName,WindowFlags_AllToolsWindowRedraw,0,1200,1000,800,400);

	char Bla[] = "This is a nice string";
	MyMainWindow->WindowIcon[4].IconData.IconTextIndirect.Text = (char*)(&Bla);

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

		// Handle the request
		switch(ReasonCode) {
		// Mouse click
		case ReasonCode_MouseClick:
			//BuggyBuffI("MainWindow",MyMainWindow,sizeof(t_WindowHeader)/4);
			if (WimpPollBlock->MouseClick.Icon == MyBarIcon) {
				switch(WimpPollBlock->MouseClick.Buttons) {
				case MouseButtonRight:
					RedrawKind = 'F';
					MyMainWindowHandle = WimpOpenNewWindow(MyMainWindow,MyMainWindowHandle,True);
					break;
				case MouseButtonMiddle: // Menu click
					OpenMenu(MyBarMenu);
					break;
				case MouseButtonLeft:
					RedrawKind = 'P';
					MyMainWindowHandle = WimpOpenNewWindow(MyMainWindow,MyMainWindowHandle,True);
					break;
				}
			}
			break;
		// Menu selection
		case ReasonCode_MenuSelection:
			switch(WimpPollBlock->MenuSelection.MenuItem[0]) {
			case 0: // Ego
				break;
			case 1: // Quit
				WimpCloseDown();
				break;
			}
			if (MouseButtonCheck(MouseButtonRight)) { OpenMenu(MyBarMenu); }

		// Window stuff
		case ReasonCode_RedrawWindow:
			WimpRedrawWindow();
			break;
		case ReasonCode_OpenWindow:
			WimpOpenWindow();
			break;
		case ReasonCode_CloseWindow:
			WimpCloseWindow();
			break;

		// User message
		case ReasonCode_UserMessage:
		case ReasonCode_UserMessageRecorded:
			if (WimpPollBlock->UserMessage.MessageCode == UserMessage_Quit) {
				 WimpCloseDown();
			}
			break;
		}
	}

}

