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

const char MyTaskName[] = "WinOne";
const char MyVersion[]  = "Version 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);
}

// 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;
	//BuggyBuff("Menu",MenuHeader,sizeof(t_MenuHeader));

	// 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));
		//BuggyBuff("MenuItem",MenuItem,sizeof(t_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

	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, 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 = WindowFlags_InfoWindow;
	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 = 0;
	WindowHeader->Workarea.MaxX = MaxX;
	WindowHeader->Workarea.MaxY = MaxY;
	WindowHeader->TitleIconFlags = IconFlags_TextWhiteIndirect;
	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;

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

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

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

	int ReasonCode;

	BuggyClear;

	WimpInitialise();

	MyBarIcon = WimpCreateIconBarIcon("Switcher",IconBar_IconRight);

	MyInfoWindow = CreateBasicWindow("Ego",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);

	//BuggyBuffI("InfoWindow",MyInfoWindow,sizeof(t_WindowHeader)/4);
	//BuggyBits("Flags",MyInfoWindow->WindowHeader.WindowFlags);
	//MyInfoWindow++;
	//BuggyBuffI("InfoIcons",MyInfoWindow,sizeof(t_Icon)/2);
	//MyInfoWindow--;

	MyBarMenu = CreateSimpleMenu((char*)MyTaskName,"Ego","Supen","Freten","Tsch",NULL);
	MyBarMenu->MenuItem[0].MenuLink.Window  = WimpCreateWindow(MyInfoWindow);
	MyBarMenu->MenuItem[1].MenuLink.SubMenu = CreateSimpleMenu("Supen","Gin-Tonic","Ralotto","O-Saft",NULL);
	MyBarMenu->MenuItem[2].MenuLink.SubMenu = CreateSimpleMenu("Freten","Erdnsse",NULL);

	while (True) {
		ReasonCode = WimpPoll();

		// Handle the request
		switch(ReasonCode) {
		// Mouse click
		case ReasonCode_MouseClick:
			if (WimpPollBlock->MouseClick.Icon == MyBarIcon) {
				switch(WimpPollBlock->MouseClick.Buttons) {
				case MouseButtonRight: // Kill my task - if Shift pressed
					if (KeyPressCheck(KeyShift)) { WimpCloseDown(); }
					break;
				case MouseButtonMiddle: // Menu click
					OpenMenu(MyBarMenu);
					break;
				case MouseButtonLeft:
					break;
				}
			}
			break;
		// Menu selection
		case ReasonCode_MenuSelection:
			//BuggyBuffI("Menu",WimpPollBlock,3);
			switch(WimpPollBlock->MenuSelection.MenuItem[0]) {
			case 0: // Ego
				break;
			case 1: // Supen
			case 2: // Freten
					if (WimpPollBlock->MenuSelection.MenuItem[1] != -1) {
						(MyBarMenu->MenuItem[WimpPollBlock->MenuSelection.MenuItem[0]].MenuLink.SubMenu)->MenuItem[WimpPollBlock->MenuSelection.MenuItem[1]].MenuFlags ^= MenuFlags_Mask_Tick;
					}
				break;
			case 3: // Quit
				WimpCloseDown();
				break;
			}
			if (MouseButtonCheck(MouseButtonRight)) { OpenMenu(MyBarMenu); }

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

}

