/**
 * Scout - The Amiga System Monitor
 *
 *------------------------------------------------------------------
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * You must not use this source code to gain profit of any kind!
 *
 *------------------------------------------------------------------
 *
 * @author Andreas Gelhausen
 * @author Richard Körber <rkoerber@gmx.de>
 */



#include "system_headers.h"

int  classcnt;

static APTR classmoretext0,classmoretext1,classmoretext2,classmoretext3,classmoretext4;

static APTR ClassPool = NULL;

__asm __saveds LONG classlist_cmpaddressfunc (register __a1 struct ClassEntry *ce1, register __a2 struct ClassEntry *ce2)
{
   return (LONG) ce1->cse_adr - (LONG)ce2->cse_adr;
}

struct Hook classlist_cmpaddresshook = {
 {NULL, NULL},
 (ULONG (* )())classlist_cmpaddressfunc,
 NULL, NULL
};

__asm __saveds LONG classlist_cmpnamefunc (register __a1 struct ClassEntry *ce1, register __a2 struct ClassEntry *ce2) {
   LONG result;
   if (result = strcmpi (ce1->cse_classname, ce2->cse_classname))
      return (result);
   return (1);
}

struct Hook classlist_cmpnamehook = {
 {NULL, NULL},
 (ULONG (* )())classlist_cmpnamefunc,
 NULL, NULL
};

__asm __saveds LONG classlist_cmpsuperfunc (register __a1 struct ClassEntry *ce1, register __a2 struct ClassEntry *ce2)
{
   return strcmpi (ce1->cse_super, ce2->cse_super);
}

struct Hook classlist_cmpsuperhook = {
 {NULL, NULL},
 (ULONG (* )())classlist_cmpsuperfunc,
 NULL, NULL
};

__asm __saveds LONG classlist_dspfunc(register __a2 char **array, register __a1 struct ClassEntry *classentry, register __a0 struct Hook *hook)
{
   if (classentry) {
      *array++ = classentry->cse_address;
      *array++ = classentry->cse_objcnt;
      *array++ = classentry->cse_subcnt;
      *array++ = classentry->cse_super;
      *array++ = classentry->cse_dispatch;
      *array   = classentry->cse_classname;
   } else {
      *array++ = ESC "bAddress";
      *array++ = ESC "bObjects";
      *array++ = ESC "bSubclasses";
      *array++ = ESC "bSuperclass";
      *array++ = ESC "bDispatcher";
      *array   = ESC "bName";
   }
   return (0);
}

struct Hook classlist_dsphook = {
 {NULL, NULL},
 (ULONG (* )())classlist_dspfunc,
 NULL, NULL
};

void FFreeClass (void) {
   MyFreePoolStructs (&ClassPool, classtext, NULL, classlist);
}

int GetClass (struct ClassEntry **first) {
   struct   ClassEntry   *classentry,*previous = NULL;

   int classcnt = 0;
   *first = 0;

   if (!ClassPool) ClassPool = tbCreatePool(MEMF_CLEAR, 4096, 4096);

   if (clientstate) {
      if (SendDaemon ("GetClassList")) {
         while ((classentry = tbAllocPooled(ClassPool, sizeof(struct ClassEntry))) \
           && (ReceiveDecodedEntry ((UBYTE *) classentry, sizeof (struct ClassEntry)))) {
            IsHex (classentry->cse_address, (long *) &classentry->cse_adr);

            if (! *first)
               *first = classentry;
            if (previous)
               previous->cse_next = classentry;

            classcnt++;
            previous = classentry;
         }
      }
   } else {
      struct IClass *myclass = MakeClass("« Scout Dummy Class »","rootclass",NULL,0,0);
      struct Node *clist;
      struct Hook *disp;

      if(myclass) {
         AddClass(myclass);
         Forbid();
            for(clist=(struct Node *)&myclass->cl_Dispatcher.h_MinNode; clist->ln_Pred; clist=clist->ln_Pred);   /* Search the list head */
            for(clist=clist->ln_Succ; clist->ln_Succ; clist=clist->ln_Succ) {
               if(clist==(struct Node *)myclass) continue;         /* Skip own dummy */
               if(classentry = tbAllocPooled(ClassPool, sizeof(struct ClassEntry))) {

                  disp = &((struct IClass *)clist)->cl_Dispatcher;

                  classentry->cse_adr = (struct IClass *)clist;
                  _sprintf(classentry->cse_address,"$%08lx",clist);
                  // _sprintf(classentry->cse_super,"$%08lx",((struct IClass *)clist)->cl_Super);
                  if (((struct IClass *)clist)->cl_Super) {
                      strncpy(classentry->cse_super,((struct IClass *)clist)->cl_Super->cl_ID,79);
                  } else {
                      strcpy(classentry->cse_super, "<none>");
                  }
                  _sprintf(classentry->cse_dispatch,"$%08lx",(disp->h_SubEntry ? disp->h_SubEntry : disp->h_Entry));
                  strncpy(classentry->cse_classname,((struct IClass *)clist)->cl_ID,79);
                  _sprintf(classentry->cse_objcnt,"%ld ",((struct IClass *)clist)->cl_ObjectCount);
                  _sprintf(classentry->cse_subcnt,"%ld ",((struct IClass *)clist)->cl_SubclassCount);

                  if (! *first)
                     *first = classentry;
                  if (previous)
                     previous->cse_next = classentry;
                  classcnt++;
                  previous = classentry;
               }
            }
         Permit();
         FreeClass(myclass);
      }
   }
   return (classcnt);
}

void PrintClass (char *filename) {
   int   i=1;
   BPTR  handle;
   struct ClassEntry *entryp = NULL;

   handle = HandlePrintStart (filename);
   if ((handle) && (PrintOneLine (handle, "\n  Address  Objects  Subclasses  Superclass             Dispatcher  Name\n\n"))) {
      if (! WI_Class) {
         i = GetClass (&entryp);
      }
      if (i) {
         for (i=0;;i++) {
            if (WI_Class)
               DoMethod (classlist,MUIM_List_GetEntry,i,&entryp);
            if (!entryp) break;

            _sprintf (tmpstr2, " %s %7s %11s  %-20s   %s   %-20s\n", entryp->cse_address, entryp->cse_objcnt, entryp->cse_subcnt, entryp->cse_super, entryp->cse_dispatch, entryp->cse_classname);
            if (! (PrintOneLine (handle, tmpstr2)))
               break;

            if (! WI_Class)
               entryp = entryp->cse_next;
         }
      }
   }
   HandlePrintStop();
}

void ShowClass (void) {
   struct   ClassEntry   *clas;

   ApplicationSleep();
   set(classlist,MUIA_List_Quiet,TRUE);
   set(classlist,MUIA_List_CompareHook,classlist_cmphook_ptr);
   set(BT_ClassRemove, MUIA_Disabled, TRUE);
   set(BT_ClassMore, MUIA_Disabled, TRUE);
   FFreeClass();

   classcnt = GetClass (&clas);

   while (clas) {
      InsertSortedEntry (classlist, (APTR *) &clas);
      clas = clas->cse_next;
   }

   SetCountText (classcount, classcnt);
   AwakeApplication();
   set(classlist,MUIA_List_Quiet,FALSE);
}

void SendClassList (void) {
   struct   ClassEntry   *clas;

   FFreeClass();
   GetClass (&clas);

   while (clas) {
      SendEncodedEntry ((UBYTE *) clas, sizeof (struct ClassEntry));
      clas = clas->cse_next;
   }
   FFreeClass();
}

void GetClassMore (struct IClass *bc) {
   unsigned char     *title = "CLASS: ";
   struct   WinFree  *ptr;
   BOOL found = FALSE;
   struct IClass *myclass = MakeClass("« Scout Dummy Class »","rootclass",NULL,0,0);
   struct Node *clist;

   if(myclass) {
      AddClass(myclass);
      Forbid();
         for(clist=(struct Node *)&myclass->cl_Dispatcher.h_MinNode; clist->ln_Pred; clist=clist->ln_Pred);   /* Search the list head */
         for(clist=clist->ln_Succ; clist->ln_Succ; clist=clist->ln_Succ) {
            if(clist==(struct Node *)myclass) continue;         /* Skip own dummy */
            if(clist==(struct Node *)bc) {
               found = TRUE;
               break;
            }
         }
      Permit();
      FreeClass(myclass);
   }

   if (found && (ptr = AllocWinFree())) {
      ptr->wf_Window = (APTR) WindowObject,
      MUIA_HelpNode, ClassesText,
      MUIA_Window_ID, MakeDetailID('.','C','L','S'),
      WindowContents, VGroup,
         Child, ColGroup(2),
            Child, MyLabel2 ("Name:"),
            Child, classmoretext0 = MyTextObject(),
            Child, MyLabel2 ("Super:"),
            Child, classmoretext1 = MyTextObject(),
         End,
         Child, HGroup,
            Child, MyLabel2 ("Address:\nSuper:\nInstOffset:\nInstSize:"),
            Child, classmoretext2 = MyTextObject2(),
            Child, HSpace(0),
            Child, MyLabel2 ("Entry:\nSubEntry:\nData:\nUserData:"),
            Child, classmoretext3 = MyTextObject2(),
            Child, HSpace(0),
            Child, MyLabel2 ("SubClasses:\nObjects:\nFlags:\nReserved:"),
            Child, classmoretext4 = MyTextObject2(),
         End,
      End, End;

      if (ptr->wf_Window) {
         MySetContentsHealed (classmoretext0, "%s", bc->cl_ID);
         MySetContentsHealed (classmoretext1, "%s", bc->cl_Super ? bc->cl_Super->cl_ID : (STRPTR)"---");
         MySetContents (classmoretext2, ESC "r$%08lx\n" ESC "r$%08lx\n" ESC "r%ld\n" ESC "r%ld", bc, bc->cl_Super, bc->cl_InstOffset, bc->cl_InstSize);
         MySetContents (classmoretext3, ESC "r$%08lx\n" ESC "r$%08lx\n" ESC "r$%08lx\n" ESC "r$%08lx", bc->cl_Dispatcher.h_Entry, bc->cl_Dispatcher.h_SubEntry, bc->cl_Dispatcher.h_Data, bc->cl_UserData);
         MySetContents (classmoretext4, ESC "r%ld\n" ESC "r%ld\n" ESC "r$%08lx\n" ESC "r$%08lx", bc->cl_SubclassCount, bc->cl_ObjectCount, bc->cl_Flags, bc->cl_Reserved);

//*         HandleFlagsButtonPressed (classmoretext4, ptr, "(COMMODITY)", "Flags", bc->mco_Flags, (struct LongFlag *) &bc_flags, NULL, 'b');
         HandleWindowOpen (ptr, title, bc->cl_ID);
         HandleWindowClose (ptr);
      }
   }
}


APTR ClassSortList[] = {
   &classlist_cmpaddresshook,
   &classlist_cmpnamehook,
   &classlist_cmpsuperhook,
   NULL
};

static const char *CYA_ClassSortText[] = {
   "address",
   "name",
   "superclass",
   NULL
};

APTR WI_Class, classlist, classtext, classcount, CY_ClassSort;
int  classsortstate = 0;
APTR BT_ClassUpdate, BT_ClassPrint, BT_ClassRemove, BT_ClassMore, BT_ClassExit;

char class_title[WINDOWTITLELEN];

void ClassWindow (BOOL state) {
   if (state) {
      if (WI_Class) {
         ShowClass();
      } else {
         WI_Class = WindowObject,
         MUIA_Window_Title, MyGetWindowTitle (class_title, "CLASSES"),
         MUIA_HelpNode, ClassesText,
         MUIA_Window_ID, MakeListID('C','L','A','S'),
         WindowContents, VGroup,
            Child, classlist = MyListviewObject ("DELTA=8,DELTA=8 P=\33r,DELTA=8 P=\33r,DELTA=8,DELTA=8,",&classlist_dsphook),
            Child, MyBelowSortedListview (&classtext, &classcount, &CY_ClassSort, CYA_ClassSortText, classsortstate),
            Child, MyVSpace(2),
            Child, VGroup,
               Child, HGroup, MUIA_Group_SameSize, TRUE,
                  Child, BT_ClassUpdate     = KeyButtonA (UpdateText   ,ID_CLASSUPDATE),
                  Child, BT_ClassPrint      = KeyButtonA (PrintText    ,ID_CLASSPRINT),
                  Child, BT_ClassRemove     = KeyButtonA (RemoveText   ,ID_CLASSREMOVE),
                  Child, BT_ClassMore       = KeyButtonA (MoreText     ,ID_CLASSMORE),
                  Child, BT_ClassExit       = KeyButtonA (ExitText     ,ID_CLASSEXIT),
               End,
            End,
         End, End;

         if (WI_Class) {
            DoMethod (AP_Scout,OM_ADDMEMBER,WI_Class);
            DoMethod (WI_Class,MUIM_Window_SetCycleChain,classlist,CY_ClassSort,BT_ClassUpdate,BT_ClassPrint,BT_ClassRemove,BT_ClassMore,BT_ClassExit,NULL);
            DoMethod (CY_ClassSort, MUIM_Notify, MUIA_Cycle_Active, MUIV_EveryTime, AP_Scout, 2, MUIM_Application_ReturnID, ID_CLASSSORT);

            SetCloseRequest (WI_Class,ID_CLASSEXIT);
            SetListActive (classlist,ID_CLASSLV_ACTIVE);
            SetListviewDoubleClick (classlist,ID_CLASSMORE);

            ShowClass();

            SetWindowOpen (WI_Class,classlist,ID_CLASSEXIT);
         }
      }
   } else if ((! state) && (WI_Class)) {
      SetWindowClose (WI_Class,TRUE);
      FFreeClass();
      DoMethod (AP_Scout,OM_REMMEMBER,WI_Class);
      MUI_DisposeObject (WI_Class);
      WI_Class = NULL;
      classlist = NULL;
   }
}

