/**
 * 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  cxcnt;

static APTR cxmoretext0,cxmoretext1,cxmoretext2,cxmoretext3,cxmoretext4,cxmoretext5;
static APTR cxsublist;

static APTR CxPool = NULL;

static struct MyCxObj {
   struct Node     mco_Node;
   UBYTE           mco_Flags;
   UBYTE           mco_dummy1;
   struct MinList  mco_SubList;
   APTR            mco_dummy2;
   char            mco_Name[CBD_NAMELEN];
   char            mco_Title[CBD_TITLELEN];
   char            mco_Descr[CBD_DESCRLEN];
   struct Task *   mco_Task;
   struct MsgPort *mco_Port;
   ULONG           mco_dummy3;
   WORD            mco_dummy4;
};

static char * GetCxNodeType (UBYTE type)
{
   char *CxNodeTypeText[] = {
      "Invalid","Filter","Typefilter","Send",
      "Signal","Translate","Broker","Debug",
      "Custom","Zero","< ??? >"
   };

   if(type>CX_ZERO)
      return("< ??? >");
   else
      return(CxNodeTypeText[type]);
}

__asm __saveds LONG cxlist_cmpprifunc (register __a1 struct CxEntry *ce1, register __a2 struct CxEntry *ce2)
{
   long  eins = 0, zwei = 0;

   IsDec (ce1->cxe_pri, &eins);
   IsDec (ce2->cxe_pri, &zwei);

   return zwei - eins;
}

struct Hook cxlist_cmpprihook = {
 {NULL, NULL},
 (ULONG (* )())cxlist_cmpprifunc,
 NULL, NULL
};

__asm __saveds LONG cxlist_cmpaddressfunc (register __a1 struct CxEntry *ce1, register __a2 struct CxEntry *ce2)
{
   return (LONG)ce1->cxe_adr - (LONG)ce2->cxe_adr;
}

struct Hook cxlist_cmpaddresshook = {
 {NULL, NULL},
 (ULONG (* )())cxlist_cmpaddressfunc,
 NULL, NULL
};

__asm __saveds LONG cxlist_cmpnamefunc (register __a1 struct CxEntry *ce1, register __a2 struct CxEntry *ce2)
{
   return strcmpi(ce1->cxe_name, ce2->cxe_name);
}

struct Hook cxlist_cmpnamehook = {
 {NULL, NULL},
 (ULONG (* )())cxlist_cmpnamefunc,
 NULL, NULL
};

__asm __saveds LONG cxlist_dspfunc(register __a2 char **array, register __a1 struct CxEntry *cxentry, register __a0 struct Hook *hook)
{
   if (cxentry) {
      *array++ = cxentry->cxe_address;
      *array++ = cxentry->cxe_type;
      *array++ = cxentry->cxe_pri;
      *array++ = cxentry->cxe_flags;
      *array++ = cxentry->cxe_port;
      *array   = cxentry->cxe_name;
   } else {
      *array++ = ESC "bAddress";
      *array++ = ESC "bln_Type";
      *array++ = ESC "bln_Pri";
      *array++ = ESC "bFlags";
      *array++ = ESC "bPort";
      *array   = ESC "bName";
   }
   return (0);
}

struct Hook cxlist_dsphook = {
 {NULL, NULL},
 (ULONG (* )())cxlist_dspfunc,
 NULL, NULL
};

void FreeCx (void)
{
    MyFreePoolStructs(&CxPool, cxtext, NULL, cxlist);
}

void GetCxEntry (struct CxEntry *cxentry, struct MyCxObj *bc) {

   if (! cxentry->cxe_adr) {
      cxentry->cxe_adr = (char *)bc;

      _sprintf (cxentry->cxe_address, "$%08lx", bc);
      strcpy (cxentry->cxe_type, GetCxNodeType (CxObjType((CxObj *)bc)));
   }

   _sprintf (cxentry->cxe_pri, "%4ld ", bc->mco_Node.ln_Pri);
   strcpy (cxentry->cxe_name, bc->mco_Name);
   strcpy (cxentry->cxe_title, bc->mco_Title);
   strcpy (cxentry->cxe_descr, bc->mco_Descr);
   _sprintf (cxentry->cxe_task, "$%08lx", bc->mco_Task);
   _sprintf (cxentry->cxe_port, "$%08lx", bc->mco_Port);
   _sprintf (cxentry->cxe_unique, "$%08lx", bc->mco_dummy3);
   cxentry->cxe_flags_int = bc->mco_Flags;
   _sprintf (cxentry->cxe_flags, "$%02lx", bc->mco_Flags);
}

int GetCx (struct CxEntry **first) {
   struct   CxEntry   *cxentry,*previous = NULL;

   int cxcnt = 0;
   *first = 0;

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

   if (clientstate) {
      if (SendDaemon ("GetCxList")) {
         while ((cxentry = tbAllocPooled(CxPool, sizeof(struct CxEntry))) \
           && (ReceiveDecodedEntry ((UBYTE *) cxentry, sizeof (struct CxEntry)))) {
            IsHex (cxentry->cxe_address, (long *) &cxentry->cxe_adr);

            if (! *first)
               *first = cxentry;
            if (previous)
               previous->cxe_next = cxentry;

            cxcnt++;
            previous = cxentry;
         }
      }
   } else {
      struct MsgPort *cxport;
      struct NewBroker cxnewbroker =
      {
        NB_VERSION,
        "« Scout Dummy »",
        "« Scout Dummy »",
        "Dummy Broker",
        0,
        0,
        0,
        NULL,
        0
      };
      struct MyCxObj *cxbroker;
      struct Node *broker;

      if(cxport = CreateMsgPort()) {
         cxnewbroker.nb_Port = cxport;
         if(cxbroker = (struct MyCxObj *)CxBroker(&cxnewbroker,NULL)) {
            Forbid();
              for(broker=&cxbroker->mco_Node; broker->ln_Pred; broker=broker->ln_Pred);   /* Search the list head */
              for(broker=broker->ln_Succ; broker->ln_Succ; broker=broker->ln_Succ) {
                 if(broker==(struct Node *)cxbroker) continue;                            /* Skip own dummy broker */
                 if(cxentry = tbAllocPooled(CxPool, sizeof(struct CxEntry))) {
                    GetCxEntry (cxentry,(struct MyCxObj *)broker);

                    if (! *first)
                       *first = cxentry;
                    if (previous)
                       previous->cxe_next = cxentry;

                    cxcnt++;
                    previous = cxentry;
                 }
              }
            Permit();
            DeleteCxObjAll((CxObj *)cxbroker);
         }
         DeleteMsgPort(cxport);
      }
   }
   return (cxcnt);
}

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

   handle = HandlePrintStart (filename);
   if ((handle) && (PrintOneLine (handle, "\n  Address  Type     Pri Flags  Port      Name\n\n"))) {
      if (! WI_Cx) {
         i = GetCx (&entryp);
      }
      if (i) {
         for (i=0;;i++) {
            if (WI_Cx)
               DoMethod (cxlist,MUIM_List_GetEntry,i,&entryp);
            if (!entryp) break;

            _sprintf (tmpstr2, " %s %-7.7s %3s %3s   %s %s\n", entryp->cxe_address, entryp->cxe_type, entryp->cxe_pri, entryp->cxe_flags, entryp->cxe_port, entryp->cxe_name);
            if (! (PrintOneLine (handle, tmpstr2)))
               break;

            if (! WI_Cx)
               entryp = entryp->cxe_next;
         }
      }
   }
   HandlePrintStop();
}

void ShowCx (void) {
   struct   CxEntry   *cx;

   ApplicationSleep();
   set(cxlist,MUIA_List_Quiet,TRUE);
   set(cxlist,MUIA_List_CompareHook,cxlist_cmphook_ptr);
   set(BT_CxRemove, MUIA_Disabled, TRUE);
   set(BT_CxPriority, MUIA_Disabled, TRUE);
   set(BT_CxMore, MUIA_Disabled, TRUE);
   set(BT_CxAppear, MUIA_Disabled, TRUE);
   set(BT_CxDisappear, MUIA_Disabled, TRUE);
   set(BT_CxEnable, MUIA_Disabled, TRUE);
   set(BT_CxDisable, MUIA_Disabled, TRUE);
   set(BT_CxKill, MUIA_Disabled, TRUE);
   set(BT_CxListChg, MUIA_Disabled, TRUE);
   set(BT_CxUnique, MUIA_Disabled, TRUE);
   FreeCx();

   cxcnt = GetCx (&cx);

   while (cx) {
      InsertSortedEntry (cxlist, (APTR *) &cx);
      cx = cx->cxe_next;
   }

   SetCountText (cxcount, cxcnt);
   AwakeApplication();
   set(cxlist,MUIA_List_Quiet,FALSE);
}

void SendCxList (void) {
   struct   CxEntry   *cx;

   FreeCx();
   GetCx (&cx);

   while (cx) {
      SendEncodedEntry ((UBYTE *) cx, sizeof (struct CxEntry));
      cx = cx->cxe_next;
   }
   FreeCx();
}

__asm __saveds LONG cxmore_dspfunc(register __a2 char **array, register __a1 struct CxSubEntry *subentry, register __a0 struct Hook *hook)
{
   if (subentry) {
      *array++ = subentry->cse_address;
      *array++ = subentry->cse_type;
      *array   = subentry->cse_pri;
   } else {
      *array++ = ESC "bAddress";
      *array++ = ESC "bln_Type";
      *array   = ESC "bln_Pri";
   }
   return (0);
}

struct Hook cxmore_dsphook = {
 {NULL, NULL},
 (ULONG (* )())cxmore_dspfunc,
 NULL, NULL
};

void GetSubList (struct MyCxObj *cxo) {
   struct CxSubEntry  *subentry;
   struct Node *node;

   set(cxsublist,MUIA_List_Quiet,TRUE);

   for(node=(struct Node *)cxo->mco_SubList.mlh_Head; node->ln_Succ; node=node->ln_Succ)
   {
      if(subentry = tbAllocPooled(CxPool, sizeof(struct CxEntry))) {
         _sprintf (subentry->cse_address, "$%08lx", node);
         strcpy  (subentry->cse_type   , GetCxNodeType(CxObjType((CxObj *)node)));
         _sprintf (subentry->cse_pri    , "%ld ", node->ln_Pri);

         InsertBottomEntry (cxsublist, (APTR *) &subentry);
      }
   }

   set(cxsublist,MUIA_List_Quiet,FALSE);
}

void GetCxMore (struct MyCxObj *bc) {
   unsigned char     *title = "COMMODITY: ";
   struct   WinFree  *ptr;
   struct MsgPort *cxport;
   struct NewBroker cxnewbroker =
   {
     NB_VERSION,
     "« Scout Dummy »",
     "« Scout Dummy »",
     "Dummy Broker",
     0,
     0,
     0,
     NULL,
     0
   };
   struct MyCxObj *cxbroker;
   struct Node *broker;
   BOOL found = FALSE;

   if(cxport = CreateMsgPort()) {
      cxnewbroker.nb_Port = cxport;
      if(cxbroker = (struct MyCxObj *)CxBroker(&cxnewbroker,NULL)) {
         Forbid();
           for(broker=&cxbroker->mco_Node; broker->ln_Pred; broker=broker->ln_Pred);   /* Search the list head */
           for(broker=broker->ln_Succ; broker->ln_Succ; broker=broker->ln_Succ) {
              if(broker==(struct Node *)cxbroker) continue;                            /* Skip own dummy broker */
              if(broker==(struct Node *)bc) {
                 found = TRUE;
                 break;
              }
           }
         Permit();
         DeleteCxObjAll((CxObj *)cxbroker);
      }
      DeleteMsgPort(cxport);
   }

   if (found && (ptr = AllocWinFree())) {
      ptr->wf_Window = (APTR) WindowObject,
      MUIA_HelpNode, CommoditiesText,
      MUIA_Window_ID, MakeDetailID('.','C','X','X'),
      WindowContents, VGroup,
         Child, ColGroup(2),
            Child, MyLabel2 ("Name:"),
            Child, cxmoretext0 = MyTextObject(),
            Child, MyLabel2 ("Title:"),
            Child, cxmoretext1 = MyTextObject(),
            Child, MyLabel2 ("Descr:"),
            Child, cxmoretext2 = MyTextObject(),
         End,
         Child, HGroup, MUIA_Group_SameWidth, TRUE,
            Child, ColGroup(2),
               Child, MyLabel2 ("Address:\nType:\nPri:\nTask:\nPort:"),
               Child, cxmoretext3 = MyTextObject2(),
               Child, MyLabel2 ("Flags:"),
               Child, cxmoretext4 = KeyButtonF ('b', bc->mco_Flags),
            End,
            Child, ColGroup(2),
               Child, MyLabel2 ("\nSub CxObj:\n\n\n\n"),
               Child, cxsublist = ListviewObject,
                  MUIA_Listview_Input, FALSE,
                  MUIA_Listview_List, ListObject,
                     MUIA_List_Format, "DELTA=8,DELTA=8 P=\33c,P=\33c",
                     MUIA_List_Title, TRUE,
                     MUIA_List_DisplayHook, &cxmore_dsphook,
                     ReadListFrame,
                  End,
                  MUIA_CycleChain, TRUE,
               End,
            End,
         End,
         Child, HGroup,
            Child, MyLabel2 ("TaskName:"),
            Child, cxmoretext5 = MyTextObject(),
         End,
      End, End;

      if (ptr->wf_Window) {
         MySetContents (cxmoretext0, "%s", bc->mco_Name);
         MySetContents (cxmoretext1, "%s", bc->mco_Title);
         MySetContents (cxmoretext2, "%s", bc->mco_Descr);
         MySetContents (cxmoretext3, ESC "r$%08lx\n" ESC "c%s\n%ld\n" ESC "r$%08lx\n$%08lx", bc, GetCxNodeType(CxObjType((CxObj *)bc)),bc->mco_Node.ln_Pri, bc->mco_Task, bc->mco_Port);
         MySetContentsHealed (cxmoretext5, "%s", nonetest (GetTaskName (bc->mco_Task)));

         GetSubList (bc);

         HandleFlagsButtonPressed (cxmoretext4, ptr, "(COMMODITY)", "Flags", bc->mco_Flags, (struct LongFlag *) &bc_flags, NULL, 'b');
         HandleWindowOpen (ptr, title, bc->mco_Name);
         set (ptr->wf_Window,MUIA_Window_ActiveObject,cxsublist);
         HandleWindowClose (ptr);
      }
   }
}



APTR CxSortList[] = {
   &cxlist_cmpaddresshook,
   &cxlist_cmpnamehook,
   &cxlist_cmpprihook,
   NULL
};

static const char *CYA_CxSortText[] = {
   "address",
   "name",
   "priority",
   NULL
};

APTR WI_Cx, cxlist, cxtext, cxcount, CY_CxSort;
int  cxsortstate = 0;
APTR BT_CxAppear, BT_CxDisappear, BT_CxEnable, BT_CxDisable, BT_CxKill, BT_CxListChg, BT_CxUnique;
APTR BT_CxUpdate, BT_CxPrint, BT_CxPriority, BT_CxRemove, BT_CxMore, BT_CxExit;

static char cx_title[WINDOWTITLELEN];

void CxWindow (BOOL state) {
   if (state) {
      if (WI_Cx) {
         ShowCx();
      } else {
         WI_Cx = WindowObject,
         MUIA_Window_Title, MyGetWindowTitle (cx_title, "COMMODITIES"),
         MUIA_HelpNode, CommoditiesText,
         MUIA_Window_ID, MakeListID('C','D','I','T'),
         WindowContents, VGroup,
            Child, cxlist = MyListviewObject ("DELTA=8,DELTA=8 P=\33c,DELTA=8 P=\33c,DELTA=8 P=\33c,DELTA=8,",&cxlist_dsphook),
            Child, MyBelowSortedListview (&cxtext, &cxcount, &CY_CxSort, CYA_CxSortText, cxsortstate),
            Child, MyVSpace(2),
            Child, VGroup,
               Child, HGroup, MUIA_Group_SameSize, TRUE,
                  Child, BT_CxAppear     = KeyButtonA (AppearText   ,ID_CXAPPEAR),
                  Child, BT_CxDisappear  = KeyButtonA (DisappearText,ID_CXDISAPPEAR),
                  Child, BT_CxEnable     = KeyButtonA (EnableText   ,ID_CXENABLE),
                  Child, BT_CxDisable    = KeyButtonA (DisableText  ,ID_CXDISABLE),
                  Child, BT_CxKill       = KeyButtonA (KillText     ,ID_CXKILL),
                  Child, BT_CxListChg    = KeyButtonA (ListChgText  ,ID_CXLISTCHG),
                  Child, BT_CxUnique     = KeyButtonA (UniqueText   ,ID_CXUNIQUE),
               End,
               Child, HGroup, MUIA_Group_SameSize, TRUE,
                  Child, BT_CxUpdate     = KeyButtonA (UpdateText   ,ID_CXUPDATE),
                  Child, BT_CxPrint      = KeyButtonA (PrintText    ,ID_CXPRINT),
                  Child, BT_CxPriority   = KeyButtonA (PriorityText ,ID_CXPRIORITY),
                  Child, BT_CxRemove     = KeyButtonA (RemoveText   ,ID_CXREMOVE),
                  Child, BT_CxMore       = KeyButtonA (MoreText     ,ID_CXMORE),
                  Child, BT_CxExit       = KeyButtonA (ExitText     ,ID_CXEXIT),
               End,
            End,
         End, End;

         if (WI_Cx) {
            DoMethod (AP_Scout,OM_ADDMEMBER,WI_Cx);
            DoMethod (WI_Cx,MUIM_Window_SetCycleChain,cxlist,CY_CxSort,BT_CxAppear,BT_CxDisappear,BT_CxEnable,BT_CxDisable,BT_CxKill,BT_CxListChg,BT_CxUnique,BT_CxUpdate,BT_CxPrint,BT_CxPriority,BT_CxRemove,BT_CxMore,BT_CxExit,NULL);
            DoMethod (CY_CxSort, MUIM_Notify, MUIA_Cycle_Active, MUIV_EveryTime, AP_Scout, 2, MUIM_Application_ReturnID, ID_CXSORT);

            SetCloseRequest (WI_Cx,ID_CXEXIT);
            SetListActive (cxlist,ID_CXLV_ACTIVE);
            SetListviewDoubleClick (cxlist,ID_CXMORE);

            ShowCx();

            SetWindowOpen (WI_Cx,cxlist,ID_CXEXIT);
         }
      }
   } else if ((! state) && (WI_Cx)) {
      SetWindowClose (WI_Cx,TRUE);
      FreeCx();
      DoMethod (AP_Scout,OM_REMMEMBER,WI_Cx);
      MUI_DisposeObject (WI_Cx);
      WI_Cx = NULL;
      cxlist = NULL;
   }
}

