/*  GadTools layout toolkit
**
**  Copyright © 1993-1995 by Olaf `Olsen' Barthel
**  Freely distributable.
**
**	:ts=4
*/

#include "gtlayout_global.h"

//#define DB(x)	x
#define DB(x)	;

#if defined(DO_TAB_KIND) && defined(DO_BOOPSI_KIND)

#define TIA_Labels		(TAG_USER+0x90000)
#define TIA_Font		(TAG_USER+0x90001)
#define TIA_Screen		(TAG_USER+0x90002)
#define TIA_Index		(TAG_USER+0x90003)
#define TIA_DrawInfo	(TAG_USER+0x90004)
#define TIA_SizeType	(TAG_USER+0x90005)

STATIC VOID __regargs
DeleteBitMap(struct BitMap *BitMap,BOOL Chip)
{
	if(V39 && !Chip)
		FreeBitMap(BitMap);
	else
	{
		if(BitMap)
		{
			WORD i;

			for(i = 0 ; i < BitMap->Depth ; i++)
				FreeVec(BitMap->Planes[i]);

			FreeVec(BitMap);
		}
	}
}

STATIC struct BitMap * __regargs
CreateBitMap(UWORD Width,UWORD Height,UBYTE Depth,struct BitMap *Friend,BOOL Chip)
{
	struct BitMap *BitMap;

	if(V39 && !Chip)
		BitMap = AllocBitMap(Width,Height,Depth,NULL,Friend);
	else
	{
		if(BitMap = (struct BitMap *)AllocVec(sizeof(struct BitMap),MEMF_ANY | MEMF_PUBLIC))
		{
			LONG PageSize;
			WORD i;

			InitBitMap(BitMap,Depth,Width,Height);

			PageSize = BitMap->BytesPerRow * BitMap->Rows;

			for(i = 0 ; i < Depth ; i++)
			{
				if(!(BitMap->Planes[i] = (PLANEPTR)AllocVec(PageSize,MEMF_CHIP)))
				{
					WORD j;

					for(j = 0 ; j < i ; j++)
						FreeVec(BitMap->Planes[j]);

					FreeVec(BitMap);

					return(NULL);
				}
			}
		}
	}

	return(BitMap);
}
/*	// not used
STATIC VOID __regargs
NotifyIndex(struct IClass *class,struct Gadget *gadget,struct GadgetInfo *GInfo,struct opUpdate *msg,LONG Index)
{
	struct TagItem Tags[3];

	Tags[0].ti_Tag	= GA_ID;
	Tags[0].ti_Data	= gadget->GadgetID;
	Tags[1].ti_Tag	= TIA_Index;
	Tags[1].ti_Data	= Index;
	Tags[2].ti_Tag	= TAG_DONE;

	DoSuperMethod(class,(Object *)gadget,OM_NOTIFY,Tags,GInfo,((msg->MethodID == OM_UPDATE) ? (msg->opu_Flags) : 0L));
}
*/
STATIC VOID __regargs
RenderTabs(struct RastPort *RPort,struct Gadget *gadget,TabInfo *Info,UWORD *Pens,UWORD Pull,BOOL Disabled)
{
	UWORD	Width,Height;
	LONG	i;
	UWORD	Pen;

	Width	= Info->TabWidth;
	Height	= Info->TabHeight;

	SetRast(&Info->RPort,Pens[BACKGROUNDPEN]);

	for(i = 0 ; i < 4 ; i++)
	{
		if(i & 1)
			Pen = SHADOWPEN;
		else
			Pen = SHINEPEN;

		SetAPen(&Info->RPort,Pens[Pen]);

		Move(&Info->RPort,0,Height - 1 - i);
		Draw(&Info->RPort,gadget->Width - 1 - Info->Thick * i,Height - 1 - i);
	}

	for(i = Info->Count - 1 ; i >= 0 ; i--)
	{
		if(i != Info->Current)
			BltMaskBitMapRastPort(Info->Tabs[i].BitMap,0,0,&Info->RPort,Info->Tabs[i].Left,4,Width,Height - 6,ABC|ABNC|ANBC,Info->Mask);
	}

	BltMaskBitMapRastPort(Info->Tabs[Info->Current].BitMap,0,0,&Info->RPort,Info->Tabs[Info->Current].Left,0,Width,Height - Pull,ABC|ABNC|ANBC,Info->Mask);

	WaitBlit();

	BltBitMapRastPort(Info->BitMap,0,0,RPort,gadget->LeftEdge,gadget->TopEdge + gadget->Height - (Info->TabHeight + 2),gadget->Width,Info->TabHeight,0xC0);
/*	// Intuition ghosts images all by itself
	if(Disabled)
	{
		SetAPen(RPort,Pens[BLOCKPEN]);
		SetDrMd(RPort,JAM1);

		SetAfPt(RPort,(UWORD *)&ghostingPat,1);
		RectFill(RPort,gadget->LeftEdge,gadget->TopEdge,gadget->LeftEdge + gadget->Width - 1,gadget->TopEdge + gadget->Height - 1);
		SetAfPt(RPort,NULL,0);
	}
*/
}

STATIC ULONG __regargs
SetMethod(struct IClass *class,struct Gadget *gadget,struct opSet *SetInfo)
{
	TabInfo			*Info = INST_DATA(class,gadget);
	struct TagItem	*Tag;
	BOOL			 NeedRefresh = FALSE;
	BOOL			 Disabled;

	Disabled = gadget->Flags & GFLG_DISABLED;

	if(Tag = FindTagItem(GA_Disabled,SetInfo->ops_AttrList))
	{
		if(Tag->ti_Data && !Disabled || !Tag->ti_Data && Disabled)
		{
			Disabled = Tag->ti_Data;

			NeedRefresh = TRUE;
		}
	}

	if(Tag = FindTagItem(TIA_Index,SetInfo->ops_AttrList))
	{
		LONG Index = Tag->ti_Data;

		if(Index >= Info->Count)
			Index = Info->Count - 1;

		if(Index != Info->Current)
		{
			Info->Initial = Info->Current = Index;

			NeedRefresh = TRUE;
		}
	}

	if(NeedRefresh)
	{
		struct RastPort *RPort;

		if(RPort = ObtainGIRPort(SetInfo->ops_GInfo))
		{
			RenderTabs(RPort,gadget,Info,SetInfo->ops_GInfo->gi_DrInfo->dri_Pens,0,Disabled);

/*			NotifyIndex(class,gadget,SetInfo->ops_GInfo,(struct opUpdate *)SetInfo,Info->Current);*/

			ReleaseGIRPort(RPort);
		}
	}

	return(DoSuperMethodA(class,(Object *)gadget,(Msg)SetInfo));
}

STATIC ULONG __regargs
RenderMethod(struct IClass *class,struct Gadget *gadget,struct gpRender *RenderInfo)
{
	TabInfo *Info = INST_DATA(class,gadget);

	RenderTabs(RenderInfo->gpr_RPort,gadget,Info,RenderInfo->gpr_GInfo->gi_DrInfo->dri_Pens,0,gadget->Flags & GFLG_DISABLED);

	return(TRUE);
}

STATIC ULONG __regargs
DisposeMethod(struct IClass *class,struct Gadget *gadget,Msg msg)
{
	TabInfo *Info = INST_DATA(class,gadget);

	if(Info->Mask || Info->Tabs || Info->BitMap)
		WaitBlit();

	if(Info->Mask)
		FreeVec(Info->Mask);

	if(Info->Tabs)
	{
		LONG i;

		for(i = 0 ; i < Info->Count ; i++)
			DeleteBitMap(Info->Tabs[i].BitMap,TRUE);

		FreeVec(Info->Tabs);
	}

	DeleteBitMap(Info->BitMap,FALSE);

	return(DoSuperMethodA(class,(Object *)gadget,msg));
}

STATIC ULONG __regargs
NewMethod(struct IClass *class,struct Gadget *gadget,struct opSet *SetInfo)
{
	if(gadget = (struct Gadget *)DoSuperMethodA(class,(Object *)gadget,(Msg)SetInfo))
	{
		TabInfo			*Info = INST_DATA(class,gadget);
		struct TagItem	*Tag,*TagList = SetInfo->ops_AttrList;
		struct TextAttr	*FontAttr;
		UWORD			 Width,Height;
		struct DrawInfo	*DrawInfo;
		STRPTR			*Labels;
		struct Screen	*Screen;

		Width = Height = 0;
		FontAttr = NULL;
		DrawInfo = NULL;
		Labels = NULL;
		Screen = NULL;

		memset(Info,0,sizeof(TabInfo));

		while(Tag = NextTagItem(&TagList))
		{
			switch(Tag->ti_Tag)
			{
				case TIA_Labels:
					Labels = (STRPTR *)Tag->ti_Data;
					break;

				case TIA_Screen:
					Screen = (struct Screen *)Tag->ti_Data;
					break;

				case TIA_Font:
					FontAttr = (struct TextAttr *)Tag->ti_Data;
					break;

				case TIA_Index:
					Info->Current = Tag->ti_Data;
					break;

				case TIA_DrawInfo:
					DrawInfo = (struct DrawInfo *)Tag->ti_Data;
					break;

				case GA_Width:
					Width = Tag->ti_Data;
					break;

				case GA_Height:
					Height = Tag->ti_Data;
					break;
			}
		}

		if(Labels)
		{
			while(Labels[Info->Count])
				Info->Count++;
		}

		if(Screen && DrawInfo && FontAttr && Width && Height && Labels && Info->Count)
		{
			struct TextFont *Font;

			if(Font = OpenFont(FontAttr))
			{
				struct RastPort *RPort = &Info->RPort;

				InitRastPort(RPort);

				SetFont(RPort,Font);

				if(Height >= RPort->TxHeight + 11)
				{
					LONG i,Len,MaxWidth,Remain;
					UWORD Lean;

					if(Info->Current < 0)
						Info->Current = 0;
					else
					{
						if(Info->Current >= Info->Count)
							Info->Current = Info->Count - 1;
					}

					Lean = (TextLength(RPort,"A",1) + 1) / 2;

					Info->Thick	= DrawInfo->dri_Resolution.Y / DrawInfo->dri_Resolution.X;
//					Info->Thick	= 2;

					if(Info->Thick < 1)
						Info->Thick = 1;

					for(i = MaxWidth = 0 ; i < Info->Count ; i++)
					{
						if((Len = TextLength(RPort,Labels[i],strlen(Labels[i]))) > MaxWidth)
							MaxWidth = Len;
					}

					MaxWidth = 2 + (Lean + Info->Thick - 1 + Info->Thick + MaxWidth + Info->Thick + Lean + Info->Thick - 1) + 2;

					Remain = (Width - MaxWidth - 5 * Info->Thick) / (Info->Thick * 4);

					if(Remain >= Info->Count - 1)
					{
						if(Info->Tabs = (TabEntry *)AllocVec(sizeof(TabEntry) * Info->Count,MEMF_ANY|MEMF_CLEAR))
						{
							STATIC BYTE PenNumbers[] =
							{
								BACKGROUNDPEN,
								SHINEPEN,
								SHADOWPEN,
								TEXTPEN
							};

							struct BitMap __aligned	 Mask;
							BOOL					 GotIt = TRUE;
							UWORD					 Depth;
							LONG					 MaxPen;
							UWORD					*Pens = DrawInfo->dri_Pens;

							for(i = 0, MaxPen = -1 ; i < sizeof(PenNumbers) ; i++)
							{
								if(Pens[PenNumbers[i]] > MaxPen)
									MaxPen = Pens[PenNumbers[i]];
							}

							for(i = 1 ; i < 9 ; i++)
							{
								if(MaxPen < (1 << i))
								{
									Depth = i;
									break;
								}
							}

							Info->TabWidth	= MaxWidth;
							Info->TabHeight	= 4 + RPort->TxHeight + 5;

							InitBitMap(&Mask,1,Info->TabWidth,Info->TabHeight);

							Mask.Planes[1] = NULL;

							for(i = 0 ; GotIt && i < 2 ; i++)
							{
								if(!(Mask.Planes[i] = (PLANEPTR)AllocVec(Mask.BytesPerRow * Mask.Rows,MEMF_CHIP)))
									GotIt = FALSE;
							}

							for(i = 0 ; GotIt && i < Info->Count ; i++)
							{
								if(!(Info->Tabs[i].BitMap = CreateBitMap(Info->TabWidth,Info->TabHeight,Depth,NULL,TRUE)))
									GotIt = FALSE;
							}

							if(V39)
								Depth = GetBitMapAttr(Screen->RastPort.BitMap,BMA_DEPTH);
							else
								Depth = Screen->RastPort.BitMap->Depth;

							if(!(Info->BitMap = CreateBitMap(gadget->Width,Info->TabHeight,Depth,Screen->RastPort.BitMap,FALSE)))
								GotIt = FALSE;

							if(GotIt)
							{
								struct TmpRas	 TmpRas;
								LONG			 j,Len,Offset,Pos;

								InitTmpRas(&TmpRas,Mask.Planes[1],RASSIZE(Info->TabWidth,Info->TabHeight));

								RPort->TmpRas	= &TmpRas;
								RPort->BitMap	= &Mask;

								SetRast(RPort,0);
								SetAPen(RPort,1);
								SetDrMd(RPort,JAM1);

								Move(RPort,0,Info->TabHeight - 1);
								Draw(RPort,Lean - 1,0);

								Move(RPort,Lean,0);
								Draw(RPort,Info->TabWidth - (Lean + 1),0);

								Move(RPort,Info->TabWidth - Lean,0);
								Draw(RPort,Info->TabWidth - 1,Info->TabHeight - 1);

								Move(RPort,0,Info->TabHeight - 1);
								Draw(RPort,Info->TabWidth - 1,Info->TabHeight - 1);

								Flood(RPort,1,Info->TabWidth / 2,Info->TabHeight / 2);

								SetAPen(RPort,0);

								for(i = 0 ; i < 4 ; i++)
								{
									for(j = 0 ; j < 2 ; j++)
										WritePixel(RPort,j,Info->TabHeight - 1 - i);

									for(j = 0 ; j < 2 ; j++)
										WritePixel(RPort,Info->TabWidth - 1 - j,Info->TabHeight - 1 - i);
								}

								WaitBlit();

								FreeVec(Mask.Planes[1]);

								RPort->TmpRas = NULL;

								Offset = (Width - Info->Thick * 5) / Info->Count;

								while(Offset > 0 && Offset * (Info->Count - 1) + Info->TabWidth >= Width - Info->Thick * 5)
									Offset--;

								if(Offset > Info->TabWidth + Info->Thick)
									Offset = Info->TabWidth + Info->Thick;

								Pos = 2 * Info->Thick;

								for(i = 0 ; i < Info->Count ; i++)
								{
									RPort->BitMap = Info->Tabs[i].BitMap;

									Info->Tabs[i].Left = Pos;
									Pos += Offset;

									SetRast(RPort,Pens[BACKGROUNDPEN]);

									SetAPen(RPort,Pens[SHINEPEN]);

									for(j = 0 ; j < Info->Thick ; j++)
									{
										Move(RPort,2 + j,Info->TabHeight - 1);
										Draw(RPort,2 + j + Lean - 1,0);
									}

									Move(RPort,2 + Lean,0);
									Draw(RPort,Info->TabWidth - (Lean + 1) - 2,0);

									SetAPen(RPort,Pens[SHADOWPEN]);

									for(j = 0 ; j < Info->Thick ; j++)
									{
										Move(RPort,Info->TabWidth - Lean - j - 2,0);
										Draw(RPort,Info->TabWidth - 1 - j - 2,Info->TabHeight - 1);
									}

									SetAPen(RPort,Pens[TEXTPEN]);
									Len = strlen(Labels[i]);

									Move(RPort,(Info->TabWidth - TextLength(RPort,Labels[i],Len)) / 2,2 + RPort->TxBaseline);
									Text(RPort,Labels[i],Len);
								}

								Info->Mask = Mask.Planes[0];

								Info->Offset = Offset;

								Info->RPort.BitMap = Info->BitMap;

								CloseFont(Font);

								return((ULONG)gadget);
							}
						}
					}
				}

				CloseFont(Font);
			}
		}

		CoerceMethod(class,(Object *)gadget,OM_DISPOSE);
	}

	return(0);
}

STATIC ULONG __regargs
InputMethod(struct IClass *class,struct Gadget *gadget,struct gpInput *InputInfo)
{
	TabInfo	*Info	= INST_DATA(class,gadget);
	ULONG	 Result	= GMR_MEACTIVE;
	BOOL	 Done	= FALSE;
	WORD	 x,y;

	x = InputInfo->gpi_Mouse.X;
	y = InputInfo->gpi_Mouse.Y;

	if(InputInfo->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE)
	{
		if(InputInfo->gpi_IEvent->ie_Code == MENUDOWN)
		{
			Result	= GMR_NOREUSE;
			Done	= TRUE;
		}
		else
		{
			if(InputInfo->gpi_IEvent->ie_Code == SELECTUP)
			{
				Done = TRUE;

				if(x >= 0 && x < gadget->Width && y >= 0 && y < gadget->Height)
				{
					Result = GMR_REUSE | GMR_VERIFY;

					*InputInfo->gpi_Termination = Info->Current;
				}
				else
					Result = GMR_NOREUSE;
			}
		}
	}

	if(Result == GMR_NOREUSE)
	{
		struct RastPort	*RPort;

		if(RPort = ObtainGIRPort(InputInfo->gpi_GInfo))
		{
			Info->Current = Info->Initial;

			RenderTabs(RPort,gadget,Info,InputInfo->gpi_GInfo->gi_DrInfo->dri_Pens,0,gadget->Flags & GFLG_DISABLED);

/*			NotifyIndex(class,gadget,InputInfo->gpi_GInfo,(struct opUpdate *)InputInfo,Info->Current);*/

			ReleaseGIRPort(RPort);
		}
	}

	if(!Done)
	{
		if(x >= 0 && x < gadget->Width && y >= 0 && y < gadget->Height)
		{
			LONG Index,i;

			for(Index = -1, i = Info->Count - 1 ; i >= 0 ; i--)
			{
				if(x >= Info->Tabs[i].Left && x < Info->Tabs[i].Left + Info->TabWidth)
					Index = i;
			}

			if(Index != Info->Current && Index != -1)
			{
				struct RastPort	*RPort;

				if(RPort = ObtainGIRPort(InputInfo->gpi_GInfo))
				{
					Info->Current = Index;

					RenderTabs(RPort,gadget,Info,InputInfo->gpi_GInfo->gi_DrInfo->dri_Pens,2,gadget->Flags & GFLG_DISABLED);

/*					NotifyIndex(class,gadget,InputInfo->gpi_GInfo,(struct opUpdate *)InputInfo,Index);*/

					ReleaseGIRPort(RPort);
				}
			}
		}
	}

	return(Result);
}

STATIC ULONG __regargs
ActiveMethod(struct IClass *class,struct Gadget *gadget,struct gpInput *InputInfo)
{
	TabInfo			*Info = INST_DATA(class,gadget);
	struct RastPort	*RPort;

	if(RPort = ObtainGIRPort(InputInfo->gpi_GInfo))
	{
		LONG Current,i;
		WORD x;

		x = InputInfo->gpi_Mouse.X;

		for(Current = -1, i = Info->Count - 1 ; i >= 0 ; i--)
		{
			if(x >= Info->Tabs[i].Left && x < Info->Tabs[i].Left + Info->TabWidth)
				Current = i;
		}

		Info->Initial = Info->Current;

		if(Current != Info->Current && Current != -1)
		{
			Info->Current = Current;

			RenderTabs(RPort,gadget,Info,InputInfo->gpi_GInfo->gi_DrInfo->dri_Pens,2,gadget->Flags & GFLG_DISABLED);
		}

/*		NotifyIndex(class,gadget,InputInfo->gpi_GInfo,(struct opUpdate *)InputInfo,Info->Current);*/

		ReleaseGIRPort(RPort);

		return(GMR_MEACTIVE);
	}
	else
		return(GMR_NOREUSE);
}

STATIC ULONG __regargs
InactiveMethod(struct IClass *class,struct Gadget *gadget,struct gpGoInactive *InactiveInfo)
{
	TabInfo			*Info = INST_DATA(class,gadget);
	struct RastPort	*RPort;
	LONG			 Index;

	if(InactiveInfo->gpgi_Abort)
		Index = Info->Initial;
	else
		Index = Info->Current;

	if(RPort = ObtainGIRPort(InactiveInfo->gpgi_GInfo))
	{
		Info->Current = Index;

		RenderTabs(RPort,gadget,Info,InactiveInfo->gpgi_GInfo->gi_DrInfo->dri_Pens,0,gadget->Flags & GFLG_DISABLED);

/*		NotifyIndex(class,gadget,InactiveInfo->gpgi_GInfo,(struct opUpdate *)InactiveInfo,Info->Current);*/

		ReleaseGIRPort(RPort);
	}

	return(0);
}

BOOL __stdargs
LTP_ObtainTabSize(struct IBox *Box,...)
{
	va_list			 VarArgs;
	struct TagItem	*TagList,*Tag;
	struct TextAttr	*FontAttr;
	struct DrawInfo	*DrawInfo;
	STRPTR			*Labels;
	LONG			 Count;
	LONG			 SizeType;
	BOOL			 Success;

	Success = FALSE;

	va_start(VarArgs,Box);

	TagList = (struct TagItem *)VarArgs;

	SizeType = GDOMAIN_NOMINAL;
	FontAttr = NULL;
	DrawInfo = NULL;
	Labels = NULL;

	while(Tag = NextTagItem(&TagList))
	{
		switch(Tag->ti_Tag)
		{
			case TIA_Labels:
				Labels = (STRPTR *)Tag->ti_Data;
				break;

			case TIA_Font:
				FontAttr = (struct TextAttr *)Tag->ti_Data;
				break;

			case TIA_DrawInfo:
				DrawInfo = (struct DrawInfo *)Tag->ti_Data;
				break;

			case TIA_SizeType:
				SizeType = Tag->ti_Data;
				break;
		}
	}

	Count = 0;

	if(Labels)
	{
		while(Labels[Count])
			Count++;
	}

	if(DrawInfo && FontAttr && Labels && Count)
	{
		struct TextFont *Font;

		if(Font = OpenFont(FontAttr))
		{
			struct RastPort	RastPort,*RPort = &RastPort;
			LONG			i,Len,MaxWidth;
			UWORD			Lean,Thick;

			InitRastPort(RPort);

			SetFont(RPort,Font);

			Box->Left	= 0;
			Box->Top	= 0;
			Box->Height	= RPort->TxHeight + 11;

			Lean = (TextLength(RPort,"A",1) + 1) / 2;

			Thick = DrawInfo->dri_Resolution.Y / DrawInfo->dri_Resolution.X;
//			Thick = 2;

			if(Thick < 1)
				Thick = 1;

			for(i = MaxWidth = 0 ; i < Count ; i++)
			{
				if((Len = TextLength(RPort,Labels[i],strlen(Labels[i]))) > MaxWidth)
					MaxWidth = Len;
			}

			MaxWidth = 2 + (Lean + Thick - 1 + Thick + MaxWidth + Thick + Lean + Thick - 1) + 2;

			if(SizeType == GDOMAIN_MINIMUM && Count > 1)
				Box->Width = 2 * Thick + MaxWidth + (Count - 1) * (Thick * 4) + Thick * 3;
			else
				Box->Width = 2 * Thick + Count * (MaxWidth + Thick) + Thick * 2;

			CloseFont(Font);

			Success = TRUE;
		}
	}

	va_end(VarArgs);

	return(Success);
}

ULONG __saveds __asm
LTP_TabClassDispatcher(register __a0 struct IClass *class,register __a2 Object *object,register __a1 Msg msg)
{
	switch(msg->MethodID)
	{
		case OM_NEW:
			return(NewMethod(class,(struct Gadget *)object,(struct opSet *)msg));

		case OM_UPDATE:
		case OM_SET:
			return(SetMethod(class,(struct Gadget *)object,(struct opSet *)msg));

		case OM_DISPOSE:
			return(DisposeMethod(class,(struct Gadget *)object,msg));

		case GM_RENDER:
			return(RenderMethod(class,(struct Gadget *)object,(struct gpRender *)msg));

		case GM_HITTEST:
			return(GMR_GADGETHIT);

		case GM_GOINACTIVE:
			return(InactiveMethod(class,(struct Gadget *)object,(struct gpGoInactive *)msg));

		case GM_HANDLEINPUT:
			return(InputMethod(class,(struct Gadget *)object,(struct gpInput *)msg));

		case GM_GOACTIVE:
			return(ActiveMethod(class,(struct Gadget *)object,(struct gpInput *)msg));

		default:
			return(DoSuperMethodA(class,object,msg));
	}
}

#endif	// defined(DO_TAB_KIND) && defined(DO_BOOPSI_KIND)
