#include <classes/BOOPSI/boopsilib.h>

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

#include <string.h>
#include <tools/str.h>
#include <exec/libraries.h>

VOID SuperDrawImageState(struct RastPort *rp, struct Image *image,
	struct Image *select, LONG dx, LONG dy, LONG width, LONG height,
	ULONG state, struct DrawInfo *drawinfo)
{

	static UWORD ditherData[] = { 0x1111,0x4444 };

	if (!image)
		return;

	if (image->Depth == CUSTOMIMAGEDEPTH)
	{
		DrawImageState(rp,image,
			dx + ((width - image->Width) >> 1),
			dy + ((height - image->Height) >> 1),
			state,drawinfo);
	}
	else {
		LONG left;
		LONG top;
		switch (state)
		{
			case IDS_NORMAL:
			case IDS_DISABLED:
				left = dx + ((width - image->Width) >> 1);
				top = dy + ((height - image->Height) >> 1);
				DrawImage(rp,image,left,top);
				break;
			case IDS_SELECTED:
			case IDS_SELECTEDDISABLED:
				if (select)
				{
					left = dx + ((width - select->Width) >> 1);
					top = dy + ((height - select->Height) >> 1);
					DrawImage(rp,select,left,top);
				}
				else {
					left = dx + ((width - image->Width) >> 1);
					top = dy + ((height - image->Height) >> 1);
					DrawImage(rp,image,left,top);
					SetDrMd(rp,COMPLEMENT);
					RectFill(rp,left,top,left+image->Width-1,top+image->Height-1);
				};
				break;
		};
		switch (state)
		{
			case IDS_DISABLED:
			case IDS_SELECTEDDISABLED:
				SetDrMd(rp,JAM1);
				SetAfPt(rp,ditherData,1);
				RectFill(rp,dx,dy,dx+width-1,dy+height-1);
				SetAfPt(rp,NULL,0);
				break;
		};
	};
}

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

ULONG SetGInfoAttrs(Object *o, struct GadgetInfo *gi, Tag tag1type, ...)
{
	return DoMethod(o,OM_SET,(struct TagItem *) &tag1type,gi);
}

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

ULONG SetSuperGInfoAttrs(Class *cl, Object *o, struct GadgetInfo *gi,
	Tag tag1type, ...)
{
	return DoSuperMethod(cl,o,OM_SET,(struct TagItem *) &tag1type,gi);
}

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

ULONG UpdateGInfoAttrs(Object *o, struct GadgetInfo *gi, ULONG flags,
	Tag tag1type, ...)
{
	return DoMethod(o,OM_UPDATE,(struct TagItem *) &tag1type,gi,flags);
}

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

ULONG SetGadgetAttrsJB(struct Gadget *g, struct Window *w, struct Requester *r,
	Tag tag1type, ...)
{
	return SetGadgetAttrsA(g,w,r,(struct TagItem *) &tag1type);
}

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

VOID GetInnerFrame(struct Gadget *g, Object *frame, struct IBox *framebox,
	WORD *frameright, WORD *framebottom)
{
	if (frame)
	{
		struct IBox box;
		SetAttrs(frame,
			IA_Left, 0,
			IA_Top, 0,
			IA_Width, g->Width,
			IA_Height, g->Height,
			TAG_END);
		DoMethod(frame, IM_FRAMEBOX, GADGET_BOX(g), &box, NULL, 0);
		if (framebox)
		{
			framebox->Left = g->LeftEdge - box.Left;
			framebox->Top = g->TopEdge - box.Top;
			framebox->Width = g->Width + g->Width - box.Width;
			framebox->Height = g->Height + g->Height - box.Height;
		};
		if (frameright)
			*frameright = box.Left + box.Width - g->Width - g->LeftEdge;
		if (framebottom)
			*framebottom = box.Top + box.Height - g->Height - g->TopEdge;
	}
	else {
		if (framebox)
		{
			framebox->Left = 0;
			framebox->Top = 0;
			framebox->Width = g->Width;
			framebox->Height = g->Height;
		};
		if (frameright)
			*frameright = 0;
		if (framebottom)
			*framebottom = 0;
	};
}

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

VOID SuperText(struct RastPort *rp, STRPTR str,
	struct TextFont *font, WORD x, WORD y, WORD width, WORD height,
	WORD adjust, UBYTE mark)
{
	if (!rp || !str)
		return;
	if (font)
		SetFont(rp,font);

	String s;
	LONG markpos = -1;
	if (mark != 0)
	{
		ULONG i = 0;
		while (str[i] != 0)
		{
			if (str[i] == mark && markpos == -1)
			{
				markpos = i;
				i++;
			};
			s += String(str[i]);
			i++;
		};
	}
	else {
		s = str;
	};

	struct TextExtent tex;
	extern struct Library *GfxBase;
	WORD w39 = width;
	if (GfxBase->lib_Version < 39)
		w39++;
	ULONG ll = TextFit(rp,s,s.length(),&tex,NULL,1,w39,height);

	Move(rp, x - tex.te_Extent.MinX,
		y - tex.te_Extent.MinY + (height - tex.te_Height + 1) / 2);
	if (markpos == -1)
	{
		Text(rp,(STRPTR) s,ll);
	}
	else {
		if (markpos > 0 && ll >= markpos)
		{
			Text(rp,(STRPTR) s,markpos);
		};
		ULONG enable = AskSoftStyle(rp);
		SetSoftStyle(rp,FSF_UNDERLINED,enable);
		Text(rp,((STRPTR) s) + markpos,1);
		SetSoftStyle(rp,FS_NORMAL,enable);
		if (ll > markpos + 1)
		{
			Text(rp,((STRPTR) s) + markpos + 1, ll - markpos - 1);
		};
	};
}

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

LONG SuperTextLength(STRPTR str, struct TextAttr *tattr, UBYTE mark)
{
	if (!str)
		return 0;
	struct IntuiText it = { 0,0,0,0,0,NULL,NULL,NULL };
	it.ITextFont = tattr;

	if (mark != 0)
	{
		String s;
		LONG markpos = -1;
		ULONG i = 0;
		while (str[i] != 0)
		{
			if (str[i] == mark && markpos == -1)
			{
				markpos = i;
				i++;
			};
			s += String(str[i]);
			i++;
		};
		it.IText = (STRPTR) s;
	}
	else {
		it.IText = str;
	};

	return IntuiTextLength(&it);
}

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

static VOID MeasureMark(struct LabelText *lt);

struct LabelText *AllocLText(STRPTR str, struct TextAttr *tattr,
	UBYTE apen, UBYTE bpen, UBYTE drmd, UBYTE mark)
{
	if (!str)
		return NULL;

	ULONG i = 0;
	LONG markpos = -1;
	while (str[i] != 0)
	{
		if (str[i] == mark && markpos == -1)
		{
			markpos = i;
		};
		i++;
	};

	struct LabelText *lt = (struct LabelText *) AllocMem(sizeof(struct LabelText),0);
	if (!lt)
		return NULL;
	lt->itext.FrontPen = apen;
	lt->itext.BackPen = bpen;
	lt->itext.DrawMode = drmd;
	lt->itext.LeftEdge = 0;
	lt->itext.TopEdge = 0;
	lt->itext.ITextFont = tattr;
	lt->itext.NextText = NULL;
	lt->memsize = i+1;
	lt->markpos = markpos;
	lt->itext.IText = (UBYTE *) AllocMem(lt->memsize,0);
	if (!lt->itext.IText)
	{
		FreeLText(lt);
		return NULL;
	};
	ULONG j = 0;
	ULONG k = 0;
	do {
		if (j == markpos)
			j++;
		lt->itext.IText[k] = str[j];
		k++;
	} while (str[j++] != 0);

	lt->length = IntuiTextLength(&lt->itext);

	MeasureMark(lt);

	return lt;
}

VOID FreeLText(struct LabelText *lt)
{
	if (lt)
	{
		if (lt->itext.IText)
			FreeMem(lt->itext.IText,lt->memsize);
		FreeMem(lt,sizeof(struct LabelText));
	};
}

WORD MeasureLText(struct LabelText *lt)
{
	if (!lt)
		return 0;
	return lt->length;
}

VOID ChangeLTextFont(struct LabelText *lt, struct TextAttr *tattr)
{
	if (!lt)
		return;
	lt->itext.ITextFont = tattr;
	lt->length = IntuiTextLength(&lt->itext);
	MeasureMark(lt);
}

VOID ChangeLTextPens(struct LabelText *lt, UBYTE apen, UBYTE bpen, UBYTE drmd)
{
	if (!lt)
		return;
	lt->itext.FrontPen = apen;
	lt->itext.BackPen = bpen;
	lt->itext.DrawMode = drmd;
}

VOID ChangeLText(struct LabelText *lt, STRPTR str, UBYTE mark)
{
	if (!lt || !str)
		return;

	if (lt->itext.IText)
	{
		FreeMem(lt->itext.IText,lt->memsize);
		lt->itext.IText = NULL;
	};

	ULONG i = 0;
	LONG markpos = -1;
	while (str[i] != 0)
	{
		if (str[i] == mark && markpos == -1)
		{
			markpos = i;
		};
		i++;
	};

	lt->memsize = i+1;
	lt->markpos = markpos;
	lt->itext.IText = (UBYTE *) AllocMem(lt->memsize,0);
	if (!lt->itext.IText)
	{
		return;
	};
	ULONG j = 0;
	ULONG k = 0;
	do {
		if (j == markpos)
			j++;
		lt->itext.IText[k] = str[j];
		k++;
	} while (str[j++] != 0);

	lt->length = IntuiTextLength(&lt->itext);

	MeasureMark(lt);
}

VOID PrintLText(struct RastPort *rp, struct LabelText *lt,
	WORD x, WORD y, WORD width, WORD height, WORD adjust)
{
	if (rp && lt)
	{
		WORD top = y + (height - lt->itext.ITextFont->ta_YSize + 1) / 2;
		switch (adjust)
		{
			case STADJUST_Center:
				x += (width - lt->length) / 2;
				break;
			case STADJUST_Right:
				x += width - lt->length;
				break;
		};
		PrintIText(rp,&lt->itext,x,top);
		if (lt->markpos >= 0)
		{
			top += lt->itext.ITextFont->ta_YSize - 1;
			Move(rp,x + lt->markX,top);
			SetAPen(rp,lt->itext.FrontPen);
			SetBPen(rp,lt->itext.BackPen);
			SetDrMd(rp,lt->itext.DrawMode);
			Draw(rp,x + lt->markX + lt->markWidth - 1,top);
		};
	};
}

static VOID MeasureMark(struct LabelText *lt)
{
	if (lt->markpos >= 0)
	{
		char c = lt->itext.IText[lt->markpos];
		lt->itext.IText[lt->markpos] = 0;
		lt->markX = IntuiTextLength(&lt->itext);
		lt->itext.IText[lt->markpos] = c;
		char markbuffer[2];
		markbuffer[0] = c;
		markbuffer[1] = 0;
		struct IntuiText it;
		it.ITextFont = lt->itext.ITextFont;
		it.IText = markbuffer;
		lt->markWidth = IntuiTextLength(&it);
	};
};

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

WORD MeasureTextLength(STRPTR s, struct TextAttr *t)
{
	if (!s)
		return 0;
	struct IntuiText it;
	it.ITextFont = t;
	it.IText = s;
	return IntuiTextLength(&it);
}

VOID PrintClipped(struct RastPort *rp, STRPTR s,
	WORD x, WORD y, WORD width, WORD height, WORD adjust)
{
	if (!s)
		return;
	struct TextExtent tex;
	// ** hier könnte man unter <V39 width+1 übergeben
	extern struct Library *GfxBase;
	WORD w39 = width;
	if (GfxBase->lib_Version < 39)
		w39++;
	ULONG ll = TextFit(rp,s,strlen(s),&tex,NULL,1,w39,height);
	if (ll > 0)
	{
		WORD cy = y - tex.te_Extent.MinY + (height - tex.te_Height + 1) / 2;
		switch (adjust)
		{
			case STADJUST_Left:
				Move(rp, x - tex.te_Extent.MinX,cy);
				break;
			case STADJUST_Center:
				Move(rp, x - tex.te_Extent.MinX + (width - tex.te_Width) / 2,cy);
				break;
			case STADJUST_Right:
				Move(rp, x - tex.te_Extent.MinX + width - tex.te_Width,cy);
				break;
		};
		Text(rp,s,ll);
	};
}

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

VOID drawOutlineImage(struct RastPort *rp, struct DrawData *v,
	WORD x, WORD y, WORD w, WORD h, struct DrawInfo *di,
	BOOL outline, BOOL plane, BOOL recessed, LONG state)
{
#define AREASIZE 200
	struct TmpRas mytmpras;
	struct AreaInfo myareainfo;
	UWORD areadata[AREASIZE];
	UWORD pens[9];
	if (!rp || !v)
		return;
	PLANEPTR myplane = NULL;
	if (!rp->TmpRas)
	{
		myplane = AllocRaster(rp->BitMap->BytesPerRow*8,rp->BitMap->Rows);
		if (!myplane)
			return;
		InitTmpRas(&mytmpras,myplane,RASSIZE(rp->BitMap->BytesPerRow*8,
			rp->BitMap->Rows));
		rp->TmpRas = &mytmpras;
	};
	BOOL ownareainfo = FALSE;
	if (!rp->AreaInfo)
	{
		InitArea(&myareainfo,areadata,AREASIZE / 5);
		ownareainfo = TRUE;
		rp->AreaInfo = &myareainfo;
	};
	/* register */ UWORD i;
	for (i = 0; i < 9; i++)
		pens[i] = di->dri_Pens[i];
	if (recessed)
	{
		i = pens[SHINEPEN];
		pens[SHINEPEN] = pens[SHADOWPEN];
		pens[SHADOWPEN] = i;
	};
	if (state == IDS_SELECTED || state == IDS_INACTIVESELECTED
		|| state == IDS_SELECTEDDISABLED)
	{
		i = pens[SHINEPEN];
		pens[SHINEPEN] = pens[SHADOWPEN];
		pens[SHADOWPEN] = i;
	};
	struct DrawData *firstPoint = v;
	while (v->command != DRAWCMD_End) {
		switch (v->command)
		{
			case DRAWCMD_Move:
				AreaMove(rp,x+(v->c.move.x*w)/16384,y+(v->c.move.y*h)/16384);
				break;
			case DRAWCMD_Draw:
				AreaDraw(rp,x+(v->c.draw.x*w)/16384,y+(v->c.draw.y*h)/16384);
				break;
			case DRAWCMD_Attr:
				break;
			case DRAWCMD_Fill:
				if (plane)
					SetAPen(rp,pens[v->c.fill.planepen])
				else
					SetAPen(rp,pens[v->c.fill.pen]);
				SetDrMd(rp,JAM2);
				BNDRYOFF(rp);
				AreaEnd(rp);
				if (outline)
				{
					BOOL noline = FALSE;
					BOOL closeLines = FALSE;
					WORD cx,cy;
					while (firstPoint != v+1) {
						switch (firstPoint->command)
						{
							case DRAWCMD_Move:
								if (closeLines && !noline)
									Draw(rp,cx,cy);
								cx = x+(firstPoint->c.move.x*w)/16384;
								cy = y+(firstPoint->c.move.y*h)/16384;
								Move(rp,cx,cy);
								closeLines = TRUE;
								break;
							case DRAWCMD_Draw:
								if (!closeLines)
								{
									cx = x+(firstPoint->c.draw.x*w)/16384;
									cy = y+(firstPoint->c.draw.y*h)/16384;
									closeLines = TRUE;
								};
								if (noline)
									Move(rp,x+(firstPoint->c.draw.x*w)/16384,
										y+(firstPoint->c.draw.y*h)/16384);
								else
									Draw(rp,x+(firstPoint->c.draw.x*w)/16384,
										y+(firstPoint->c.draw.y*h)/16384);
								break;
							case DRAWCMD_Attr:
								if (firstPoint->c.attr.pen < 0)
								{
									noline = TRUE;
								}
								else {
									if (plane)
										SetAPen(rp,pens[firstPoint->c.attr.planepen])
									else
										SetAPen(rp,pens[firstPoint->c.attr.pen]);
									noline = FALSE;
								};
								break;
							case DRAWCMD_Fill:
								if (closeLines && !noline)
									Draw(rp,cx,cy);
								closeLines = FALSE;
								break;
						};
						firstPoint++;
					};
				};
				firstPoint = v + 1;
				break;
			case DRAWCMD_Image:
				break;
		};
		v++;
	};
	if (state == IDS_DISABLED || state == IDS_INACTIVEDISABLED
		|| state == IDS_SELECTEDDISABLED)
	{
	};
	if (ownareainfo)
		rp->AreaInfo = NULL;
	if (myplane)
	{
		rp->TmpRas = NULL;
		FreeRaster(myplane,rp->BitMap->BytesPerRow*8,rp->BitMap->Rows);
	};
}

