#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <intuition/intuitionbase.h>
#include <intuition/classusr.h>
#include <intuition/imageclass.h>
#include <intuition/gadgetclass.h>
#include <intuition/cghooks.h>
#include <intuition/icclass.h>
#include <intuition/classes.h>
#include <intuition/sghooks.h>
#include <intuition/screens.h>
#include <graphics/gfxbase.h>
#include <graphics/text.h>
#include <graphics/gfxmacros.h>
#include <utility/tagitem.h>
#include <utility/hooks.h>
#include <datatypes/datatypesclass.h>
#include <datatypes/datatypes.h>
#include <datatypes/pictureclass.h>
#include <string.h>
#include "launchclass.h"
#include "tinymeter.h"

#define err_nolis       7

#define col_entry       1
#define col_hbright     2
#define col_hdark       3
#define col_cursor      4

struct LauncherData
{
	struct  Screen *scr;
	char    *txtlbl;
	char    *file;
	struct  LAU_Color Colors[LAU_UsedColors+1];
	BOOL    Style3D;
	BOOL    StyleBorder;
	BOOL    StyleBackground;
	BOOL    StyleShadowLabel;
	BOOL    StyleTransparent;
	struct  TextFont *textFont;
	ULONG   Pens[LAU_UsedColors];
	BOOL    PorC[LAU_UsedColors];
	BOOL    InitNotDone;
	UWORD   text_y;
	UWORD   indent;
	struct  lau_entry *list;
	ULONG   x_pos;
	ULONG   down;
	ULONG   diswhat;
	Object  *img;
};

__saveds ULONG dispatchLauncherGadget(Class *cl,Object *o, Msg msg)
{
    switch( msg->MethodID )
    {
	case    OM_NEW:
		if(o = (Object *)DoSuperMethodA(cl, o, msg) )
		{
		    newLauncher(cl,(struct Gadget *)o,(struct gpRender *)msg);
		}
		return((ULONG)o);
	case    GM_RENDER:
		return(renderLauncher(cl,(struct Gadget *)o,(struct gpRender *)msg));
	case    OM_SET:
		setLauncher((Class *)cl,(struct Gadget *)o,(struct gpRender *)msg);
		return(DoSuperMethodA(cl,o,msg));
	case    OM_GET:
		return((ULONG)getLauncher(cl,(struct Gadget *)o,(struct gpRender *)msg));
	case    GM_HITTEST:
		return(GMR_GADGETHIT);
	case    GM_GOACTIVE:
		if( ((struct gpInput *)msg)->gpi_IEvent )
		{
		    struct LauncherData *inst=INST_DATA(cl,o);
		    ((struct Gadget *)o)->Flags |= GFLG_SELECTED;
		    if(inst->diswhat!=typ_iconlauncher) renderLauncher(cl,(struct Gadget *)o, (struct gpRender *)msg);
		    return(GMR_MEACTIVE);
		}
		return(GMR_NOREUSE);
	case    GM_GOINACTIVE:
		{
		    struct LauncherData *inst=INST_DATA(cl,o);
		    ((struct Gadget *)o)->Flags &= ~GFLG_SELECTED;
		    if(inst->diswhat!=typ_iconlauncher) renderLauncher(cl,(struct Gadget *) o, (struct gpRender *)msg);
		}
		return(NULL);
	case    GM_HANDLEINPUT:
		{
		    struct Gadget *g = (struct Gadget *)o;
		    struct gpInput *gpi = (struct gpInput *)msg;
		    struct InputEvent *ie = gpi->gpi_IEvent;
		    ULONG  retval=GMR_MEACTIVE;

		    if( ie->ie_Class==IECLASS_RAWMOUSE)
		    {
			switch( ie->ie_Code )
			{
			    case SELECTUP:
				    if( (gpi->gpi_Mouse.X < g->LeftEdge ) ||
					(gpi->gpi_Mouse.X > g->LeftEdge + g->Width) ||
					(gpi->gpi_Mouse.Y < g->TopEdge ) ||
					(gpi->gpi_Mouse.Y > g->TopEdge  + g->Height) )
				    {
					    retval= GMR_NOREUSE | GMR_VERIFY;
				    }
				    else    retval= GMR_NOREUSE ;
				    break;
			    case MENUUP:
			    case MENUDOWN:
				    retval=GMR_NOREUSE;
				    break;
			    default:
				    retval=GMR_MEACTIVE;
				    break;
			}
		    }
		    return(retval);
		}
	case    OM_DISPOSE:
		{
		    struct lau_entry *entry;
		    struct LauncherData *inst=INST_DATA(cl,o);
		    int w;
		    if(inst->diswhat!=typ_iconlauncher) for(w=0;w<LAU_UsedColors;w++)FreePenNew(inst->scr,&inst->Colors[w],w,inst->Pens);
		    pFreeVec((ULONG *)inst->txtlbl);
		    DisposeDTObject(inst->img);
		    while(inst->list)
		    {
			entry = inst->list->next;
			DisposeDTObject(inst->list->img);
			pFreeVec  ((ULONG *)inst->list->lau_lbl);
			pFreeVec  ((ULONG *)inst->list->lau_cmd);
			pFreeVec  ((ULONG *)inst->list->lau_pth);
			pFreeVec  ((ULONG *)inst->list->lau_out);
			pFreeVec  ((ULONG *)inst->list);
			inst->list=entry;
		    }
		}
	default:
		return(DoSuperMethodA(cl,o,msg));
    }
}

Class *initLauncherGadgetClass(void)
{
    Class *cl;
    if( cl = (Class *)MakeClass( NULL, "gadgetclass", NULL, sizeof(struct LauncherData), 0) )
    {
	cl->cl_Dispatcher.h_Entry    = HookEntry;
	cl->cl_Dispatcher.h_SubEntry = (HOOKFUNC)dispatchLauncherGadget;
    }
    return(cl);
}

void setLauncher(Class *cl,struct Gadget *g,struct gpRender *msg)
{
    struct LauncherData    *inst=INST_DATA(cl,g);
    if ( FindTagItem(GA_Width,  ((struct opSet *)msg)->ops_AttrList) ||
	 FindTagItem(GA_Height, ((struct opSet *)msg)->ops_AttrList) ||
	 FindTagItem(GA_Top,    ((struct opSet *)msg)->ops_AttrList) ||
	 FindTagItem(GA_Left,   ((struct opSet *)msg)->ops_AttrList) )
    {
	inst->InitNotDone=TRUE;
    }
}

ULONG getLauncher(Class *cl,struct Gadget *g,struct gpRender *msg)
{
    struct LauncherData *inst=INST_DATA(cl,g);
    switch (((struct opGet *)msg)->opg_AttrID)
    {
	case    LAU_Down:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->down;
		break;
	case    LAU_List:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->list;
		break;
	case    LAU_ColLabel:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->Pens[0];
		break;
	case    LAU_ColEntry:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->Pens[1];
		break;
	case    LAU_ColHalfBright:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->Pens[2];
		break;
	case    LAU_ColHalfShadow:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->Pens[3];
		break;
	case    LAU_ColCursor:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->Pens[4];
		break;
	case    LAU_ColBrightEdg:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->Pens[5];
		break;
	case    LAU_ColDarkEdg:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->Pens[6];
		break;
	case    LAU_ColBackground:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->Pens[7];
		break;
	case    LAU_Type:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->diswhat;
		break;
	case    LAU_Transparent:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->StyleTransparent;
		break;
	case    LAU_XPos:
		*(((struct opGet *)msg)->opg_Storage)=(ULONG)inst->x_pos;
		break;
	case    LAU_ImgHeight:
		{
		    struct BitMapHeader *bmhd;
		    if(inst->img)
		    {
			GetDTAttrs(inst->img,PDTA_BitMapHeader,&bmhd,TAG_DONE);
			*(((struct opGet *)msg)->opg_Storage)=(ULONG)bmhd->bmh_Height;
		    }
		    else *(((struct opGet *)msg)->opg_Storage)=0L;
		}
		break;
	default:
		*(((struct opGet *)msg)->opg_Storage)=0;
		break;
    }
    return((ULONG)TRUE);
}


BOOL freeLauncherGadgetClass( Class *cl )
{
    return(FreeClass(cl));
}

void CleanString(char *buffer)
{
    int i=my_strlen(buffer);
    if(i>0)
    if(buffer[i-1]==0x0a) buffer[i-1]=0x00;
}

void StripSpaces(char *src, char *dest)
{
    int  i,j,len=my_strlen(src);
    BOOL foo=FALSE;
    if(len>0)
    {
	if(src[len-1]==0x20)
	{
	    for(i=len-1;i>-1;i--)
		if(src[i]==0x20)
		    src[i]=0x0;
			else break;
	}
	for(i=0,j=0;i<len;i++) if((src[i]!=0x20)||(foo==TRUE))
	{
	    if(src[i]!=0x0a) dest[j++]=src[i]; foo=TRUE;
	}
	dest[j]=0x00;
    }
    else dest[0]=0x00;
}

void CopyString(char *src, char **dest, struct LauncherData *inst)
{
    if(my_strlen(src))
    {
	*dest=(char *)pAllocVec(my_strlen(src)+1L);
	StripSpaces(src,*dest);
    }
    else *dest=(char *)pAllocVec(1L);
}

ULONG newLauncher(Class *cl,struct Gadget *g,struct gpRender *msg)
{
    struct   TagItem *ti;
    struct   LauncherData *inst=INST_DATA(cl,g);
    BPTR     file;

    ti      = ((struct opSet *)msg)->ops_AttrList;
    if(inst->scr = (struct Screen *)GetTagData(LAU_Screen, 0, ti))
    {
	if(inst->textFont = (struct TextFont *)GetTagData(LAU_TextFont,(ULONG)OpenTopaz(),ti))
	{
	    char *dummy;
	    if(dummy=(char *)pAllocVec(256L))
	    {
		int     i;
		UWORD   def_pens[]={ 2, 2, 1, 3, 1, 2, 1, 0 };
		ULONG   lau_tags[]={LAU_ColLabel,LAU_ColEntry,LAU_ColHalfBright,LAU_ColHalfShadow,LAU_ColCursor,LAU_ColBrightEdg,LAU_ColDarkEdg,LAU_ColBackground};

		inst->file              = (char *)GetTagData(LAU_File,(ULONG)" ", ti);
		inst->Style3D           = GetTagData(LAU_3D,  TRUE, ti);
		inst->StyleBorder       = GetTagData(LAU_Border, TRUE, ti);
		inst->StyleBackground   = GetTagData(LAU_Background, FALSE, ti);
		inst->StyleShadowLabel  = GetTagData(LAU_ShadowLabel, FALSE, ti);
		inst->StyleTransparent  = GetTagData(LAU_Transparent, FALSE, ti);
		inst->down              = GetTagData(LAU_Down, FALSE, ti);
		inst->indent            = GetTagData(LAU_Indent, lind_realcenter, ti);
		inst->diswhat           = GetTagData(LAU_Type, 0, ti);
		for(i=0;i<LAU_UsedColors;i++) CopyMem((APTR)GetTagData(lau_tags[i],(ULONG)GetGaugePen(def_pens[i]), ti),(APTR)&inst->Colors[i], sizeof(struct LAU_Color));
		inst->InitNotDone=TRUE;
		if(file=Open(inst->file,MODE_OLDFILE))
		{
		    if(FGets(file,dummy,254))
		    {
			if(*((ULONG *)dummy)==0x544D4C49)
			{
			    if(FGets(file,dummy,254))
			    {
				CopyString(dummy,&inst->txtlbl,inst);
				if(FGets(file,dummy,254))
				{
				    CleanString(dummy);
				    if(dummy[0])inst->img=(Object *)LoadImage(dummy,inst->scr);
				    else        inst->img=0L;
				    if(inst->list=(struct lau_entry *)pAllocVec(sizeof(struct lau_entry)))
				    {
					struct lau_entry *oldentry,*actentry=inst->list;
					while(FGets(file,dummy,32))
					{
					    CopyString(dummy,&actentry->lau_lbl,inst);
					    FGets(file,dummy,254);
					    CopyString(dummy,&actentry->lau_cmd,inst);
					    FGets(file,dummy,8);
					    CleanString(dummy);
					    actentry->lau_type=dummy[0]-0x30;
					    FGets(file,dummy,254);
					    CopyString(dummy,&actentry->lau_pth,inst);
					    FGets(file,dummy,254);
					    CopyString(dummy,&actentry->lau_out,inst);
					    FGets(file,dummy,16);
					    CleanString(dummy);
					    {
						LONG i,len=my_strlen(dummy),j=1,out=0L;
						if(len>0) /* Shit... that was a trap... */
						for(i=len-1;i>-1;i--)
						{
						    if((dummy[i]>0x29)&&(dummy[i]<0x3A))
						    {
							out+=((dummy[i]-0x30)*j); j*=10;
						    }
						}
						actentry->lau_stack=out;
					    }
					    FGets(file,dummy,254);
					    CleanString(dummy);
					    if(dummy[0]) actentry->img=(Object *)LoadImage(dummy,inst->scr);
					    else         actentry->img=0L;
					    oldentry=actentry;
					    actentry=(struct lau_entry *)pAllocVec(sizeof(struct lau_entry));
					    oldentry->next=actentry;
					}
					pFreeVec((ULONG *)actentry);
					oldentry->next=NULL;
				    }
				    else
				    {
					pFreeVec((ULONG *)inst->list);
					inst->list=NULL;
				    }
				}
			    }
			    else show(err_nolis);
			}
			else show(err_nolis);
		    }
		    else show(err_nolis);
		    Close(file);
		}
		pFreeVec((ULONG *)dummy);
		return((ULONG)g);
	    }
	}
    }
    return(0L);
}

ULONG renderLauncher(Class *cl,struct Gadget *g,struct gpRender *msg)
{
    struct RastPort         *rp;
    struct BitMapHeader     *bmhd;
    struct LauncherData     *inst=INST_DATA(cl,(Object *)g);

    if(msg->gpr_GInfo)
    if(rp = ( msg->MethodID == GM_RENDER ) ? msg->gpr_RPort : (struct RastPort *) ObtainGIRPort(msg->gpr_GInfo) )
    {
	switch (inst->diswhat)
	{
	    case    typ_simplelauncher:
		    {
			UWORD text_x,w,t_length;
			if(inst->InitNotDone) for(w=0;w<LAU_UsedColors;w++) GetPenNew(msg->gpr_GInfo->gi_Screen,&inst->Colors[w],w,inst->PorC,inst->Pens);

			SetDrMd(rp,JAM1);
			SetFont(rp,inst->textFont);

			inst->text_y=((g->Height-((inst->textFont->tf_Baseline+inst->textFont->tf_YSize)>>1))>>1);
			if(inst->textFont->tf_Baseline<inst->textFont->tf_YSize)
			{
			    inst->text_y+=inst->textFont->tf_Baseline-1;
			    if(inst->text_y<inst->textFont->tf_Baseline)inst->text_y=inst->textFont->tf_Baseline;
			}
			else
			{
			    inst->text_y+=inst->textFont->tf_YSize-1;
			    if(inst->text_y<inst->textFont->tf_YSize)   inst->text_y=inst->textFont->tf_YSize;
			}
			SetAPen(rp,inst->Pens[col_bg]);
			if(inst->StyleBackground) my_RectFill(rp,g->LeftEdge,g->TopEdge,g->Width,g->Height);
			if(inst->img)
			{
			    UWORD y,height,ypos;
			    GetDTAttrs(inst->img,PDTA_BitMapHeader,&bmhd,TAG_DONE);
			    if(bmhd->bmh_Height>(g->Height-6))
			    {
				height=(g->Height-6);
				y=(bmhd->bmh_Height-height)>>1;
				ypos=3;
			    }
			    else
			    {
				height=bmhd->bmh_Height;
				y=0;
				ypos=(g->Height/2)-(height/2);
			    }
			    DrawDTObject(inst->img,0,y,rp,g->LeftEdge+4,g->TopEdge+ypos,-1,height,inst->StyleTransparent);
			}
			if(inst->StyleBorder)
			{
			    if(inst->Style3D)
			    {
				draw_border_new(rp,g->LeftEdge,g->TopEdge,g->Width,g->Height,inst->Pens[col_dark],inst->Pens[col_dark]);
				if(g->Flags & GFLG_SELECTED)draw_border_new(rp,g->LeftEdge+1,g->TopEdge+1,g->Width-2,g->Height-2,inst->Pens[col_hdark],inst->Pens[col_bright]);
				else                        draw_border_new(rp,g->LeftEdge+1,g->TopEdge+1,g->Width-2,g->Height-2,inst->Pens[col_bright],inst->Pens[col_hdark]);
			    }
			    else
			    {
				if(g->Flags & GFLG_SELECTED)draw_border_new(rp,g->LeftEdge,g->TopEdge,g->Width,g->Height,inst->Pens[col_hdark],inst->Pens[col_bright]);
				else                        draw_border_new(rp,g->LeftEdge,g->TopEdge,g->Width,g->Height,inst->Pens[col_bright],inst->Pens[col_hdark]);
			    }
			}
			t_length=TextLength(rp,inst->txtlbl,my_strlen(inst->txtlbl));
			if(t_length<(g->Width - ((inst->indent==lind_left)||(inst->indent==lind_center) ? ( bmhd ? bmhd->bmh_Width : 0 )  : 0)))
			{
			    switch(inst->indent)
			    {
				case    lind_center:
					if(bmhd) text_x=bmhd->bmh_Width+((g->Width-t_length-bmhd->bmh_Width)>>1);
					else     text_x=((g->Width-t_length)>>1);
					break;
				case    lind_left:
					if(bmhd) text_x=bmhd->bmh_Width+11;
					else     text_x=5;
					break;
				case    lind_right:
					text_x=g->Width-t_length-5;
					break;
				default :
					text_x=((g->Width-t_length)>>1);
					break;
			    }
			    if(inst->StyleShadowLabel)
			    {
				SetAPen(rp,inst->Pens[col_dark]);
				Move(rp,text_x+g->LeftEdge+1,inst->text_y+g->TopEdge+1);
				Text(rp,inst->txtlbl,my_strlen(inst->txtlbl));
			    }
			    if(g->Flags & GFLG_SELECTED)
			    {
				Move(rp,text_x+g->LeftEdge+1,inst->text_y+g->TopEdge+1);
			    }
			    else
			    {
				Move(rp,text_x+g->LeftEdge,inst->text_y+g->TopEdge);
			    }
			    SetAPen(rp,inst->Pens[col_label]);
			    Text(rp,inst->txtlbl,my_strlen(inst->txtlbl));
			}
		    }
		    break;
	    case    typ_iconlauncher:
		    {
			struct  lau_entry       *foobar;
			ULONG                   fullness=0;
			if(foobar=inst->list)
			{
			    do
			    {
				if(foobar->img)
				{
				    GetDTAttrs(foobar->img,PDTA_BitMapHeader,&bmhd,TAG_DONE);
				    if(bmhd)
				    {
					if((fullness+bmhd->bmh_Width)>g->Width) break;
					fullness+=bmhd->bmh_Width;
				    }
				}
			    }
			    while(foobar=foobar->next);
			    switch (inst->indent)
			    {
				case    lind_right:
					inst->x_pos=g->Width-fullness;
					break;
				case    lind_left:
					inst->x_pos=0;
					break;
				case    lind_center:
				default:
					inst->x_pos=(g->Width-fullness)>>1;
					break;
			    }
			    foobar=inst->list;
			    fullness=0;
			    do
			    {
				if(foobar->img)
				{
				    GetDTAttrs(foobar->img,PDTA_BitMapHeader,&bmhd,TAG_DONE);
				    if(bmhd)
				    {
					if((fullness+bmhd->bmh_Width)>g->Width) break;;
					DrawDTObject(foobar->img,0,0,rp,g->LeftEdge+fullness+inst->x_pos,g->TopEdge+((g->Height-bmhd->bmh_Height)>>1),-1,-1,inst->StyleTransparent);
					fullness+=bmhd->bmh_Width;
				    }
				}
			    }
			    while(foobar=foobar->next);
			}
		    }
		    break;
	}
	inst->InitNotDone=FALSE;
	if(msg->MethodID !=GM_RENDER) ReleaseGIRPort(rp);
	return(TRUE);
    }
    return(FALSE);
}
