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

#include <string.h>

#pragma -
#include <pragma/utility_lib.h>
#include <clib/alib_protos.h>
#include <pragma/intuition_lib.h>
#include <pragma/graphics_lib.h>
#include <graphics/gfxmacros.h>
#pragma +

struct InstanceData {
	struct TextAttr *tattr;
	struct TextFont *font;
	UWORD imageadjust;
	UBYTE labelmark;
	Object *frame;
	struct LabelText *label;
};

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

ULONG IButtonClass::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->tattr = NULL;
				data->font = NULL;
				data->imageadjust = IBT_AdjustLeft;
				data->labelmark = '_';
				TagItemCursorC tic(((struct opSet *) msg)->ops_AttrList);
				while (!tic.isDone()) {
					/* register */ ULONG ticData = tic.itemData();
					switch (tic.itemTag())
					{
						case IBTA_ImageAdjust:
							data->imageadjust = (UWORD) ticData;
							break;
						case IBTA_Font:
							data->tattr = (struct TextAttr *) ticData;
							break;
						case IBTA_LabelMark:
							data->labelmark = (UBYTE) ticData;
							break;
						default:
							break;
					};
					tic.next();
				};
				if (data->tattr)
					data->font = OpenFont(data->tattr);
				data->frame = (Object *) NewObject(NULL,FRAMEICLASS,
					IA_EdgesOnly, FALSE,
					IA_FrameType, FRAME_BUTTON,
					IA_Recessed, FALSE,
					TAG_END);
				data->label = AllocLText(
					(STRPTR) ((struct Gadget *) object)->GadgetText,
					data->tattr, 1,0,JAM2,data->labelmark);
				retval = (ULONG) object;
			};
			break;
		};
		case OM_DISPOSE:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			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 IBTA_ImageAdjust:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->imageadjust;
					retval = TRUE;
					break;
				case IBTA_Font:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->font;
					retval = TRUE;
					break;
				case IBTA_LabelMark:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->labelmark;
					retval = TRUE;
					break;
				case IBTA_AutoWidth:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) labelWidth(cl,(struct Gadget *) o,data);
					retval = TRUE;
					break;
				case IBTA_AutoHeight:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) labelHeight(cl,(struct Gadget *) o,data);
					retval = TRUE;
					break;
				case IBTA_LeftBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) 0;
					retval = TRUE;
					break;
				case IBTA_RightBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) 0;
					retval = TRUE;
					break;
				case IBTA_TopBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) 0;
					retval = TRUE;
					break;
				case IBTA_BottomBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) 0;
					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_Text:
						ChangeLText(data->label,(STRPTR) ticData,data->labelmark);
						break;
					case IBTA_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);
						break;
					default:
						break;
				};
				tic.next();
			};
			retval = DoSuperMethodA(cl,o,msg);
			if (renderIt)
				retval = 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 = GMR_GADGETHIT;
			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 IButtonClass::render(Class *cl, struct Gadget *g,
	struct gpRender *msg, struct InstanceData *data)
{
	static UWORD ditherData[] = { 0x1111,0x4444 };

	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 TRUE;
	UWORD *pens = ginfo->gi_DrInfo->dri_Pens;
	if (msg->MethodID == GM_RENDER)
		rp = msg->gpr_RPort
	else
		rp = ObtainGIRPort(ginfo);
	if (rp)
	{
		ULONG state = g->Flags & GFLG_SELECTED ? IDS_SELECTED : IDS_NORMAL;
		if (data->frame)
		{
			SetAttrs(data->frame,
				IA_Left, g->LeftEdge,
				IA_Top, g->TopEdge,
				IA_Width, g->Width,
				IA_Height, g->Height,
				TAG_END);
			DrawImageState(rp,(struct Image *) data->frame,0,0,
				state, ginfo->gi_DrInfo);
		};
		WORD labelwidth = 0;
		BOOL textlabel = FALSE;
		if (data->font && g->GadgetText && (g->Flags & GFLG_LABELSTRING))
		{
			labelwidth = MeasureLText(data->label);
			textlabel = TRUE;
		};
		WORD imagewidth = 0;
		BOOL imagelabel = FALSE;
		WORD imagey = 0;
		if (g->GadgetRender && (g->Flags & GFLG_GADGIMAGE))
		{
			imagelabel = TRUE;
			if (((struct Image *) g->GadgetRender)->Depth == CUSTOMIMAGEDEPTH)
			{
				SetAttrs(g->GadgetRender,
					IA_Left,2,
					IA_Top,0,
					IA_Width,g->Height - 4,
					IA_Height,g->Height - 4,
					TAG_END);
				imagewidth = g->Height;
				imagey = g->TopEdge + 2;
			}
			else {
				imagewidth = ((struct Image *) g->GadgetRender)->Width + 4;
				imagey = g->TopEdge + (g->Height - ((struct Image *) g->GadgetRender)->Height) / 2;
			};
		};
		WORD left = g->LeftEdge + (g->Width - labelwidth - imagewidth) / 2;
		ChangeLTextPens(data->label,
			pens[state == IDS_SELECTED ? FILLTEXTPEN : TEXTPEN],
			pens[BACKGROUNDPEN],JAM1);
		switch (data->imageadjust)
		{
			case IBT_AdjustLeft:
				if (imagelabel)
				{
					DrawImageState(rp,((struct Image *) g->GadgetRender),left,imagey,
						state,ginfo->gi_DrInfo);
					left += imagewidth;
				};
				if (textlabel)
				{
					PrintLText(rp,data->label,
						left,g->TopEdge,labelwidth,g->Height);
				};
				break;
			case IBT_AdjustRight:
				if (imagelabel)
				{
					DrawImageState(rp,((struct Image *) g->GadgetRender),
						left + labelwidth,imagey,state,ginfo->gi_DrInfo);
				};
				if (textlabel)
				{
					PrintLText(rp,data->label,
						left,g->TopEdge,labelwidth,g->Height);
				};
				break;
		};
		if (g->Flags & GFLG_DISABLED)
		{
			SetDrMd(rp,JAM1);
			SetAPen(rp,pens[TEXTPEN]);
			SetAfPt(rp,ditherData,1);
			RectFill(rp,g->LeftEdge, g->TopEdge,
				g->LeftEdge + g->Width - 1, g->TopEdge + g->Height - 1);
			SetAfPt(rp,NULL,0);
		};
		if (msg->MethodID != GM_RENDER)
			ReleaseGIRPort(rp);
		return TRUE;
	};
	return FALSE;
}

WORD IButtonClass::labelWidth(Class *cl, struct Gadget *g,
		struct InstanceData *data)
{
	WORD labelwidth = 0;
	if (data->label)
	{
		labelwidth = MeasureLText(data->label);
	};
	if (g->GadgetRender && (g->Flags & GFLG_GADGIMAGE))
	{
		if (data->tattr && ((struct Image *) g->GadgetRender)->Depth == CUSTOMIMAGEDEPTH)
		{
			labelwidth += data->tattr->ta_YSize - 4
		}
		else {
			labelwidth += ((struct Image *) g->GadgetRender)->Width + 4;
		};
	};
	return labelwidth + 10;
}

WORD IButtonClass::labelHeight(Class *cl, struct Gadget *g,
		struct InstanceData *data)
{
	WORD labelheight = 0;
	if (data->tattr && g->GadgetText && (g->Flags & GFLG_LABELSTRING))
	{
		labelheight = data->tattr->ta_YSize;
	};
	if (g->GadgetRender && (g->Flags & GFLG_GADGIMAGE))
	{
		WORD iheight;
		if (((struct Image *) g->GadgetRender)->Depth == CUSTOMIMAGEDEPTH)
		{
			iheight = labelheight;
		}
		else {
			iheight = ((struct Image *) g->GadgetRender)->Height;
		};
		if (iheight > labelheight)
			labelheight = iheight;
	};
	return labelheight + 6;
}

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

IButtonClass BIButtonC::ibc;

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

