/*
   ***************************************************************************
   *
   * Datei:
   *    RSysMonitor.c
   *
   * Inhalt:
   *
   *      --- Globale Routinen ---
   *
   *    void Monitor ( Objid *obj );
   *
   *      --- Lokale  Routinen ---
   *
   *    static int OpenMemMonitorWindow ( void );
   *    static void SetListView ( void );
   *    static void ConvertLine ( int type , int offset , char *memline , char *nodeline );
   *    static void FreeDisplayMemory ( void );
   *    static void ReInitDisplays ( void );
   *    static int InitDisplays ( void );
   *    static void ReadNextBlock ( void );
   *    static void ReadPrevBlock ( void );
   *    static int DisplayCYGadClicked ( void );
   *    static int SaveCGadClicked ( void );
   *    static int ASCIIGadClicked ( void );
   *    static int SaveHexGadClicked ( void );
   *    static int SaveGadClicked ( void );
   *    static int PageBackwardGadClicked ( void );
   *    static int PageForwardGadClicked ( void );
   *    static int StartAdrGadClicked ( void );
   *    static int HandleMemMonitorIDCMP ( void );
   *
   * Bemerkungen:
   *    Speichermonitor von RSys. Wird verwendet, wenn keine
   *    Modifikation von Objekten möglich ist.
   *
   * Erstellungsdatum:
   *    29-Jul-93    Rolf Böhme
   *
   * Änderungen:
   *    29-Jul-93    Rolf Böhme    Erstellung
   *
   ***************************************************************************
 */

#include "RSys.h"
#include "protos.h"


static int DisplayCYGadClicked (void);
static int SaveCGadClicked (void);
static int ASCIIGadClicked (void);
static int SaveHexGadClicked (void);
static int SaveGadClicked (void);
static int PageBackwardGadClicked (void);
static int PageForwardGadClicked (void);
static int StartAdrGadClicked (void);

static WINDOW *MemMonitorWnd = NULL;
static GADGET *MemMonitorGList = NULL;
INTUIMESSAGE MemMonitorMsg;
static GADGET *MemMonitorGadgets[13];
static UWORD MemMonitorLeft = 37;
static UWORD MemMonitorTop = 14;
static UWORD MemMonitorWidth = 616;
static UWORD MemMonitorHeight = 217;
static UBYTE *MemMonitorWdt = (UBYTE *) NAME " - Monitor";

static UBYTE *DisplayCYGad0Labels[] =
{
  (UBYTE *) "Hex/ASCII",
  (UBYTE *) "Hex",
  (UBYTE *) "ASCII",
  NULL};

static char *headerline[3] =
{
  "     00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0123456789ABCDEF",
  "     00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F",
  "     0123456789ABCDEF"
};

static enum displaytypes
  {
    DT_hexascii, DT_hex, DT_ascii
  };

static int CurrentDisplayType = DT_hexascii;
static char Head[BUFSIZE];

static LIST DisplayLists[DISPLAYCOUNT];
static NODE DisplayNodes[DISPLAYCOUNT][LINECOUNT];

static union
  {
    char memblock[BLOCKSIZE];
    char memparts[LINECOUNT][LINESIZE];
    ULONG memlongs[LINECOUNT][LINESIZE / sizeof (ULONG)];
  }
MemoryBlock;

static ULONG CurrentAddress, FirstAddress;

static REMEMBER *MonKey = NULL;

static UWORD MemMonitorGTypes[] =
{
  LISTVIEW_KIND,
  CYCLE_KIND,
  BUTTON_KIND,
  BUTTON_KIND,
  BUTTON_KIND,
  BUTTON_KIND,
  BUTTON_KIND,
  BUTTON_KIND,
  TEXT_KIND,
  TEXT_KIND,
  BUTTON_KIND,
  TEXT_KIND,
  TEXT_KIND
};

static NEWGADGET MemMonitorNGad[] =
{
  12, 50, 589, 136, NULL, NULL, GD_MemoryLVGad, 0, NULL, NULL,
  476, 19, 125, 13, (UBYTE *) "Display", NULL, GD_DisplayCYGad, PLACETEXT_LEFT, NULL, (APTR) DisplayCYGadClicked,
  12, 201, 142, 13, (UBYTE *) "Save C-Code", NULL, GD_SaveCGad, PLACETEXT_IN, NULL, (APTR) SaveCGadClicked,
  161, 201, 142, 13, (UBYTE *) "Save ASCII", NULL, GD_ASCIIGad, PLACETEXT_IN, NULL, (APTR) ASCIIGadClicked,
  310, 201, 142, 13, (UBYTE *) "Save Hex", NULL, GD_SaveHexGad, PLACETEXT_IN, NULL, (APTR) SaveHexGadClicked,
  459, 201, 142, 13, (UBYTE *) "Save as is", NULL, GD_SaveAsIsGad, PLACETEXT_IN, NULL, (APTR) SaveGadClicked,
  12, 186, 193, 13, (UBYTE *) "<<< Prev 512 Bytes", NULL, GD_PageBackwardGad, PLACETEXT_IN, NULL, (APTR) PageBackwardGadClicked,
  408, 186, 193, 13, (UBYTE *) "Next 512 Bytes >>>", NULL, GD_PageForwardGad, PLACETEXT_IN, NULL, (APTR) PageForwardGadClicked,
  232, 19, 89, 13, (UBYTE *) "Current", NULL, GD_AddressTGad, PLACETEXT_LEFT, NULL, NULL,
  64, 19, 89, 13, (UBYTE *) "Start", NULL, GD_StartAdrTGad, PLACETEXT_LEFT, NULL, NULL,
  210, 186, 193, 13, (UBYTE *) "To start address", NULL, GD_StartAdrGad, PLACETEXT_IN, NULL, (APTR) StartAdrGadClicked,
  64, 4, 537, 13, (UBYTE *) "Entry", NULL, GD_EntryInfo, PLACETEXT_LEFT, NULL, NULL,
  12, 36, 589, 13, (UBYTE *) NULL, NULL, GD_HeaderLine, PLACETEXT_LEFT, NULL, NULL
};

static ULONG *MemMonitorGTags[] =
{
  (ULONG *) (GTLV_ReadOnly), (ULONG *) TRUE, (ULONG *) (TAG_DONE),
  (ULONG *) (GTCY_Labels), (ULONG *) & DisplayCYGad0Labels[0], (ULONG *) (TAG_DONE),
  (ULONG *) (TAG_DONE),
  (ULONG *) (TAG_DONE),
  (ULONG *) (TAG_DONE),
  (ULONG *) (TAG_DONE),
  (ULONG *) (TAG_DONE),
  (ULONG *) (TAG_DONE),
  (ULONG *) (GTTX_Border), (ULONG *) TRUE, (ULONG *) (TAG_DONE),
  (ULONG *) (GTTX_Border), (ULONG *) TRUE, (ULONG *) (TAG_DONE),
  (ULONG *) (TAG_DONE),
  (ULONG *) (GTTX_Border), (ULONG *) TRUE, (ULONG *) (TAG_DONE),
  (ULONG *) (GTTX_Border), (ULONG *) TRUE, (ULONG *) (TAG_DONE),
};

static int
OpenMemMonitorWindow (void)
{
  NEWGADGET ng;
  GADGET *g;
  UWORD lc, tc;
  UWORD wleft = MemMonitorLeft, wtop = MemMonitorTop, ww, wh;
  int gl[] =
  {GD_MemoryLVGad - GD_MemoryLVGad, GD_AddressTGad - GD_MemoryLVGad,
   GD_StartAdrTGad - GD_MemoryLVGad, GD_EntryInfo - GD_MemoryLVGad,
   GD_HeaderLine - GD_MemoryLVGad};

  AdjustWindowDimensions (Scr, MemMonitorLeft, MemMonitorTop,
			  MemMonitorWidth, MemMonitorHeight,
			  &wleft, &wtop, &ww, &wh);

  if (!(g = CreateContext (&MemMonitorGList)))
    return (1L);

  for (lc = 0, tc = 0; lc < MemMonitor_CNT; lc++)
    {

      CopyMem ((char *) &MemMonitorNGad[lc], (char *) &ng,
	       (long) sizeof (NEWGADGET));

      ng.ng_VisualInfo = VisualInfo;
      ng.ng_TextAttr = Font;
      ng.ng_LeftEdge = OffX + ComputeX (ng.ng_LeftEdge);
      ng.ng_TopEdge = OffY + ComputeY (ng.ng_TopEdge);
      ng.ng_Width = ComputeX (ng.ng_Width);
      ng.ng_Height = ComputeY (ng.ng_Height);

      MemMonitorGadgets[lc] = g = CreateGadgetA ((ULONG) MemMonitorGTypes[lc], g, &ng, (TAGITEM *) & MemMonitorGTags[tc]);

      while (MemMonitorGTags[tc])
	tc += 2;
      tc++;

      if (NOT g)
	return (2L);
    }

  if (!(MemMonitorWnd = OpenWindowTags (NULL,
					WA_Left, wleft,
					WA_Top, wtop,
					WA_Width, ww,
					WA_Height, wh,
					WA_IDCMP, LISTVIEWIDCMP |
					CYCLEIDCMP | BUTTONIDCMP |
					TEXTIDCMP | IDCMP_CLOSEWINDOW |
					IDCMP_REFRESHWINDOW |
					IDCMP_VANILLAKEY,
					WA_Flags, WFLG_DRAGBAR |
					WFLG_DEPTHGADGET |
					WFLG_CLOSEGADGET |
					WFLG_SMART_REFRESH |
					WFLG_RMBTRAP,
					WA_Title, MemMonitorWdt,
					WA_PubScreenFallBack, TRUE,
					WA_PubScreen, Scr,
					TAG_DONE)))
    return (4L);

  RefreshRastPort (MemMonitorWnd, MemMonitorGadgets, gl, 5, FALSE, MemMonitorGList);

  return (0L);
}

static void
SetTextGadgets (void)
{
  char curraddr[10], addr[10];

  sprintf (curraddr, "%08lX", CurrentAddress);
  sprintf (addr, "%08lX", FirstAddress);

  GT_SetGadgetAttrs (MemMonitorGadgets[GD_AddressTGad - GD_MemoryLVGad], MemMonitorWnd,
		     NULL,
		     GTTX_Text, (UBYTE *) curraddr,
		     TAG_DONE);

  GT_SetGadgetAttrs (MemMonitorGadgets[GD_StartAdrTGad - GD_MemoryLVGad], MemMonitorWnd,
		     NULL,
		     GTTX_Text, (UBYTE *) addr,
		     TAG_DONE);

  GT_SetGadgetAttrs (MemMonitorGadgets[GD_HeaderLine - GD_MemoryLVGad], MemMonitorWnd,
		     NULL,
		     GTTX_Text, (UBYTE *) headerline[CurrentDisplayType],
		     TAG_DONE);

  return;
}

static void
SetListView (void)
{
  InitListView (MemMonitorWnd, MemMonitorGadgets[GD_MemoryLVGad - GD_MemoryLVGad],
		&DisplayLists[CurrentDisplayType], UNSETLVPOS);

  return;
}

static void
ConvertLine (int type, int offset, char *memline, char *nodeline)
{
  char bytehex[LINESIZE][4], i, offsetstr[7], charline[LINESIZE + 1];

  for (i = 0; i < LINESIZE; i++)
    {
      sprintf (bytehex[i], "%02lX ", (unsigned char) memline[i]);
      charline[i] = (isprint ((int) memline[i]) ? memline[i] : '.');
    }

  charline[LINESIZE] = STRINGEND;

  sprintf (offsetstr, "%03lX: ", offset * 16);

  switch (type)
    {
    case DT_hexascii:
      strncpy (nodeline, offsetstr, 5);
      for (i = 0; i < LINESIZE; i++)
	strncat (nodeline, bytehex[i], 4);
      strncat (nodeline, charline, LINESIZE);
      break;

    case DT_hex:
      strncpy (nodeline, offsetstr, 5);
      for (i = 0; i < LINESIZE; i++)
	strncat (nodeline, bytehex[i], 4);
      break;

    case DT_ascii:
      strncpy (nodeline, offsetstr, 5);
      strncat (nodeline, charline, LINESIZE);
      break;
    }

  return;
}

static void
FreeDisplayMemory (void)
{
  FreeRemember (MONKEY, TRUE);
  return;
}

static void
ReInitDisplays (void)
{
  char memline[LINESIZE + 1];
  char displaytype, j;

  SetTextGadgets ();

  Forbid ();
  CopyMem ((APTR) CurrentAddress, (APTR) & MemoryBlock, BLOCKSIZE);
  Permit ();

  for (displaytype = 0; displaytype < DISPLAYCOUNT; displaytype++)
    {
      NewList (&DisplayLists[displaytype]);

      for (j = 0; j < LINECOUNT; j++)
	{
	  CopyMem (&MemoryBlock.memparts[j][0], &memline[0], LINESIZE);

	  ConvertLine ((int) displaytype, (int) j, memline, DisplayNodes[displaytype][j].ln_Name);

	  AddTail (&DisplayLists[displaytype], &DisplayNodes[displaytype][j]);
	}
    }

  SetListView ();

  return;
}

static int
InitDisplays (void)
{
  char memline[LINESIZE + 1];
  char displaytype, j;

  SetTextGadgets ();

  Forbid ();
  CopyMem ((APTR) CurrentAddress, (APTR) & MemoryBlock, BLOCKSIZE);
  Permit ();

  for (displaytype = 0; displaytype < DISPLAYCOUNT; displaytype++)
    {
      NewList (&DisplayLists[displaytype]);

      for (j = 0; j < LINECOUNT; j++)
	{
	  DisplayNodes[displaytype][j].ln_Name = AllocMonMem (3 * (LINESIZE + 9));

	  if (!DisplayNodes[displaytype][j].ln_Name)
	    return FALSE;

	  DisplayNodes[displaytype][j].ln_Type = NT_USER;
	  DisplayNodes[displaytype][j].ln_Pri = 0;

	  CopyMem (&MemoryBlock.memparts[j][0], &memline[0], LINESIZE);

	  ConvertLine ((int) displaytype, (int) j, memline, DisplayNodes[displaytype][j].ln_Name);

	  AddTail (&DisplayLists[displaytype], &DisplayNodes[displaytype][j]);
	}
    }

  SetListView ();

  return TRUE;
}

static void
ReadNextBlock (void)
{
  if (CurrentAddress + 512L <= 0x01000000L)
    CurrentAddress += 512L;
  ReInitDisplays ();

  return;
}

static void
ReadPrevBlock (void)
{
  if (CurrentAddress - 512 >= 0)
    CurrentAddress -= 512L;
  ReInitDisplays ();

  return;
}

static int
DisplayCYGadClicked (void)
{
  CurrentDisplayType = MemMonitorMsg.Code;
  SetListView ();

  return TRUE;
}

static int
SaveCGadClicked (void)
{
  /* routine when gadget "Save C-Code" is clicked. */
  BPTR fout;
  int i;

  sprintf (Head, "/\*\n   RSys-C-Code Generator"
	   "\n   Memory at 0x%08lX\n\*/\n\n", CurrentAddress);

  if (GetFile (MemMonitorWnd, "T:", "RSysData.c", "#?.c", "Save C-Code...", "Save"))
    {
      fout = AskFileMode ((char *) _fullpath, MemMonitorWnd);

      if (fout)
	{
	  FPuts (fout, (STRPTR) Head);
	  FPuts (fout, (STRPTR) "ULONG RSysData[] = \{\n");

	  for (i = 0; i < LINECOUNT; i++)
	    FPrintf (fout, (STRPTR) "   0x%08lX, 0x%08lX, 0x%08lX, 0x%08lX,\n",
		     MemoryBlock.memlongs[i][0],
		     MemoryBlock.memlongs[i][1],
		     MemoryBlock.memlongs[i][2],
		     MemoryBlock.memlongs[i][3]);

	  FPuts (fout, (STRPTR) "\};\n/\* End of Code \*/\n");

	  Close (fout);
	}
    }

  return TRUE;
}

static int
ASCIIGadClicked (void)
{
  /* routine when gadget "Save ASCII" is clicked. */
  sprintf (Head, "Memory at 0x%08lX", CurrentAddress);

  if (GetFile (MemMonitorWnd, "T:", "RSysMemoryASCII.dat", "#?.dat",
	       "Save as ASCII-Data...", "Save"))
    SaveList (MemMonitorWnd, (char *) _fullpath, Head,
	      &DisplayLists[DT_ascii], FALSE);

  return TRUE;
}

static int
SaveHexGadClicked (void)
{
  /* routine when gadget "Save Hex" is clicked. */

  sprintf (Head, "Memory at 0x%08lX", CurrentAddress);

  if (GetFile (MemMonitorWnd, "T:", "RSysMemoryHEX.dat", "#?.dat",
	       "Save as Hex-Datas...", "Save"))
    SaveList (MemMonitorWnd, (char *) _fullpath, Head,
	      &DisplayLists[DT_hex], FALSE);
  return TRUE;
}

static int
SaveGadClicked (void)
{
  /* routine when gadget "Save as is" is clicked. */

  sprintf (Head, "Memory at 0x%08lX", CurrentAddress);

  if (GetFile (MemMonitorWnd, "T:", "MemoryList.dat", "#?.dat",
	       "Save List as is...", "Save"))
    SaveList (MemMonitorWnd, (char *) _fullpath, Head,
	      &DisplayLists[CurrentDisplayType], FALSE);

  return TRUE;
}

static int
PageBackwardGadClicked (void)
{
  /* routine when gadget "<<< Prev 512 Bytes" is clicked. */
  ReadPrevBlock ();

  return TRUE;
}

static int
PageForwardGadClicked (void)
{
  /* routine when gadget "Next 512 Bytes >>>" is clicked. */
  ReadNextBlock ();

  return TRUE;
}

static int
StartAdrGadClicked (void)
{
  CurrentAddress = FirstAddress;

  ReInitDisplays ();

  return TRUE;
}

static int
HandleMemMonitorIDCMP (void)
{
  INTUIMESSAGE *m;
  int (*func) (void);
  BOOL running = TRUE;

  while (m = GT_GetIMsg (MemMonitorWnd->UserPort))
    {
      CopyMem ((char *) m, (char *) &MemMonitorMsg,
	       (long) sizeof (INTUIMESSAGE));

      GT_ReplyIMsg (m);

      switch (MemMonitorMsg.Class)
	{
	case IDCMP_REFRESHWINDOW:
	  MakeWindowRefresh (MemMonitorWnd);
	  break;

	case IDCMP_CLOSEWINDOW:
	  running = FALSE;
	  break;

	case IDCMP_VANILLAKEY:
	  if (MemMonitorMsg.Code == ESC)
	    running = FALSE;
	  break;

	case IDCMP_GADGETUP:
	case IDCMP_GADGETDOWN:
	  HandleHelp ((enum RSysNumber)
		      ((GADGET *) MemMonitorMsg.IAddress)->GadgetID);
	  func = (int (*)()) ((GADGET *) MemMonitorMsg.IAddress)->UserData;
	  running = func ();
	  break;
	}
    }
  return (running);
}

void
Monitor (RSYS_Objid * obj, char *entrystring)
{
  RSYS_ValidMemoryNode *vmnode;
  char header[2 * BUFSIZE];

  HandleHelp (MONITOR);

  CurrentDisplayType = DT_hexascii;

  CurrentAddress = (ULONG) obj->address;
  FirstAddress = (ULONG) obj->address;

  vmnode = CheckNode (CurrentAddress);

  if (!vmnode)
    ErrorHandle ("Address not found in table", MEMORY_ERR, TYPE_FAIL, NO_KILL);
  else if (!IsValid (vmnode) && !MyEasyRequest (SysWnd, (STRPTR) NAME " - Memory validation info",
					     (STRPTR) "Call Monitor|Cancel",
	(STRPTR) "You requested a memory start address which is not valid\n"
		    "and can produce an Enforcer Hit! The Attributes are:\n"
						"%s \n%s \n%s\nComment: %s\n"
			 "Do you want to call the " NAME "-Monitor anyway?",
						"- Invalid",
						(IsWriteProtect (vmnode) ? "- Write protected" : "- Not write protected"),
		 (IsCacheable (vmnode) ? "- Cacheable" : "- Not cacheable"),
						vmnode->vm_comment))
    return;

  if (OpenASysWindow (OpenMemMonitorWindow, NO_KILL))
    {
      LockMainWindow (WIN_LOCK);

      if (vmnode)
	sprintf (header, "%s [%s]", &entrystring[9], vmnode->vm_comment);
      else
	sprintf (header, "%s [no valid memory info]", &entrystring[9]);

      GT_SetGadgetAttrs (MemMonitorGadgets[GD_EntryInfo - GD_MemoryLVGad], MemMonitorWnd,
			 NULL,
			 GTTX_Text, (UBYTE *) header,
			 TAG_DONE);

      if (InitDisplays ())
	while (HandleMemMonitorIDCMP ());
      else
	ErrorHandle ("Display nodes", MEMORY_ERR, ALLOC_FAIL, NO_KILL);

      FreeDisplayMemory ();

      CloseASysWindow (&MemMonitorWnd, &MemMonitorGList, NULL);

      LockMainWindow (WIN_UNLOCK);
    }

  return;
}
