#include <classes/Intuition/Gadgets.h>

#include <exec/libraries.h>

#pragma -
#include <pragma/graphics_lib.h>
#include <pragma/commodities_lib.h>
#pragma +

GadgetC::GadgetC(GadgetEventC *event)
{
	gevent = event;
	gadget_ob = NULL;
}

GadgetC::~GadgetC()
{
}

VOID GadgetC::erase(WindowC *w)
{
	if (gadget_ob && w)
	{
		if (w->rastPort())
			EraseRect(w->rastPort(),
				gadget_ob->LeftEdge,
				gadget_ob->TopEdge,
				gadget_ob->LeftEdge + gadget_ob->Width,
				gadget_ob->TopEdge + gadget_ob->Height);
	};
}

VOID GadgetC::refresh(WindowC *w)
{
	if (gadget_ob && w)
	{
		w->refreshGadgets(gadget_ob,1);
	};
}

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

BOOL GModelC::snapshot(GModelSnapshotC &s)
{
	s.setDisabled(isDisabled());
	return TRUE;
}

BOOL GModelC::stamp(GModelSnapshotC &s)
{
	disable(s.isDisabled());
	return TRUE;
}

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

BOOL GStringC::snapshot(GStringSnapshotC &s)
{
	s.setString(get());
	return GModelC::snapshot(s);
}

BOOL GStringC::stamp(GStringSnapshotC &s)
{
	set(s.string());
	return GModelC::stamp(s);
}

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

BOOL GIntegerC::snapshot(GIntegerSnapshotC &s)
{
	s.setInteger(get());
	return GModelC::snapshot(s);
}

BOOL GIntegerC::stamp(GIntegerSnapshotC &s)
{
	set(s.integer());
	return GModelC::stamp(s);
}

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

BOOL GCheckboxC::snapshot(GCheckboxSnapshotC &s)
{
	s.setCheck(checked());
	return GModelC::snapshot(s);
}

BOOL GCheckboxC::stamp(GCheckboxSnapshotC &s)
{
	check(s.checked());
	return GModelC::stamp(s);
}

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

BOOL GRadioC::snapshot(GRadioSnapshotC &s)
{
	s.setSelection(selected());
	return GModelC::snapshot(s);
}

BOOL GRadioC::stamp(GRadioSnapshotC &s)
{
	select(s.selected());
	return GModelC::stamp(s);
}

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

BOOL GCycleC::snapshot(GCycleSnapshotC &s)
{
	s.setSelection(selected());
	return GModelC::snapshot(s);
}

BOOL GCycleC::stamp(GCycleSnapshotC &s)
{
	select(s.selected());
	return GModelC::stamp(s);
}

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

BOOL GSliderC::snapshot(GSliderSnapshotC &s)
{
	s.setMax(max());
	s.setMin(min());
	s.setLevel(level());
	return GModelC::snapshot(s);
}

BOOL GSliderC::stamp(GSliderSnapshotC &s)
{
	setMax(s.max());
	setMin(s.min());
	setLevel(s.level());
	return GModelC::stamp(s);
}

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

BOOL GScrollerC::snapshot(GScrollerSnapshotC &s)
{
	s.setPot(pot());
	s.setTotal(total());
	s.setVisible(visible());
	return GModelC::snapshot(s);
}

BOOL GScrollerC::stamp(GScrollerSnapshotC &s)
{
	setPot(s.pot());
	setTotal(s.total());
	setVisible(s.visible());
	return GModelC::stamp(s);
}

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

BOOL GListviewC::snapshot(GListviewSnapshotC &s)
{
	s.setSelection(selected());
	return GModelC::snapshot(s);
}

BOOL GListviewC::stamp(GListviewSnapshotC &s)
{
	select(s.selected());
	return GModelC::stamp(s);
}

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

BOOL GPaletteC::snapshot(GPaletteSnapshotC &s)
{
	s.setSelection(selected());
	return GModelC::snapshot(s);
}

BOOL GPaletteC::stamp(GPaletteSnapshotC &s)
{
	select(s.selected());
	return GModelC::stamp(s);
}

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

BOOL GTextC::snapshot(GTextSnapshotC &s)
{
	s.setString(get());
	return GModelC::snapshot(s);
}

BOOL GTextC::stamp(GTextSnapshotC &s)
{
	set(s.string());
	return GModelC::stamp(s);
}

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

BOOL GNumberC::snapshot(GNumberSnapshotC &s)
{
	s.setInteger(get());
	return GModelC::snapshot(s);
}

BOOL GNumberC::stamp(GNumberSnapshotC &s)
{
	set(s.integer());
	return GModelC::stamp(s);
}

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

GadgetListC::GadgetListC()
{
	disabled = FALSE;
	idcmpFlags = 0;
}

GadgetListC::~GadgetListC()
{
}

VOID GadgetListC::add(GadgetC &g)
{
	idcmpFlags |= g.idcmp();
	addTail(g);
}

struct Gadget *GadgetListC::rebuild(struct Gadget *prev)
{
	ListCursorC lc(*this);
	gadget_ob = NULL;
	if (!prev)
		return NULL;
	lc.last();
	while (!lc.isDone()) {
		if (!(prev = ((GadgetC *) lc.item())->rebuild(prev)))
		{
			gadget_ob = NULL;
			return NULL;
		};
		if (!gadget_ob)
			gadget_ob = ((GadgetC *) lc.item())->gadget();
		lc.prev();
	};
	return prev;
}

VOID GadgetListC::erase(WindowC *w)
{
	ListCursorC lc(*this);
	while (!lc.isDone()) {
		((GadgetC *) lc.item())->erase(w);
		lc.next();
	};
}

VOID GadgetListC::refresh(WindowC *w)
{
	ListCursorC lc(*this);
	while (!lc.isDone()) {
		((GadgetC *) lc.item())->refresh(w);
		lc.next();
	};
}

BOOL GadgetListC::disable(BOOL value)
{
	disabled = value;
	ListCursorC lc(*this);
	while (!lc.isDone()) {
		if (((GadgetC *) lc.item())->deepfollow())
			((GadgetC *) lc.item())->disable(value);
		lc.next();
	};
	return TRUE;
}

VOID GadgetListC::setTextAttr(const TextAttrC *tattr)
{
	ListCursorC lc(*this);
	while (!lc.isDone()) {
		if (((GadgetC *) lc.item())->deepfollow())
			((GadgetC *) lc.item())->setTextAttr(tattr);
		lc.next();
	};
}

VOID GadgetListC::setDefTextAttr(const TextAttrC *tattr)
{
	ListCursorC lc(*this);
	while (!lc.isDone()) {
		if (((GadgetC *) lc.item())->deepfollow())
			((GadgetC *) lc.item())->setDefTextAttr(tattr);
		lc.next();
	};
}

// ********************************************************
AnchorGadgetListC::AnchorGadgetListC()
	: GadgetListC()
{
}

AnchorGadgetListC::~AnchorGadgetListC()
{
}

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

RootGadgetListC::RootGadgetListC(WindowC &w)
{
	window = &w;
	window->setGadgetList(glist);
}

RootGadgetListC::~RootGadgetListC()
{
	window->clearGadgetList();
}

VOID RootGadgetListC::add(GadgetC &g)
{
	glist.add(g);
}

VOID RootGadgetListC::rebuild(BOOL refresh)
{
	window->clearGadgetList(FALSE,FALSE);
	struct Gadget first;
	glist.rebuild(&first);
	window->addIDCMP(glist.idcmp());
	window->resetGadgetList(refresh);
}

VOID RootGadgetListC::erase()
{
	window->clearGadgetList(FALSE,FALSE);
	glist.erase(window);
}

VOID RootGadgetListC::refresh()
{
	glist.refresh(window);
}

VOID RootGadgetListC::setTextAttr(const TextAttrC *tattr)
{
	glist.setTextAttr(tattr);
}

VOID RootGadgetListC::setDefTextAttr(const TextAttrC *tattr)
{
	glist.setDefTextAttr(tattr);
}

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

BOOL GadgetUpHandlerC::_handle(IntuiMessageC &msg)
{
	GadgetC *g = (GadgetC *) (
		((struct Gadget *) msg.IAddress)->UserData);
	if (g && g->event())
	{
		g->event()->up((WindowC *) msg.IDCMPWindow->UserData,g,&msg);
		_exit = g->event()->exit();
	};
	return TRUE;
}

BOOL GadgetUpHandlerC::exit()
{
	return _exit;
}

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

BOOL GadgetDownHandlerC::_handle(IntuiMessageC &msg)
{
	GadgetC *g = (GadgetC *) (
		((struct Gadget *) msg.IAddress)->UserData);
	if (g && g->event())
	{
		g->event()->down((WindowC *) msg.IDCMPWindow->UserData,g,&msg);
		_exit = g->event()->exit();
	};
	return TRUE;
}

BOOL GadgetDownHandlerC::exit()
{
	return _exit;
}

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

BOOL GadgetMoveDownHandlerC::_handle(IntuiMessageC &msg)
{
	handler->gadget = (struct Gadget *) msg.IAddress;
	return TRUE;
}

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

BOOL GadgetMoveUpHandlerC::_handle(IntuiMessageC &msg)
{
	handler->gadget = NULL;
	return TRUE;
}

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

GadgetMoveHandlerC::GadgetMoveHandlerC()
	: GadgetEventHandlerC(IDCMP_MOUSEMOVE),
	  down_handler(*this), up_handler(*this),
	  _exit(FALSE)
{
	gadget = NULL;
}

BOOL GadgetMoveHandlerC::_handle(IntuiMessageC &msg)
{
	if (gadget)
	{
		GadgetC *g = (GadgetC *) gadget->UserData;
		if (g && g->event())
		{
			g->event()->move((WindowC *) msg.IDCMPWindow->UserData,g,&msg);
			_exit = g->event()->exit();
		};
		return TRUE;
	};
	return FALSE;
}

BOOL GadgetMoveHandlerC::exit()
{
	return _exit;
}

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

GadgetKeyHandlerC::GadgetKeyHandlerC(WindowC &w)
	: WindowEventHandlerC(w,IDCMP_RAWKEY),
	  keys(sizeof(struct GadgetKey),10),
	  _exit(FALSE)
{
}

BOOL GadgetKeyHandlerC::add(STRPTR key, GadgetC &g)
{
	IX ix;
	if (ParseIX(key,&ix) == 0)
	{
		struct GadgetKey &k = (struct GadgetKey &) keys.addTail();
		k.gadget = &g;
		k.ix = ix;
		return TRUE;
	};
	return FALSE;
}

static BOOL MatchIX37(struct InputEvent *ev, struct InputXpression *ix)
{
	if (ev->ie_Class != ix->ix_Class)
		return FALSE;
	if ((ev->ie_Code & ix->ix_CodeMask) != (ix->ix_Code & ix->ix_CodeMask))
		return FALSE;
	UWORD qie = ev->ie_Qualifier & ix->ix_QualMask;
	UWORD qix = ix->ix_Qualifier & ix->ix_QualMask;
	if (ix->ix_QualSame & IXSYM_SHIFT)
	{
		if (qie & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
			qie |= (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT);
		if (qix & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
			qix |= (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT);
	};
	if (ix->ix_QualSame & IXSYM_CAPS)
	{
		if (qie & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT|IEQUALIFIER_CAPSLOCK))
			qie |= (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT|IEQUALIFIER_CAPSLOCK);
		if (qix & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT|IEQUALIFIER_CAPSLOCK))
			qix |= (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT|IEQUALIFIER_CAPSLOCK);
	};
	if (ix->ix_QualSame & IXSYM_ALT)
	{
		if (qie & (IEQUALIFIER_LALT|IEQUALIFIER_RALT))
			qie |= (IEQUALIFIER_LALT|IEQUALIFIER_RALT);
		if (qix & (IEQUALIFIER_LALT|IEQUALIFIER_RALT))
			qix |= (IEQUALIFIER_LALT|IEQUALIFIER_RALT);
	};
	return qie == qix;
};

BOOL GadgetKeyHandlerC::_handle(IntuiMessageC &msg)
{
	_exit = FALSE;
	struct InputEvent ev;
	ev.ie_NextEvent = NULL;
	ev.ie_Class = IECLASS_RAWKEY;
	ev.ie_SubClass = 0;
	ev.ie_Code = msg.Code;
	ev.ie_Qualifier = msg.Qualifier;
	ev.ie_EventAddress = (APTR) *((ULONG *) msg.IAddress);
	gen_arraycursor gac(keys);
	extern struct Library *CxBase;
	BOOL v37 = CxBase->lib_Version < 38;
	while (!gac.isDone())
	{
		struct GadgetKey &k = (struct GadgetKey &) gac.item();
		BOOL matched;
		if (v37)
			matched = MatchIX37(&ev,&k.ix)
		else
			matched = MatchIX(&ev,&k.ix);
		if (matched)
		{
			if (k.gadget && k.gadget->event())
			{
				if (msg.Code & 0x80)
				{
					k.gadget->event()->keyUp(window(),k.gadget,&msg);
				}
				else {
					k.gadget->event()->keyDown(window(),k.gadget,&msg);
				};
				_exit = k.gadget->event()->exit();
			};
		};
		gac.next();
	};
	return TRUE;
}

BOOL GadgetKeyHandlerC::exit()
{
	return _exit;
}
