#include <classes/BOOPSI/gadgets.h>
#include <classes/BOOPSI/boopsilib.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 +

#include <tools/str.h>
#include <stdio.h>

struct InstanceData {
	InstanceData() { };
	struct TextAttr *tattr;
	struct TextFont *font;
	String text;
	LONG number;
	BOOL isnumber;
	ULONG adjust;
	ULONG labelplace;
	Object *frame;
	WORD leftborder;
	WORD rightborder;
	WORD topborder;
	WORD bottomborder;
	ULONG columns;
};

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

ULONG LabelClass::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->text = "";
				data->number = 0;
				data->adjust = LB_AdjustLeft;
				data->labelplace = LB_LabelLeft;
				data->columns = 0;
				data->isnumber = FALSE;
				BOOL oborder = TRUE;
				TagItemCursorC tic(((struct opSet *) msg)->ops_AttrList);
				while (!tic.isDone()) {
					/* register */ ULONG ticData = tic.itemData();
					switch (tic.itemTag())
					{
						case LBA_Font:
							data->tattr = (struct TextAttr *) ticData;
							break;
						case LBA_Text:
							data->text = (STRPTR) ticData;
							data->isnumber = FALSE;
							break;
						case LBA_Number:
							data->number = (ULONG) ticData;
							data->isnumber = TRUE;
							break;
						case LBA_LabelPlace:
							data->labelplace = (ULONG) ticData;
							break;
						case LBA_Adjust:
							data->adjust = (ULONG) ticData;
							break;
						case LBA_Border:
							oborder = (BOOL) ticData;
							break;
						case LBA_Columns:
							data->columns = (ULONG) ticData;
							break;
						default:
							break;
					};
					tic.next();
				};
				if (oborder)
				{
					data->frame = (Object *) NewObject(NULL,FRAMEICLASS,
						IA_Recessed, TRUE,
						IA_EdgesOnly, FALSE,
						IA_FrameType, FRAME_DEFAULT,
						TAG_END);
				};
				if (data->tattr)
					data->font = OpenFont(data->tattr);
				calcBorders(cl,(struct Gadget *) object, data);
				retval = (ULONG) object;
			};
			break;
		};
		case OM_DISPOSE:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			if (data->font)
				CloseFont(data->font);
			if (data->frame)
				DisposeObject(data->frame);
			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 LBA_Font:
 					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->font;
					retval = TRUE;
					break;
				case LBA_Text:
					*(((struct opGet *) msg)->opg_Storage) =
						(ULONG) (char *) data->text;
					retval = TRUE;
					break;
				case LBA_Number:
 					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->number;
					retval = TRUE;
					break;
				case LBA_LabelPlace:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->labelplace;
					retval = TRUE;
					break;
				case LBA_Adjust:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->adjust;
					retval = TRUE;
					break;
				case LBA_Border:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) (data->frame != NULL);
					retval = TRUE;
					break;
				case LBA_LeftBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->leftborder;
					retval = TRUE;
					break;
				case LBA_RightBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->rightborder;
					retval = TRUE;
					break;
				case LBA_TopBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->topborder;
					retval = TRUE;
					break;
				case LBA_BottomBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->bottomborder;
					retval = TRUE;
					break;
				case LBA_AutoWidth:
					*(((struct opGet *) msg)->opg_Storage) =
						(ULONG) calcWidth(cl,(struct Gadget *) o, data);
					retval = TRUE;
					break;
				case LBA_AutoHeight:
					*(((struct opGet *) msg)->opg_Storage) =
						(ULONG) calcHeight(cl,(struct Gadget *) o, data);
					retval = TRUE;
					break;
				case LBA_Columns:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->columns;
					retval = TRUE;
					break;
				default:
					retval = DoSuperMethodA(cl,o,msg);
					break;
			};
			break;
		};
		case OM_SET:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			BOOL redrawIt = FALSE;
			TagItemCursorC tic(((struct opSet *) msg)->ops_AttrList);
			while (!tic.isDone()) {
				/* register */ ULONG ticData = tic.itemData();
				switch (tic.itemTag())
				{
					case LBA_Font:
						if (data->font)
						{
							CloseFont(data->font);
							data->font = NULL;
						};
						data->tattr = (struct TextAttr *) ticData;
						if (data->tattr)
							data->font = OpenFont(data->tattr);
						calcBorders(cl,(struct Gadget *) o, data);
						break;
					case LBA_Text:
						data->text = (STRPTR) ticData;
						data->isnumber = FALSE;
						redrawIt = TRUE;
						break;
					case LBA_Number:
						data->number = (LONG) ticData;
						data->isnumber = TRUE;
						redrawIt = TRUE;
						break;
					case LBA_Adjust:
						data->adjust = (ULONG) ticData;
						break;
					case LBA_Columns:
						data->columns = (ULONG) ticData;
						break;
					default:
						break;
				};
				tic.next();
			};
			retval = DoSuperMethodA(cl,o,msg);
			if (redrawIt)
			{
				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;
		};
		default:
		{
			retval = DoSuperMethodA(cl,o,msg);
			break;
		};
	};
	return retval;
}

ULONG LabelClass::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)
	{
		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,IDS_NORMAL,
				ginfo->gi_DrInfo);
		}
		else {
			SetAPen(rp,pens[BACKGROUNDPEN]);
			SetDrMd(rp,JAM1);
			RectFill(rp, g->LeftEdge, g->TopEdge,
				g->LeftEdge + g->Width - 1, g->TopEdge + g->Height - 1);
		};
		SetAPen(rp,pens[TEXTPEN]);
		SetBPen(rp,pens[BACKGROUNDPEN]);
		SetDrMd(rp,JAM2);
		SetFont(rp,data->font);
		if (data->font)
		{
			if (g->GadgetText)
			{
				switch (data->labelplace)
				{
					case LB_LabelLeft:
						PrintClipped(rp,(STRPTR) g->GadgetText,
							g->LeftEdge - data->leftborder, g->TopEdge,
							data->leftborder,g->Height,
							STADJUST_Left);
						break;
					case LB_LabelRight:
						PrintClipped(rp,(STRPTR) g->GadgetText,
							g->LeftEdge + g->Width + 4, g->TopEdge,
							data->rightborder - 4, g->Height,
							STADJUST_Left);
						break;
				};
			};
			if (data->isnumber)
			{
				char buffer[80];
				sprintf(buffer,"%-ld",(int) data->number);
				data->text = (char *) buffer;
			};
			ULONG adjust = STADJUST_Left;
			switch (data->adjust)
			{
				case LB_AdjustCenter:
					adjust = STADJUST_Center;
					break;
				case LB_AdjustRight:
					adjust = STADJUST_Right;
					break;
			};
			PrintClipped(rp,(STRPTR) (char *) data->text,
				g->LeftEdge + 4, g->TopEdge, g->Width - 8, g->Height,
				adjust);
		};
		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;
}

VOID LabelClass::calcBorders(Class *cl, struct Gadget *g,
		struct InstanceData *data)
{
	data->leftborder = 0;
	data->rightborder = 0;
	data->topborder = 0;
	data->bottomborder = 0;
	if (data->tattr && g->GadgetText)
	{
		switch (data->labelplace)
		{
			case LB_LabelLeft:
				data->leftborder = MeasureTextLength((STRPTR) g->GadgetText,
					data->tattr) + 4;
				break;
			case LB_LabelRight:
				data->rightborder = MeasureTextLength((STRPTR) g->GadgetText,
					data->tattr) + 4;
				break;
		};
	};
}

WORD LabelClass::calcWidth(Class *cl, struct Gadget *g,
		struct InstanceData *data)
{
	if (data->tattr)
	{
		if (data->columns == 0)
		{
			if (data->isnumber && data->number != 0)
			{
				char buffer[80];
				sprintf(buffer,"%-ld",(int) data->number);
				return MeasureTextLength(buffer,data->tattr) + 8;
			}
			else if (!data->isnumber && !(data->text == String("")))
			{
				return MeasureTextLength(data->text,data->tattr) + 8;
			};
		};
		ULONG chars = 0;
		if (data->columns != 0)
			chars = data->columns
		else if (data->isnumber)
			chars = 7;
		else
			chars = 16;
		return MeasureTextLength("0",data->tattr)*chars + 8;
	};
	return 60;
}

WORD LabelClass::calcHeight(Class *cl, struct Gadget *g,
		struct InstanceData *data)
{
	if (data->tattr)
	{
		return data->tattr->ta_YSize + 6;
	};
	return 12;
}

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

LabelClass BTextC::lc;

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

BNumberC::BNumberC(GadgetEventC *ev, WindowC &w, Tag tag1type, ...)
		: BGadgetC(ev,lc.boopsiclass(),NULL,w,(struct TagItem *) &tag1type)
{
	set(get());
}

BNumberC::BNumberC(GadgetEventC *ev, WindowC &w, struct TagItem *tags)
		: BGadgetC(ev,lc.boopsiclass(),NULL,w,tags)
{
	set(get());
}

LabelClass BNumberC::lc;
