/*************************************************************************
;  :Module.	winmem.c
;  :Author.	Bert Jahn
;  :Address.	Franz-Liszt-Straße 16, Rudolstadt, 07404, Germany
;  :Version.	$Id: winmem.c 1.5 2002/02/19 21:15:21 wepl Exp wepl $
;  :History.	21.03.00 separated from main
;  :Copyright.	All Rights Reserved
;  :Language.	C
;  :Translator.	GCC
*************************************************************************/

#include <stdlib.h>
#include <string.h>

#include <exec/execbase.h>
#include <libraries/mui.h>
#include <mui/HexEdit_mcc.h>

#include <clib/alib_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/muimaster_protos.h>
#include <clib/utility_protos.h>

#include "whddump.h"
#include "WHDLoadGCI.h"

/************************************************************************/
/* Compiler Stuff                                                       */
/************************************************************************/

#define REG(x) register __ ## x
#define ASM    __asm
#define SAVEDS __saveds

/************************************************************************/
/* defines								*/
/************************************************************************/

#define MAXWINMEM 10	/* amount of possible memory hexdump windows */
#define MUIA_HexEdit_My	TAG_USER + 1

/************************************************************************/
/* extern variables							*/
/************************************************************************/

extern APTR			app,win;
extern struct whddump_header	*header;
extern struct Library		*MUIMasterBase;
extern APTR			mem,emem,slave;
extern const char* StringHexAccept;

/************************************************************************/
/* static variables							*/
/************************************************************************/

static APTR win_mem[MAXWINMEM];		/* memory hexdump windows */
static APTR gad_hex[MAXWINMEM];		/* hex display gadgets */
static APTR gad_goto[MAXWINMEM];	/* goto gadgets */

static struct MUI_CustomClass *HexClass;

static struct NewMenu MenuDataMem[] =
{
	MENUBASE
	{ NM_TITLE, "Memory"	, 0 ,0 ,0 ,(APTR)MEN_MEMORY	},\
	{ NM_ITEM ,  "Goto"	,"G",0 ,0 ,(APTR)MEN_GOTO0	},\
	{ NM_END,NULL,0,0,0,(APTR)0 },
};

/************************************************************************/
/* function declarations						*/
/************************************************************************/

void *	wmem_init	(void);
void	wmem_finit	(void);

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

struct HexData {
	int low_bound;
	int high_bound;
	int base_address;
	int len_base;
	int len_off;
};

SAVEDS ULONG
mNew(
	struct IClass *cl,
	Object *obj,
	Msg msg
) {
	struct TagItem *tags,*tag;
	int i;
	struct HexData tmp = {0,0,0,0,0};

	for (tags=((struct opSet *)msg)->ops_AttrList; (tag=NextTagItem(&tags)); )
	{
		switch (tag->ti_Tag)
		{
			case MUIA_HexEdit_LowBound: tmp.low_bound = tag->ti_Data; break;
			case MUIA_HexEdit_HighBound: tmp.high_bound = tag->ti_Data; break;
			case MUIA_HexEdit_BaseAddressOffset: tmp.base_address = tag->ti_Data; break;
			case MUIA_HexEdit_AddressChars:
				i = tmp.high_bound + tmp.base_address;
				tmp.len_base =	i < 0x10000 ? 4 :
								i < 0x100000 ? 5 :
								i < 0x1000000 ? 6 :
								i < 0x10000000 ? 7 : 8;
				if (tmp.low_bound + tmp.base_address) {
					i = tmp.high_bound - tmp.low_bound;
					tmp.len_off =	i < 0x1000 ? 3 :
									i < 0x10000 ? 4 :
									i < 0x100000 ? 5 : 6;
				} else {
					tmp.len_off = 0;
				}
				tag->ti_Data = tmp.len_base + (tmp.len_off ? 1 + tmp.len_off : 0);
				break;
		}
	}

	if (!(obj = (Object *)DoSuperMethodA(cl,obj,msg))) return(0);

	memcpy(INST_DATA(cl,obj), &tmp, sizeof(tmp));

	return ((ULONG)obj);
}

SAVEDS ULONG
mSet(
	struct IClass *cl,
	Object *obj,
	Msg msg
) {
	struct TagItem *tags,*tag;
	char *s;
	int i;

	for (tags=((struct opSet *)msg)->ops_AttrList; (tag=NextTagItem(&tags)); )
	{
		switch (tag->ti_Tag)
		{
			case MUIA_HexEdit_My:
				get( gad_goto[tag->ti_Data], MUIA_String_Contents, &s );
				i = atoi( s );
				set( obj, MUIA_HexEdit_CursorAddress, 1000 );
				break;
		}
	}

	return DoSuperMethodA(cl,obj,msg);
}

SAVEDS ULONG
mCreateDisplayAddress(
	struct IClass *cl,
	Object *obj,
	struct MUIP_HexEdit_CreateDisplayAddress *msg
) {
	struct HexData *d = INST_DATA(cl,obj);
	ULONG address;
	UBYTE i;
	UBYTE *hextable = "0123456789ABCDEF";

	address = (d->base_address + msg->address) << (32 - d->len_base * 4);
	for(i = 0; i < d->len_base; i++)
	{
		*(*msg->cp)++ = hextable[address >> (32 - 4)];
		address <<= 4;
	}

	address = (msg->address - d->low_bound) << (32 - d->len_off * 4);
	if (d->len_off) {
		*(*msg->cp)++ = (unsigned char) '·';
		for(i = 0; i < d->len_off; i++)
		{
			*(*msg->cp)++ = hextable[address >> (32 - 4)];
			address <<= 4;
		}
	}

	return(TRUE);
}

/*
SAVEDS ASM ULONG
HexDispatcher(
	REG(a0) struct IClass *cl,
	REG(a2) Object *obj,
	REG(a1) Msg msg
*/
SAVEDS ULONG
HexDispatcher(
	struct IClass *cl __asm("a0"),
	Object *obj __asm("a2"),
	Msg msg __asm("a1")
) {
	switch (msg->MethodID)
	{
		case OM_NEW				: return(mNew			(cl,obj,(APTR)msg));
		case OM_SET				: return(mSet			(cl,obj,(APTR)msg));
		case MUIM_HexEdit_CreateDisplayAddress	: return(mCreateDisplayAddress	(cl,obj,(APTR)msg));
	}
	return(DoSuperMethodA(cl,obj,msg));
}

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

void * wmem_init() {
	return HexClass = MUI_CreateCustomClass(NULL,MUIC_HexEdit,NULL,sizeof(struct HexData),HexDispatcher);
}

void wmem_finit() {
	if (HexClass) MUI_DeleteCustomClass(HexClass);
}

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

void
wmem_make(
	int adr		/* start address (logical) of memory to display */
) {
	int n;
	APTR prop;
	ULONG open;
	APTR low,high;
	ULONG off,cur;
	char *title;
	
	/*
	 *  check if there is a free window left
	 *  if window closed dispose it
	 */
	for (n=0; (n<MAXWINMEM) && win_mem[n]; n++) {
		get(win_mem[n],MUIA_Window_Open,&open);
		if (!open) {
			DoMethod(app,OM_REMMEMBER,win_mem[n]);
			MUI_DisposeObject(win_mem[n]);
			win_mem[n] = NULL;
			break;
		}
	}
	if (n==MAXWINMEM) {
		MUI_Request(app,win,0,NULL,"Ok","Sorry, too many windows already open.");
		return;
	}
	
	/*
	 *  check which memory is requested
	 */
	if (adr < header->wdh_BaseMemSize) {
		title = "Base Memory (Chip)";
		low = mem;
		high = ((UBYTE*)mem) + header->wdh_BaseMemSize - 1;
		off = -(ULONG)mem;
		cur = adr;
	} else if ((adr >= header->wdh_ExpMemLog) && (adr < header->wdh_ExpMemLog + header->wdh_ExpMemLen)) {
		title = "Expansion Memory (Fast)";
		low = emem;
		high = ((UBYTE*)emem) + header->wdh_ExpMemLen - 1;
		off = header->wdh_ExpMemLog - (ULONG)emem;
		cur = adr - header->wdh_ExpMemLog;
	} else if ((adr >= header->wdh_SlaveLog) && (adr < header->wdh_SlaveLog + header->wdh_SlaveLen)) {
		title = "Slave Memory";
		low = slave;
		high = ((UBYTE*)slave) + header->wdh_SlaveLen - 1;
		off = header->wdh_SlaveLog - (ULONG)slave;
		cur = adr - header->wdh_SlaveLog;
	} else {
		MUI_Request(app,win,0,NULL,"Ok","Sorry, address is outside the defined memory.");
		return;
	}

	/*
	 *  create the window
	 */
	win_mem[n] = WindowObject,
		MUIA_Window_Title, title,
		MUIA_Window_ID   , n + MAKE_ID('M','E','M','0'),
		MUIA_Window_Menustrip, MUI_MakeObject(MUIO_MenustripNM,MenuDataMem,0),
		WindowContents, VGroup,
			Child, HGroup,
				Child, Label2("Goto Offset:"),
				Child, gad_goto[n] = BetterStringObject,
					StringFrame,
					MUIA_String_Accept , StringHexAccept,
					MUIA_String_MaxLen , 10,
					MUIA_String_Format , MUIV_String_Format_Right,
					MUIA_ShortHelp, "Goto Offset",
					MUIA_String_Contents, "$",
					End,
				End,
			Child, HGroup,
				Child, gad_hex[n] = NewObject(HexClass->mcc_Class,0, VirtualFrame,
					MUIA_HexEdit_LowBound, low,
					MUIA_HexEdit_HighBound, high,
					MUIA_HexEdit_BaseAddressOffset, off,
					MUIA_HexEdit_AddressChars, 0,	/* must be after Low, High and BaseAddressOffset! */
					MUIA_HexEdit_EditMode, TRUE,
					MUIA_HexEdit_SelectMode, MUIV_HexEdit_SelectMode_Byte,
					//MUIA_HexEdit_CursorAddress, cur,
					MUIA_CycleChain, 1,
					End,
				Child, prop = ScrollbarObject,
					MUIA_Prop_UseWinBorder, MUIV_Prop_UseWinBorder_Right,
					End,
				End,
			End,
		End;
	if (!win_mem[n]) {
		MUI_Request(app,win,0,NULL,"Ok","Couldn't open window.");
		return;
	}

	set(gad_hex[n],MUIA_HexEdit_BaseAddressOffset,off);
	set(gad_hex[n],MUIA_HexEdit_AddressChars,2);

	/*
	 *  add window to application and open it
	 */
	DoMethod(app,OM_ADDMEMBER,win_mem[n]);
	set(gad_hex[n],MUIA_HexEdit_PropObject,prop);
	DoMethod(win_mem[n],MUIM_Notify,MUIA_Window_CloseRequest,TRUE,win_mem[n],3,MUIM_Set,MUIA_Window_Open,FALSE);
	DoMethod(gad_goto[n],MUIM_Notify,MUIA_String_Accept,TRUE,gad_hex[n],3,MUIM_Set,MUIA_HexEdit_My,n);
	set(win_mem[n],MUIA_Window_Open,TRUE);

}

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

void wmem_goto ( int num )
{

}

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

