#define MAIN		//this tells global.h to declare globals, rather than making them externs

#include "global.h"

#include <libraries/asl.h>
#include <graphics/gfxmacros.h>
#include <intuition/gadgetclass.h>
#include <exec/memory.h>
#include <exec/nodes.h>
#include <exec/lists.h>

#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/asl.h>
#include <proto/exec.h>
#include <proto/gadtools.h>
#include <proto/layers.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define	SIZEBOX_X	8
#define	SIZEBOX_Y	6

Point		minRectSize = { 5, 5 },
			maxRectSize = { 1000, 1000 };

void main()
{
	IntuiMessage	*message;
	BOOL				done = FALSE;
	USHORT			code;
	Gadget			*gadgetPtr;
	ULONG				class;
	Point				mousePt;
	TimeStamp		timeStamp;
	
	
	init();
	
	while (!done)
	{
		Wait(1 << gWindPtr->UserPort->mp_SigBit);
		
		while (message = GT_GetIMsg( gWindPtr->UserPort ))
		{
			class = message->Class;
			code = message->Code;
			mousePt.x = message->MouseX;
			mousePt.y = message->MouseY;
			timeStamp.seconds = message->Seconds;
			timeStamp.micros = message->Micros;
			if (class == GADGETUP || class == GADGETDOWN)
				gadgetPtr = (Gadget *) message->IAddress;
			GT_ReplyIMsg(message);
			switch(class)
			{
				case IDCMP_MOUSEBUTTONS:
					DoMouse(&mousePt, code, &timeStamp);
					break;
				case IDCMP_GADGETUP:
					DoGadget(gadgetPtr, code);
					break;
				case IDCMP_MENUPICK:
					DoMenu(code);
					break;
				case IDCMP_CLOSEWINDOW:
					done = TRUE;
					break;
				case IDCMP_VANILLAKEY:
					DoKey(code);
					break;
				case IDCMP_RAWKEY:
					break;
				case IDCMP_NEWSIZE:
					SetClip();
					ReDraw();
					break;
			}
		}
		
	}

	quit("Normal Termination\n");
}

void DoGadget(Gadget *gadgetPtr, USHORT code)
{
	GadgetObj		*objPtr = gadgetPtr->UserData;
	
	if (!objPtr)
		return;
	
	switch(objPtr->type)
	{
		case OTYPE_Cycle:
//			GT_SetGadgetAttrs(gadgetPtr, gWindPtr, 0L,
//													GTCY_Active, ( (CycleObj *) objPtr)->tempValue,
//													TAG_DONE);
			break;
	}
}

/* ===================================================================================== */

void	DoKey(USHORT code)
{
	switch (code)
	{
		case KEYCODE_Delete:
		case KEYCODE_BackSpace:
			if (gSelObj)
			{
				DisposeObj(gSelObj);
				gSelObj = 0L;
				ReDraw();
			}
			break;
		default:
			printf("got key code %d\n", code);
			break;
	}
}

/* ===================================================================================== */

void	DoMouse(Point *mousePt, USHORT code, TimeStamp *timeStamp)
{
	LinkNode		*nodePtr,
					*oldSelObj = gSelObj;
	Rect			sizeRect;
	BOOL			changedFlag = FALSE;
	
	if (code != SELECTDOWN)
		return;
		
	//	Erase the current selection rectangle
	
	if (gSelObj)
		SelectObj(gSelObj);
	gSelObj = 0L;

	nodePtr = FindObj(mousePt);
	
	if (nodePtr)
	{
		sizeRect = nodePtr->rect;
		sizeRect.minX = sizeRect.maxX - SIZEBOX_X;
		sizeRect.minY = sizeRect.maxY - SIZEBOX_Y;
		
		// first, check for a double-click
		
		if (oldSelObj == nodePtr && DoubleClick(gSelTime.seconds, gSelTime.micros,
					timeStamp->seconds, timeStamp->micros))
		{
			HandleDoubleClick(nodePtr);
		}
			
		// second, check for a click in the size box
		
		else if (CanResize(nodePtr) && PtinRect( mousePt, &sizeRect ))
		{
			UpdateInfoItem(nodePtr);
			if (SizeRect(gWindPtr, mousePt, &nodePtr->rect, &minRectSize, &maxRectSize))
				changedFlag = TRUE; 
		}
			
		// otherwise, it's an ordinary drag operation
		
		else
		{
			UpdateInfoItem(nodePtr);
			if (DragRect(gWindPtr, mousePt, &nodePtr->rect))
				changedFlag = TRUE; 
		}
		
		gSelObj = nodePtr;
		gSelTime = *timeStamp;
	}
	
	//	Draw the selection rectangle
	
	if (changedFlag)
	{
		if (GetGadgetData(nodePtr, 0L, 0L, 0L, 0L, 0L))	// just verify that it's a gadget object
			MakeGadget( nodePtr );
		
		// if there are any current ListView connections to this gadget, update them
	
		UpdateLVConnect( nodePtr, FALSE );
	
		ReDraw();
	}
	else if (gSelObj)
	{
		SelectObj(gSelObj);
		UpdateInfoItem(gSelObj);
		UpdateInfoWindow(&gSelObj->rect);
	}
	else
	{
		UpdateInfoItem(0L);
		UpdateInfoWindow(0L);
	}

}

LinkNode *FindObj(Point *mousePt)
{
	LinkNode		*nodePtr;
	
	for (nodePtr=gObjList; nodePtr; nodePtr=nodePtr->next)
	{
		if (PtinRect( mousePt, &nodePtr->rect ))
		{
			return nodePtr;
		}
	}
	
	return 0L;
}

void PickLVString(USHORT code)
{
	USHORT	itemNum;
	LinkNode	*nodePtr;
	
	if (gSelObj && gSelObj->type == OTYPE_ListView)
	{
		printf("Enter the item number of the string gadget that you wish to connect:\n");
		scanf("%hd", &itemNum);
		nodePtr = FindNode(&gObjList, itemNum);
		if (nodePtr && nodePtr->type == OTYPE_String)
		{
			if (!UpdateLVConnect(nodePtr, TRUE))	// clear any current connections to this string
																// gadget
			{
				printf("Selection failed\n");
				return;
			}
			( (ListViewObj *) gSelObj)->stringObj = nodePtr;
			
			if (MakeGadget(gSelObj) != 0)
			{
				DisposeObj(gSelObj);
				gSelObj = 0L;
				printf("Selection failed\n");
			}
			else
				printf("Selection successful\n");
			ReDraw();
		}
		else if (nodePtr)
			printf("Error: the selected item is not a string gadget (operation aborted)\n");
		else
			printf("Error: invalid item number (operation aborted)\n");
	}
}

/* ===================================================================================== */

void SelectObj(LinkNode *selObj)
{
	RastPort		*rp = gWindPtr->RPort;
	Rect			tempRect = selObj->rect;
	
	if (!selObj)
		return;
	
	SetDrPt(rp, DASH);
	SetDrMd(rp, COMPLEMENT);
	
	Move(rp,	tempRect.minX,	tempRect.minY);
	Draw(rp,	tempRect.maxX,	tempRect.minY);
	Draw(rp,	tempRect.maxX,	tempRect.maxY);
	Draw(rp,	tempRect.minX,	tempRect.maxY);
	Draw(rp,	tempRect.minX,	tempRect.minY);
	
	if (CanResize(selObj))
	{
		tempRect.minX = tempRect.maxX - SIZEBOX_X;
		tempRect.minY = tempRect.maxY - SIZEBOX_Y;
		
		RectFill( rp, tempRect.minX, tempRect.minY, tempRect.maxX, tempRect.maxY );
	}
	
	SetDrMd(rp, JAM1);
	SetDrPt(rp, SOLID);
}

/* ===================================================================================== */

BOOL CanResize(LinkNode *objPtr)
{
	switch(objPtr->type)
	{
		case OTYPE_Rect:
		case OTYPE_Button:
		case OTYPE_Cycle:
		case OTYPE_String:
		case OTYPE_Palette:
		case OTYPE_ListView:
		case OTYPE_Scroller:
		case OTYPE_Text:
			return TRUE;
		default:
			return FALSE;
	}
}

/* ===================================================================================== */

void ReDraw()
{
	LinkNode		*nodePtr;
	RastPort		*rp = gWindPtr->RPort;
	Rect			*rectPtr;
	
	SetAPen(rp, grayPen);
	RectFill(rp,	gWindPtr->BorderLeft,
						gWindPtr->BorderTop,
						gWindPtr->Width - gWindPtr->BorderRight - 1,
						gWindPtr->Height - gWindPtr->BorderBottom - 1);

	for (nodePtr=gObjList; nodePtr; nodePtr=nodePtr->next)
	{
		rectPtr = &( (RectObj *) nodePtr)->rect;
		
		switch(nodePtr->type)
		{
			case OTYPE_Rect:
				SetAPen( rp, ( (RectObj *) nodePtr)->color );
				Move(rp, rectPtr->minX, rectPtr->minY);
				Draw(rp, rectPtr->maxX, rectPtr->minY);
				Draw(rp, rectPtr->maxX, rectPtr->maxY);
				Draw(rp, rectPtr->minX, rectPtr->maxY);
				Draw(rp, rectPtr->minX, rectPtr->minY);
				break;
			case OTYPE_IText:
				PrintIText(rp, &( (ITextObj *) nodePtr)->iText, rectPtr->minX, rectPtr->minY);
				break;
			case OTYPE_Button:
//				RefreshGList( ((ButtonObj *) nodePtr)->gadget, gWindPtr, NULL, 1);
				break;
			case OTYPE_String:
//				RefreshGList( ((StringObj *) nodePtr)->gadget, gWindPtr, NULL, 1);
				break;
	
		}
	}
	
	SetAPen(rp, blackPen);
	
	if (!gGadgetsOn)
	{
		AddGList(gWindPtr, gGadgetContext, -1L, -1L, 0L);
		gGadgetsOn = TRUE;
	}
		
	RefreshGList(gGadgetContext, gWindPtr, NULL, -1);
	GT_RefreshWindow(gWindPtr, NULL);
	
	if (gEditMode)
	{
		RemoveGList(gWindPtr, gGadgetContext, -1L);
		gGadgetsOn = FALSE;
	}

	if (gSelObj)
	{
		SelectObj(gSelObj);
		UpdateInfoItem(gSelObj);
		UpdateInfoWindow(&gSelObj->rect);
	}
	else
	{
		UpdateInfoItem(0L);
		UpdateInfoWindow(0L);
	}
}

/* ===================================================================================== */

void DoMenu(USHORT code)
{
	struct MenuItem	*itemPtr;
	void					(*fPtr)(USHORT);
	USHORT				itemNum;

	while (code != MENUNULL)
	{
		itemPtr = ItemAddress(gMenuPtr, code);
		fPtr = MENU_USERDATA(itemPtr);
		if (fPtr)
		{
			itemNum = ITEMNUM(code);
			fPtr(itemNum);
		}
		code = itemPtr->NextSelect;
	}
}

void About(USHORT code)
{
	printf("Maker version 0.1 (7/9/91)\n by John Champion\n\n");
}

/* ===================================================================================== */

void Quit(USHORT code)
{
	quit("Quit by User\n");
}

/* ===================================================================================== */

void HandleDoubleClick(LinkNode *objPtr)
{
	if (GetGadgetData( objPtr, 0L, 0L, 0L, 0L, 0L ) || objPtr->type == OTYPE_IText )
		DoubleIText(objPtr);
	else
		printf("got doubleclick!\n");
}

/* ===================================================================================== */

void DoWindSize(USHORT code)
{
	USHORT	left = gWindPtr->LeftEdge,
				top = gWindPtr->TopEdge,
				width = gWindPtr->Width,
				height = gWindPtr->Height;
	
	gWindSizeable = ! gWindSizeable;
	
	NewWindow(left, top, width, height, gWindSizeable);
	
	ReDraw();
}

BOOL NewWindow(USHORT left, USHORT top, USHORT width, USHORT height, BOOL sizeable)
{
	if (gWindPtr)
	{
		if (gGadgetsOn)
		{
			RemoveGList(gWindPtr, gGadgetContext, -1L);		// detach the gadget list
			gGadgetsOn = FALSE;
		}

		ClearMenuStrip(gWindPtr);
		CloseWindow(gWindPtr);
	}
	
	gWindPtr = 
			OpenWindowTags(0L,
								WA_Left,		left,
								WA_Top,		top,
								WA_Width,	width,
								WA_Height,	height,
								WA_IDCMP,	WIND_IDCMP,
								WA_Flags,	WIND_FLAG | (gWindSizeable ? WFLG_SIZEGADGET : 0),
								WA_Title,	gWindTitle,
								WA_PubScreen, gPubScreen,
								WA_MinWidth,	70,
								WA_MinHeight,	30,
								WA_MaxWidth,	-1L,
								WA_MaxHeight,	-1L,
								TAG_DONE);
								
	if (!gWindPtr)
		quit("failed to open a window\n");
	
	SetClip();
	SetFont(gWindPtr->RPort, gFont);
								
	SetMenuStrip(gWindPtr, gMenuPtr);

	if (!gEditMode)
	{
		AddGList(gWindPtr, gGadgetContext, -1L, -1L, 0L);		// reattach the gadgets
		gGadgetsOn = TRUE;
	}
	
	return TRUE;
}

void SetClip()
{
	struct Rectangle	tempRect;
	
	if (!gClipRegion)
		gClipRegion = NewRegion();

	if (!gClipRegion)
	{
		printf("failed to allocate a region!\n");
		return;
	}

	ClearRegion(gClipRegion);

	tempRect.MinX = gWindPtr->BorderLeft;
	tempRect.MinY = gWindPtr->BorderTop;
	tempRect.MaxX = gWindPtr->Width - gWindPtr->BorderRight - 1;
	tempRect.MaxY = gWindPtr->Height - gWindPtr->BorderBottom - 1;

	OrRectRegion(gClipRegion, &tempRect);

	InstallClipRegion(gWindPtr->WLayer, gClipRegion);
}

void DoEditMode(USHORT code)
{
	gEditMode = !gEditMode;
	
	if (!gEditMode)
		gSelObj = 0L;
	
	ReDraw();
}

void UpdateInfoItem(LinkNode *nodePtr)
{
	RastPort		*rp = gInfoWindPtr->RPort;
	char			buf[100];
	short			itemNum;
	
	if (nodePtr)
		itemNum = NodeNum( &gObjList, nodePtr );
	else
		itemNum = -1;
		
	if (itemNum >= 0)
	{
		sprintf(buf, "  Item: %3d", itemNum);
		Move(rp, 10, 20);
		Text(rp, buf, 11);
	}
	else
	{
		sprintf(buf, "  Item:    ");
		Move(rp, 10, 20);
		Text(rp, buf, 11);
	}
}
	
void UpdateInfoWindow(Rect *rectPtr)
{
	RastPort		*rp = gInfoWindPtr->RPort;
	char			buf[100];
	
	if (rectPtr)
	{
		sprintf(buf, "  Left: %3d", rectPtr->minX);
		Move(rp, 10, 30);
		Text(rp, buf, 11);
		
		sprintf(buf, "   Top: %3d", rectPtr->minY);
		Move(rp, 10, 40);
		Text(rp, buf, 11);
		
		sprintf(buf, " Width: %3d", RectWidth(*rectPtr));
		Move(rp, 10, 50);
		Text(rp, buf, 11);
		
		sprintf(buf, "Height: %3d", RectHeight(*rectPtr));
		Move(rp, 10, 60);
		Text(rp, buf, 11);
	}
	else
	{
		sprintf(buf, "  Left:    ");
		Move(rp, 10, 30);
		Text(rp, buf, 11);
		
		sprintf(buf, "   Top:    ");
		Move(rp, 10, 40);
		Text(rp, buf, 11);
		
		sprintf(buf, " Width:    ");
		Move(rp, 10, 50);
		Text(rp, buf, 11);
		
		sprintf(buf, "Height:    ");
		Move(rp, 10, 60);
		Text(rp, buf, 11);
	}
}