#include <classes/BOOPSI/gadgets.h>
#include <classes/BOOPSI/boopsilib.h>

#include <classes/BOOPSI/imageclass.h>
#include <exec/libraries.h>

#pragma -
#include <pragma/utility_lib.h>
#include <clib/alib_protos.h>
#include <pragma/intuition_lib.h>
#include <pragma/graphics_lib.h>
#include <pragma/layers_lib.h>
#include <pragma/icon_lib.h>
#pragma +

extern struct Library *IntuitionBase;

struct InstanceData {
	Object *frame;
	struct DiskObject *diskobject;
	BOOL dopriv;
	struct TextAttr *tattr;
	struct TextFont *font;
	WORD topborder;
	WORD autowidth;
	WORD autoheight;
	struct LabelText *label;
};

WBIconDropClass::WBIconDropClass()
	: BoopsiClass(sizeof(struct InstanceData),(ULONG (*)()) &dispatcher,GADGETCLASS,NULL)
{
}

ULONG WBIconDropClass::dispatcher(Class *cl, Object *o, Msg msg)
{
	struct InstanceData *data;
	ULONG retval = FALSE;
	switch (msg->MethodID)
	{
		case OM_NEW:
		{
			Object *object;
			if (object = (Object *) DoSuperMethodA(cl,o,msg))
			{
				data = (struct InstanceData *) INST_DATA(cl,object);
				data->diskobject = NULL;
				data->dopriv = FALSE;
				data->autowidth = 50;
				data->autoheight = 40;
				TagItemCursorC tic(((struct opSet *) msg)->ops_AttrList);
				while (!tic.isDone()) {
					/* register */ ULONG ticData = tic.itemData();
					switch (tic.itemTag())
					{
						case WBIDA_DiskObject:
							data->diskobject = (struct DiskObject *) ticData;
							data->dopriv = FALSE;
							break;
						case WBIDA_Name:
							data->diskobject = GetDiskObjectNew((STRPTR) ticData);
							data->dopriv = TRUE;
							break;
						case WBIDA_Font:
							data->tattr = (struct TextAttr *) ticData;
							break;
						default:
							break;
					};
					tic.next();
				};
				data->frame = (Object *) NewObject(NULL,FRAMEICLASS,
					IA_Left,((struct Gadget *) object)->LeftEdge,
					IA_Top,((struct Gadget *) object)->TopEdge,
					IA_Width,((struct Gadget *) object)->Width,
					IA_Height,((struct Gadget *) object)->Height,
					IA_EdgesOnly, FALSE,
					IA_FrameType, FRAME_ICONDROPBOX,
					IA_Recessed, IntuitionBase->lib_Version < 39,
					TAG_END);
				if (data->tattr)
				{
					data->font = OpenFont(data->tattr);
				};
				data->label = AllocLText(
					(STRPTR) ((struct Gadget *) object)->GadgetText,
					data->tattr,1,0,JAM2,0);
				data->topborder = calcTopBorder(cl,(struct Gadget *) object,data);
				struct DiskObject *dob = GetDefDiskObject(WBPROJECT);
				if (dob)
				{
					data->autowidth = dob->do_Gadget.Width + 24;
					data->autoheight = dob->do_Gadget.Height + 12;
					FreeDiskObject(dob);
				};
				retval = (ULONG) object;
			};
			break;
		};
		case OM_DISPOSE:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			if (data->dopriv && data->diskobject)
				FreeDiskObject(data->diskobject);
			if (data->frame)
				DisposeObject(data->frame);
			if (data->font)
				CloseFont(data->font);
			FreeLText(data->label);
			retval = DoSuperMethodA(cl,o,msg);
			break;
		};
		case OM_GET:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			switch (((struct opGet *) msg)->opg_AttrID)
			{
				case GA_Left:
					*(((struct opGet *) msg)->opg_Storage) = ((struct Gadget *) o)->LeftEdge;
					retval = TRUE;
					break;
				case GA_Top:
					*(((struct opGet *) msg)->opg_Storage) = ((struct Gadget *) o)->TopEdge;
					retval = TRUE;
					break;
				case GA_Width:
					*(((struct opGet *) msg)->opg_Storage) = ((struct Gadget *) o)->Width;
					retval = TRUE;
					break;
				case GA_Height:
					*(((struct opGet *) msg)->opg_Storage) = ((struct Gadget *) o)->Height;
					retval = TRUE;
					break;
				case WBIDA_DiskObject:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->diskobject;
					retval = TRUE;
					break;
				case WBIDA_Font:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->tattr;
					retval = TRUE;
					break;
				case WBIDA_LeftBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) 0;
					retval = TRUE;
					break;
				case WBIDA_RightBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) 0;
					retval = TRUE;
					break;
				case WBIDA_TopBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->topborder;
					retval = TRUE;
					break;
				case WBIDA_BottomBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) 0;
					retval = TRUE;
					break;
				case WBIDA_AutoWidth:
					*(((struct opGet *) msg)->opg_Storage) =
						(ULONG) calcWidth(cl,(struct Gadget *) o, data);
					retval = TRUE;
					break;
				case WBIDA_AutoHeight:
					*(((struct opGet *) msg)->opg_Storage) =
						(ULONG) calcHeight(cl,(struct Gadget *) o, data);
					retval = TRUE;
					break;
				default:
					retval = DoSuperMethodA(cl,o,msg);
					break;
			};
			break;
		};
		case OM_SET:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			BOOL renderIt = FALSE;
			TagItemCursorC tic(((struct opSet *) msg)->ops_AttrList);
			while (!tic.isDone()) {
				/* register */ ULONG ticData = tic.itemData();
				switch (tic.itemTag())
				{
					case GA_Top:
						if (data->frame)
							SetAttrs(data->frame,IA_Top,ticData,TAG_END);
						break;
					case GA_Left:
						if (data->frame)
							SetAttrs(data->frame,IA_Left,ticData,TAG_END);
						break;
					case GA_Width:
						if (data->frame)
							SetAttrs(data->frame,IA_Width,ticData,TAG_END);
						break;
					case GA_Height:
						if (data->frame)
							SetAttrs(data->frame,IA_Height,ticData,TAG_END);
						break;
					case WBIDA_DiskObject:
						if (data->dopriv && data->diskobject)
							FreeDiskObject(data->diskobject);
						data->diskobject = (struct DiskObject *) ticData;
						data->dopriv = FALSE;
						renderIt = TRUE;
						break;
					case WBIDA_Name:
						if (data->dopriv && data->diskobject)
							FreeDiskObject(data->diskobject);
						data->diskobject = GetDiskObjectNew((STRPTR) ticData);
						data->dopriv = TRUE;
						renderIt = TRUE;
						break;
					case WBIDA_Font:
						if (data->font)
						{
							CloseFont(data->font);
							data->font = NULL;
						};
						data->tattr = (struct TextAttr *) ticData;
						if (data->tattr)
						{
							data->font = OpenFont(data->tattr);
						};
						ChangeLTextFont(data->label,data->tattr);
						data->topborder = calcTopBorder(cl,(struct Gadget *) o,data);
						break;
					default:
						break;
				};
				tic.next();
			};
			retval = DoSuperMethodA(cl,o,msg);
			if (renderIt)
			{
				render(cl,(struct Gadget *) o, (struct gpRender *) msg,data);
			};
			break;
		};
		case GM_RENDER:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			retval = render(cl,(struct Gadget *) o,
				(struct gpRender *) msg,data);
			break;
		};
		case GM_HITTEST:
		{
			retval = 0;
			break;
		};
		case GM_GOACTIVE:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			if (((struct gpInput *) msg)->gpi_IEvent)
			{
				((struct Gadget *) msg)->Flags |= GFLG_SELECTED;
				render(cl,(struct Gadget *) o,
					(struct gpRender *) msg,data);
				retval = GMR_MEACTIVE;
			}
			else
				retval = GMR_NOREUSE;
			break;
		};
		case GM_GOINACTIVE:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			((struct Gadget *) o)->Flags &= ~GFLG_SELECTED;
			render(cl,(struct Gadget *) o,
				(struct gpRender *) msg,data);
			break;
		};
		case GM_HANDLEINPUT:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			struct gpInput *gpi = (struct gpInput *) msg;
			struct InputEvent *ie = gpi->gpi_IEvent;
			BOOL mouseOverGadget = ((gpi->gpi_Mouse.X >= 0) &&
					(gpi->gpi_Mouse.X < ((struct Gadget *) o)->Width) &&
					(gpi->gpi_Mouse.Y >= 0) &&
					(gpi->gpi_Mouse.Y < ((struct Gadget *) o)->Height));
			if (mouseOverGadget)
			{
				if (!(((struct Gadget *) o)->Flags & GFLG_SELECTED))
				{
					((struct Gadget *) o)->Flags |= GFLG_SELECTED;
					render(cl,(struct Gadget *) o,
						(struct gpRender *) msg,data);
				};
			};
			else {
				if (((struct Gadget *) o)->Flags & GFLG_SELECTED)
				{
					((struct Gadget *) o)->Flags &= ~GFLG_SELECTED;
					render(cl,(struct Gadget *) o,
						(struct gpRender *) msg,data);
				};
			};
			switch (ie->ie_Class)
			{
				case IECLASS_RAWMOUSE:
				{
					switch (ie->ie_Code)
					{
						case SELECTUP:
							if (mouseOverGadget)
							{
								retval = GMR_NOREUSE | GMR_VERIFY;
							}
							else {
								retval = GMR_NOREUSE;
							};
							break;
						case MENUDOWN:
							retval = GMR_REUSE;
							break;
						default:
							retval = GMR_MEACTIVE;
							break;
					};
					break;
				};
				default:
					retval = GMR_MEACTIVE;
					break;
			};
			break;
		};
		default:
		{
			retval = DoSuperMethodA(cl,o,msg);
			break;
		};
	};
	return retval;
}

ULONG WBIconDropClass::render(Class *cl, struct Gadget *g,
	struct gpRender *msg, struct InstanceData *data)
{
	struct RastPort *rp;
	struct GadgetInfo *ginfo;
	if (msg->MethodID == OM_SET || msg->MethodID == OM_UPDATE)
		ginfo = ((struct opSet *) msg)->ops_GInfo
	else
		ginfo = msg->gpr_GInfo;
	if (!ginfo)
		return FALSE;
	UWORD *pens = ginfo->gi_DrInfo->dri_Pens;
	if (msg->MethodID == GM_RENDER)
		rp = msg->gpr_RPort
	else
		rp = ObtainGIRPort(ginfo);
	if (rp)
	{
		if (data->frame)
		{
			DrawImageState(rp,(struct Image *) data->frame,
				0,0,IDS_NORMAL,ginfo->gi_DrInfo);
		};
		if (data->font && g->GadgetText && (g->Flags & GFLG_LABELSTRING))
		{
			PrintLText(rp,data->label,
				g->LeftEdge,g->TopEdge - data->topborder,g->Width,data->topborder,
				STADJUST_Center);
		};
		if (data->diskobject)
		{
			struct Image *im = (struct Image *) data->diskobject->do_Gadget.GadgetRender;
			if (im)
			{
				if ((im->Width > g->Width - 20) ||
					(im->Height > g->Height - 10))
				{
					struct Rectangle rectangle;
					struct Region *new_region = NewRegion();
					struct Region *old_region;
					if (new_region)
					{
						rectangle.MinX = g->LeftEdge + 10;
						rectangle.MinY = g->TopEdge + 5;
						rectangle.MaxX = g->LeftEdge + g->Width - 11;
						rectangle.MaxY = g->TopEdge + g->Height - 6;
						if (OrRectRegion(new_region,&rectangle))
						{
							old_region = InstallClipRegion(ginfo->gi_Window->WLayer,new_region);
							DrawImage(rp,im,
								g->LeftEdge + (g->Width - im->Width) / 2,
								g->TopEdge + (g->Height - im->Height) / 2);
							InstallClipRegion(ginfo->gi_Window->WLayer,old_region);
						};
						DisposeRegion(new_region);
					};
				}
				else {
					DrawImage(rp,im,
						g->LeftEdge + (g->Width - im->Width) / 2,
						g->TopEdge + (g->Height - im->Height) / 2);
				};
			};
		};
		if (msg->MethodID != GM_RENDER)
			ReleaseGIRPort(rp);
		return TRUE;
	};
	return FALSE;
}

LONG WBIconDropClass::calcWidth(Class *cl, struct Gadget *g,
	struct InstanceData *data)
{
	WORD labelwidth = MeasureLText(data->label);
	WORD dragwidth = data->autowidth;
	if (data->diskobject)
	{
		dragwidth = data->diskobject->do_Gadget.Width + 24;
	};
	if (labelwidth > dragwidth)
		return labelwidth;
	return dragwidth;
}

LONG WBIconDropClass::calcHeight(Class *cl, struct Gadget *g,
	struct InstanceData *data)
{
	WORD dragheight = data->autoheight;
	if (data->diskobject)
	{
		dragheight = data->diskobject->do_Gadget.Height + 12;
	};
	return dragheight;
}

WORD WBIconDropClass::calcTopBorder(Class *cl, struct Gadget *g,
	struct InstanceData *data)
{
	if (data->tattr && g->GadgetText && (g->Flags & GFLG_LABELSTRING))
		return data->tattr->ta_YSize + 4;
	return 0;
}

// *************************************************************

WBIconDropClass BWBIconDropC::idc;

// *************************************************************

IconDropHandlerC::IconDropHandlerC(AppWindowC &w, BWBIconDropC &g)
	: AppWindowHandlerC(w), aGadget(&g)
{
}

BOOL IconDropHandlerC::_forMe(AppMessageC &msg)
{
	if (AppWindowHandlerC::_forMe(msg))
	{
		BOOL b = (aGadget->left() <= msg.am_MouseX)
			&& (aGadget->left() + aGadget->width() > msg.am_MouseX)
			&& (aGadget->top() <= msg.am_MouseY)
			&& (aGadget->top() + aGadget->height() > msg.am_MouseY);
		return b;
	};
	return FALSE;
}

// *************************************************************
