/****h *SysMemory/SysMemViewer.c ************************************
**
** NAME
**    SysMemViewer.c
**
** DESCRIPTION
**    These functions form a GUI for the SysMemory program so that
**    the user can view the memory that was selected from SysMemory.
**
** FUNCTIONAL INTERFACE:
**
**    PUBLIC int ViewMemory( int type, ULONG startaddress );
**
** GUI Designed by : Jim Steichen
*********************************************************************
*/

#include <string.h>

#include <exec/types.h>

#include <AmigaDOSErrs.h>

#include <intuition/intuition.h>
#include <intuition/classes.h>
#include <intuition/classusr.h>
#include <intuition/gadgetclass.h>

#include <libraries/gadtools.h>

#include <graphics/displayinfo.h>
#include <graphics/gfxbase.h>

#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/utility_protos.h>
#include <clib/diskfont_protos.h>

#include "CPGM:GlobalObjects/CommonFuncs.h"

#define StrBfPtr( g ) (((struct StringInfo *)g->SpecialInfo)->Buffer)

#define MemLV      0
#define StartAddr  1
#define NextPage   2
#define PrevPage   3
#define DoneBt     4
#define Update     5

#define MV_CNT     6

#define START_ADDR_STRING StrBfPtr( MVGadgets[ StartAddr ] )

IMPORT struct Screen *Scr;
IMPORT UBYTE         *PubScreenName;
IMPORT APTR           VisualInfo;

IMPORT struct TextAttr *Font, Attr;
IMPORT struct TextFont *MVFont = NULL;

IMPORT struct CompFont  CFont;

PRIVATE struct Window       *MVWnd   = NULL;
PRIVATE struct Gadget       *MVGList = NULL;
PRIVATE struct IntuiMessage  MVMsg;
PRIVATE struct Gadget       *MVGadgets[ MV_CNT ];

PRIVATE UWORD  MVLeft   = 38;
PRIVATE UWORD  MVTop    = 16;
PRIVATE UWORD  MVWidth  = 540;
PRIVATE UWORD  MVHeight = 296;
PRIVATE UBYTE *MVWdt    = (UBYTE *) "System Memory Viewer ";

// -------------------------------------------------------------------

#define MAXNODE    20
#define NODELENGTH 64

PRIVATE struct MinList MemLVList;

PRIVATE struct Node MemLVNode;
PRIVATE struct Node MemLVNodes[ MAXNODE ] = { NULL, };

PRIVATE UBYTE       NodeStrs[ MAXNODE * NODELENGTH ] = "";

// -------------------------------------------------------------------

PRIVATE UWORD MVGTypes[] = {

   LISTVIEW_KIND, STRING_KIND, BUTTON_KIND,
   BUTTON_KIND,   BUTTON_KIND,  BUTTON_KIND
};

PRIVATE int MemLVClicked(     void );
PRIVATE int StartAddrClicked( void );
PRIVATE int NextPageClicked(  void );
PRIVATE int PrevPageClicked(  void );
PRIVATE int DoneBtClicked(    void );
PRIVATE int UpdateClicked(    void );

PRIVATE struct NewGadget MVNGad[] = {

     4,  27, 531, 240, (UBYTE *) "Memory Contents:", NULL, MemLV,
   PLACETEXT_ABOVE, NULL, (APTR) MemLVClicked,
  
   106, 269,  81,  18, (UBYTE *) "Start Addr:",      NULL, StartAddr,
   PLACETEXT_LEFT, NULL, (APTR) StartAddrClicked,

   198, 269,  87,  18, (UBYTE *) "_Next Page",       NULL, NextPage,
   PLACETEXT_IN, NULL, (APTR) NextPageClicked,

   294, 269,  87,  18, (UBYTE *) "_Prev Page",       NULL, PrevPage,
   PLACETEXT_IN, NULL, (APTR) PrevPageClicked,

   467, 269,  67,  18, (UBYTE *) "_Done!",           NULL, DoneBt,
   PLACETEXT_IN, NULL, (APTR) DoneBtClicked,

   385, 236,  75,  18, (UBYTE *) "_Update",          NULL, Update, 
   PLACETEXT_IN, NULL, (APTR) UpdateClicked
};

PRIVATE ULONG MVGTags[] = {

   GTLV_Labels, (ULONG) &MemLV0List, GTLV_ReadOnly, TRUE, 
   LAYOUTA_Spacing, 2, TAG_DONE,

   GA_TabCycle, FALSE, GTST_String, (STRPTR) "00000000", 
   GTST_MaxChars, 10, 
   STRINGA_Justification, GACT_STRINGCENTER, TAG_DONE,

   GT_Underscore, '_', TAG_DONE,
   GT_Underscore, '_', TAG_DONE,
   GT_Underscore, '_', TAG_DONE,
   GT_Underscore, '_', TAG_DONE
};


PRIVATE int MemLVClicked( void )
{
   return( (int) TRUE );
}

PRIVATE UBYTE t[80], *title = &t[0];

PRIVATE void UpdateTitle( ULONG address )
{
   sprintf( title, "%s (at %08LX):", MVWdt, address );
   SetWindowTitles( MVWnd, title, (UBYTE *) -1 );
   return;
}

PRIVATE ULONG PrevAddress    = 0L;
PRIVATE ULONG CurrentAddress = 0L;

PRIVATE int StartAddrClicked( void )
{
   PrevAddress = CurrentAddress;

   // Convert the string gadget contents to an address:   
   (void) stch_l( START_ADDR_STRING, &CurrentAddress );

   if (CurrentAddress < 0)
      {
      // Tell the user what a bonehead he is:
      (void) Handle_Problem( "", "", NULL );
      
      CurrentAddress = PrevAddress;
      }
         
   UpdateTitle( CurrentAddress );
   return( (int) TRUE );
}

PRIVATE int NextPageClicked( void )
{
   PrevAddress = CurrentAddress;

   // Kill old nodes & re-calculate the display:

   CurrentAddress += (16 * MAXNODE);

   UpdateTitle( CurrentAddress );
   return( (int) TRUE );
}

PRIVATE int PrevPageClicked( void )
{
   /* routine when gadget "_Prev Page" is clicked. */
   CurrentAddress = PrevAddress;

   UpdateTitle( CurrentAddress );
   return( (int) TRUE );
}

PRIVATE void CloseMVWindow( void )
{
   if (MVWnd != NULL) 
      {
      CloseWindow( MVWnd );
      MVWnd = NULL;
      }

   if (MVGList != NULL) 
      {
      FreeGadgets( MVGList );
      MVGList = NULL;
      }

   if (MVFont != NULL) 
      {
      CloseFont( MVFont );
      MVFont = NULL;
      }

   return;
}

PRIVATE int MVCloseWindow( void )
{
   CloseMVWindow();
   return( (int) FALSE );
}

PRIVATE int DoneBtClicked( void )
{
   return( MVCloseWindow() );
}

PRIVATE int UpdateClicked( void )
{
   int   i;
   ULONG temp = (ULONG) CurrentAddress;
   
   HideListFromView( MVGadgets[ MemLV ], MVWnd );

   for (i = 1; i <= MAXNODE; i++)
       NodeStrs[ i * NODELENGTH ] = '\0'; // Kill old ListView strings.

   // Re-construct the memory strings, starting at CurrentAddress:
   
   for (i = 0; i < MAXNODE; i++)
      {
      (void) MakeHexASCIIStr( &NodeStrs[i * NODELENGTH ], 
                              (char *) temp, 16 );
      temp += 16;
      }

   ModifyListView( MVGadgets[ MemLV ], MVWnd, &MemLVList, NULL ); 
   return( (int) TRUE );
}

PRIVATE void MVRender( char *description )
{
   struct IntuiText it;
   char             d[80], *txt = &d[0];

   // "You Selected Expansion Memory (07000000-08000000):"
   // "You Selected Chip Memory (00001000-001FFFFF):"

   ComputeFont( Scr, Font, &CFont, MVWidth, MVHeight );

   strcpy( txt, description ); 

   it.NextText  = NULL;
   it.FrontPen  = 2;
   it.BackPen   = 0;
   it.DrawMode  = JAM1;
   it.IText     = (UBYTE *) txt;       
   it.ITextFont = Font;
   it.LeftEdge  = 277;
   it.TopEdge   = 6;
   
   it.LeftEdge  = CFont.OffX + ComputeX( CFont.FontX, it.LeftEdge ) 
                  - (IntuiTextLength( &it ) >> 1);
      
   it.TopEdge   = CFont.OffY + ComputeY( CFont.FontY, it.TopEdge ) 
                  - (Font->ta_YSize >> 1);

   PrintIText( MVWnd->RPort, &it, 0, 0 );

   return;
}

PRIVATE int OpenMVWindow( char *description )
{
   struct NewGadget  ng;
   struct Gadget    *g;
   UWORD             lc, tc;
   UWORD             wleft = MVLeft, wtop = MVTop, ww, wh;

   ComputeFont( Scr, Font, &CFont, MVWidth, MVHeight );

   ww = ComputeX( CFont.FontX, MVWidth );
   wh = ComputeY( CFont.FontY, MVHeight );

   if ((wleft + ww + CFont.OffX + Scr->WBorRight) > Scr->Width) 
      wleft = Scr->Width - ww;
   
   if ((wtop + wh + CFont.OffY + Scr->WBorBottom) > Scr->Height) 
      wtop = Scr->Height - wh;

   if ( !(MVFont = OpenDiskFont( Font )))
      return( -5 );

   if ( !(g = CreateContext( &MVGList )))
      return( -1 );

   for (lc = 0, tc = 0; lc < MV_CNT; lc++) 
      {
      CopyMem( (char *) &MVNGad[ lc ], (char *) &ng, 
               (long) sizeof( struct NewGadget )
             );

      ng.ng_VisualInfo = VisualInfo;
      ng.ng_TextAttr   = Font;
      ng.ng_LeftEdge   = CFont.OffX + ComputeX( CFont.FontX,
                                                ng.ng_LeftEdge
                                              );

      ng.ng_TopEdge    = CFont.OffY + ComputeY( CFont.FontY,
                                                ng.ng_TopEdge
                                              );

      ng.ng_Width      = ComputeX( CFont.FontX, ng.ng_Width );
      ng.ng_Height     = ComputeY( CFont.FontY, ng.ng_Height);

      MVGadgets[ lc ] = g = CreateGadgetA( (ULONG) MVGTypes[ lc ], 
                              g, 
                              &ng, 
                              (struct TagItem *) &MVGTags[ tc ] );

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

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

   if ( !(MVWnd = OpenWindowTags( NULL,

            WA_Left,        wleft,
            WA_Top,         wtop,
            WA_Width,       ww + CFont.OffX + Scr->WBorRight,
            WA_Height,      wh + CFont.OffY + Scr->WBorBottom,
            WA_IDCMP,       LISTVIEWIDCMP | INTEGERIDCMP | BUTTONIDCMP
              | IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | IDCMP_REFRESHWINDOW,

            WA_Flags,       WFLG_DRAGBAR | WFLG_DEPTHGADGET
              | WFLG_CLOSEGADGET | WFLG_SMART_REFRESH | WFLG_ACTIVATE
              | WFLG_RMBTRAP,

            WA_Gadgets,     MVGList,
            WA_Title,       MVWdt,
            WA_ScreenTitle, "System Memory Viewer ©1999 by J.T. Steichen",
            TAG_DONE ))
      )
      return( -4 );

   GT_RefreshWindow( MVWnd, NULL );
   MVRender( description );
   return( 0 );
}

PRIVATE int MVVanillaKey( int whichkey )
{
   int rval = TRUE;
   
   switch (whichkey)
      {
      case 'd':
      case 'D':
      case 'q':
      case 'Q':
         rval = DoneBtClicked();
         break;
         
      case 'p':
      case 'P':
         rval = PrevPageClicked();
         break;
         
      case 'n':
      case 'N':

         rval = NextPageClicked();
         break;

      case 'u':
      case 'U':
         rval = UpdateClicked();
         break;
               
      default:
         break;
      }
      
   return( rval );
}

PRIVATE int HandleMVIDCMP( void )
{
   struct IntuiMessage  *m;
   int                 (*func)( void );
   BOOL                  running = TRUE;

   while (running == TRUE)
      {
      if ((m = GT_GetIMsg( MVWnd->UserPort )) == NULL) 
         {
         (void) Wait( 1L << MVWnd->UserPort->mp_SigBit );
         continue;
         }

      CopyMem( (char *) m, (char *) &MVMsg, 
               (long) sizeof( struct IntuiMessage )
             );

      GT_ReplyIMsg( m );

      switch (MVMsg.Class) 
         {
         case IDCMP_REFRESHWINDOW:
            GT_BeginRefresh( MVWnd );
            MVRender();
            GT_EndRefresh( MVWnd, TRUE );
            break;

         case IDCMP_CLOSEWINDOW:
            running = MVCloseWindow();
            break;

         case IDCMP_VANILLAKEY:
            running = MVVanillaKey( (int) MVMsg.Code );
            break;

         case IDCMP_GADGETUP:
         // case IDCMP_GADGETDOWN:
            
            func = (void *) ((struct Gadget *)MVMsg.IAddress)->UserData;
            
            if (func != NULL)
               running = func();
            
            break;
         }
      }

   return( running );
}

/****i *InitMemoryNodes() ---------------------------------------------
**
** NAME
**    InitMemoryNodes()
**
** NOTES
**    This is what a typical list item might look like:
**
**    "00000000: aaaaffff 22334455 22334455 66554433 ................"
** --------------------------------------------------------------------
*/

PRIVATE int InitMemoryNodes( ULONG startaddress )
{
   int i = 0;
   
   MVLVNode.ln_Succ = (struct Node *) MVLVList.mlh_Tail;
   MVLVNode.ln_Pred = (struct Node *) MVLVList.mlh_Head;
   MVLVNode.ln_Type = 0;

   MVLVNodes[0] = MVLVNode;

   SetNotifyWindow( MVWnd );

   for (i = 0; i < MAXNODE; i++)
      {
      MVLVNodes[i].ln_Name = &NodeStrs[ i * NODELENGTH ];
      MVLVNodes[i].ln_Pri  = MAXNODE - i;
      }

   NewList( (struct List *) &MVLVList );      

   for (i = 0; i < MAXNODE; i++)
      Enqueue( (struct List *) &MVLVList, &MVLVNodes[ i ] );

   // Make the list:
   (void) MakeMemoryList();   

   ModifyListView( MVGadgets[ MemLV ], MVWnd, 
                   (struct List *) &MVLVList, NULL
                 );

   GT_RefreshWindow( MVWnd, NULL );
   return( 0 );
}

PUBLIC int ViewMemory( int type, ULONG startaddress, ULONG endaddress )
{
   CurrentAddress = startaddress;

   if (type == MEMF_CHIP)
      sprintf( description, 
               "You've selected Chip Memory (%08LX-%08LX):", 
               startaddress, endaddress
             );
   else if (type == MEMF_FAST)
      sprintf( description, 
               "You've selected Expansion Memory (%08LX-%08LX):", 
               startaddress, endaddress
             );
      
   if (OpenMVWindow( description ) < 0)
      {
      int ans = 0;
      
      ans = Handle_Problem( "Couldn't open Memory Viewer!", 
                            "System Problem:", NULL
                          );
      return( ans );
      }
       
   (void) InitMemoryNodes( CurrentAddress );

   (void) HandleMVIDCMP();
   MVCloseWindow();        // Just in case.
   
   return( 0 );
}

/* ------------------- END of SysMemViewer.c file! --------------- */
