#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>
#pragma +
#include <graphics/gfxmacros.h>
#include <string.h>

struct InstanceData {
	struct TextAttr *tattr;
	struct TextFont *font;
	ULONG labelplace;
	WORD leftborder;
	WORD rightborder;
	WORD topborder;
	WORD bottomborder;
	BOOL readonly;
	UBYTE labelmark;
	struct LabelText *label;
};

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

ULONG CheckboxClass::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->labelplace = CB_LabelRight;
				data->leftborder = 0;
				data->rightborder = 0;
				data->topborder = 0;
				data->bottomborder = 0;
				data->readonly = FALSE;
				data->labelmark = '_';
				TagItemCursorC tic(((struct opSet *) msg)->ops_AttrList);
				while (!tic.isDone()) {
					/* register */ ULONG ticData = tic.itemData();
					switch (tic.itemTag())
					{
						case CBA_Font:
							data->tattr = (struct TextAttr *) ticData;
							break;
						case CBA_LabelPlace:
							data->labelplace = (ULONG) ticData;
							break;
						case CBA_ReadOnly:
							data->readonly = (BOOL) ticData;
							break;
						case CBA_LabelMark:
							data->labelmark = (UBYTE) ticData;
							break;
						default:
							break;
					};
					tic.next();
				};
				if (data->tattr)
				{
					data->font = OpenFont(data->tattr);
				};
				data->label = AllocLText(
					(STRPTR) ((struct Gadget *) object)->GadgetText,
					data->tattr,1,0,JAM2,data->labelmark);
				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);
			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 GA_Selected:
					*(((struct opGet *) msg)->opg_Storage) =
						(ULONG) (((struct Gadget *) o)->Flags & GFLG_SELECTED) != 0;
					break;
				case CBA_Font:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->tattr;
					retval = TRUE;
					break;
				case CBA_LabelPlace:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->labelplace;
					retval = TRUE;
					break;
				case CBA_LeftBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->leftborder;
					retval = TRUE;
					break;
				case CBA_RightBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->rightborder;
					retval = TRUE;
					break;
				case CBA_TopBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->topborder;
					retval = TRUE;
					break;
				case CBA_BottomBorder:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->bottomborder;
					retval = TRUE;
					break;
				case CBA_AutoWidth:
					*(((struct opGet *) msg)->opg_Storage) =
						(ULONG) calcWidth(cl,(struct Gadget *) o, data);
					retval = TRUE;
					break;
				case CBA_AutoHeight:
					*(((struct opGet *) msg)->opg_Storage) =
						(ULONG) calcHeight(cl,(struct Gadget *) o, data);
					retval = TRUE;
					break;
				case CBA_ReadOnly:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->readonly;
					retval = TRUE;
					break;
				case CBA_LabelMark:
					*(((struct opGet *) msg)->opg_Storage) = (ULONG) data->labelmark;
					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 GA_Selected:
						redrawIt = TRUE;
						break;
					case CBA_Font:
						data->tattr = (struct TextAttr *) ticData;
						if (data->font)
						{
							CloseFont(data->font);
							data->font = NULL;
						};
						if (data->tattr)
							data->font = OpenFont(data->tattr);
						ChangeLTextFont(data->label,data->tattr);
						calcBorders(cl,(struct Gadget *) o, data);
						break;
					case CBA_ReadOnly:
						data->readonly = (BOOL) ticData;
						redrawIt = TRUE;
						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 = GMR_GADGETHIT;
			break;
		};
		case GM_GOACTIVE:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			if (((struct gpInput *) msg)->gpi_IEvent && !data->readonly)
			{
				((struct Gadget *) o)->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);
			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;
			switch (ie->ie_Class)
			{
				case IECLASS_RAWMOUSE:
				{
					switch (ie->ie_Code)
					{
						case SELECTUP:
							retval = GMR_NOREUSE | GMR_VERIFY;
							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 CheckboxClass::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)
	{
		Object *checker = (Object *) NewObject(NULL,SYSICLASS,
			IA_Left,0,
			IA_Top,0,
			IA_Width,g->Width,
			IA_Height,g->Height,
			SYSIA_DrawInfo,ginfo->gi_DrInfo,
			SYSIA_ReferenceFont, data->font,
			SYSIA_Size, SYSISIZE_HIRES,
			SYSIA_Which, CHECKIMAGE,
			TAG_END);
		if (checker)
		{
			DrawImageState(rp,(struct Image *) checker,g->LeftEdge,g->TopEdge,
				g->Flags & GFLG_SELECTED ? IDS_SELECTED : IDS_NORMAL,
				ginfo->gi_DrInfo);
			DisposeObject(checker);
		};
		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 (data->font && g->GadgetText)
		{
			ChangeLTextPens(data->label,pens[TEXTPEN],pens[BACKGROUNDPEN],JAM2);
			switch (data->labelplace)
			{
				case CB_LabelLeft:
					PrintLText(rp,data->label,
						g->LeftEdge - data->leftborder, g->TopEdge,
						data->leftborder, g->Height,
						STADJUST_Left);
					break;
				case CB_LabelRight:
					PrintLText(rp,data->label,
						g->LeftEdge + g->Width, g->TopEdge,
						data->rightborder, g->Height,
						STADJUST_Right);
					break;
			};
		};
		if (msg->MethodID != GM_RENDER)
			ReleaseGIRPort(rp);
		return TRUE;
	};
	return FALSE;
}

VOID CheckboxClass::calcBorders(Class *cl, struct Gadget *g,
	struct InstanceData *data)
{
	if (data->tattr && g->GadgetText)
	{
		LONG labelwidth = MeasureLText(data->label);
		data->leftborder = 0;
		data->rightborder = 0;
		data->topborder = 0;
		data->bottomborder = 0;
		switch (data->labelplace)
		{
			case CB_LabelLeft:
				data->leftborder = labelwidth + 4;
				break;
			case CB_LabelRight:
				data->rightborder = labelwidth + 4;
				break;
			default:
				break;
		};
	};
}

WORD CheckboxClass::calcWidth(Class *cl, struct Gadget *g,
	struct InstanceData *data)
{
	if (data->tattr)
	{
		WORD h = data->tattr->ta_YSize;
		if (h < 11)
			h = 11;
		return (h * 26) / 11;
	};
	return 26;
}

WORD CheckboxClass::calcHeight(Class *cl, struct Gadget *g,
	struct InstanceData *data)
{
	WORD h = 11;
	if (data->tattr)
	{
		h = data->tattr->ta_YSize;
		if (h < 11)
			h = 11;
	};
	return h;
}

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

CheckboxClass BCheckboxC::cbc;

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

