/**
 * 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"

static APTR SysPool = NULL;

__asm __saveds LONG syslist_dspfunc(register __a2 char **array, register __a1 struct SystemEntry *sysentry, register __a0 struct Hook *hook)
{
   if (sysentry) {
      *array++ = sysentry->syse_desc;
      *array   = sysentry->syse_line;
   }
   return(0);
}

struct Hook syslist_dsphook = {
 {NULL, NULL},
 (ULONG (* )())syslist_dspfunc,
 NULL, NULL
};

void FreeSystem (void)
{
    MyFreePoolStructs(&SysPool, systext, NULL, syslist);
}

static struct SystemEntry * genentry(struct SystemEntry **first, struct SystemEntry **previous, int *syscnt)
{
  struct SystemEntry *sysentry;

  if(sysentry = tbAllocPooled(SysPool, sizeof(struct SystemEntry))) {
    if (! *first)
       *first = sysentry;
    if (*previous)
       (*previous)->syse_next = sysentry;
    (*syscnt)++;
    *previous = sysentry;
  }
  return(sysentry);
}

STRPTR MyIdHardware(ULONG num)
{
  return IdHardwareTags(num,IDTAG_Localize,FALSE,TAG_DONE);
}
int MyIdCheck(ULONG num)
{
  return (int)IdHardwareTags(num,IDTAG_NULL4NA,TRUE,TAG_DONE);
}

void DateToInt( struct DateStamp *ds,
                ULONG *u )
{
    *u = SMult32(ds->ds_Days, 86400) +
         SMult32(ds->ds_Minute, 60) +
         SDivMod32(ds->ds_Tick, TICKS_PER_SECOND);
}

void IntToDate( ULONG r,
                struct DateStamp *ds )
{
    ds->ds_Days = UDivMod32(r, 86400);

    r = getreg(REG_D1);
    ds->ds_Minute = UDivMod32(r, 60);

    r = getreg(REG_D1);
    ds->ds_Tick = UMult32(r, TICKS_PER_SECOND);
}

int GetSystem (struct SystemEntry **first) {
   struct   SystemEntry    *sysentry,*previous = NULL;
   int syscnt = 0;

   ULONG cacr = GetCACR(); /* 68020..68060 */
   ULONG pcr  = GetPCR();  /* 68060 */
   UWORD attn = SysBase->AttnFlags;

   *first = 0;

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

   if (clientstate) {
      if (SendDaemon ("GetSysList")) {
         while ((sysentry = tbAllocPooled(SysPool, sizeof(struct SystemEntry))) \
           && (ReceiveDecodedEntry ((UBYTE *) sysentry, sizeof (struct SystemEntry)))) {

            if (! *first)
               *first = sysentry;
            if (previous)
               previous->syse_next = sysentry;

            syscnt++;
            previous = sysentry;
         }
      }
   } else if(!IdentifyBase) {
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_line,ESC "8identify.library not available!");
      }
   } else {
      ULONG lastalert;

      IdHardwareUpdate();                 /* Liste auf dem aktuellsten Stand */

      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_line,ESC "c" ESC "8-- HARDWARE --");
      }
      genentry(first,&previous,&syscnt);

      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"System");
         _sprintf(sysentry->syse_line,"%s",IdHardware(IDHW_SYSTEM,NULL));
      }
      genentry(first,&previous,&syscnt);
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"Processor");
         if(MyIdCheck(IDHW_CPUREV))
           _sprintf(sysentry->syse_line,"CPU: %s / %s (ID $%04lx, Rev %s)", MyIdHardware(IDHW_CPU), MyIdHardware(IDHW_CPUCLOCK), pcr>>16, MyIdHardware(IDHW_CPUREV));
         else
           _sprintf(sysentry->syse_line,"CPU: %s / %s", MyIdHardware(IDHW_CPU), MyIdHardware(IDHW_CPUCLOCK));
      }

      if(MyIdCheck(IDHW_FPU)) {
        if(sysentry = genentry(first,&previous,&syscnt)) {
           _sprintf(sysentry->syse_line,"FPU: %s / %s", MyIdHardware(IDHW_FPU), MyIdHardware(IDHW_FPUCLOCK));
        }
      }

      if(MyIdCheck(IDHW_MMU)) {
        if(sysentry = genentry(first,&previous,&syscnt)) {
           _sprintf(sysentry->syse_line,"MMU: %s", MyIdHardware(IDHW_MMU));
        }
      }

      if(MyIdCheck(IDHW_POWERPC)) {
        if(sysentry = genentry(first,&previous,&syscnt)) {
           _sprintf(sysentry->syse_line,"PPC: %s / %s", MyIdHardware(IDHW_POWERPC), MyIdHardware(IDHW_PPCCLOCK));
        }
      }

      genentry(first,&previous,&syscnt);
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"Chipset");
         _sprintf(sysentry->syse_line,"%s, RamSey %s, Gary %s",MyIdHardware(IDHW_CHIPSET),MyIdHardware(IDHW_RAMSEY),MyIdHardware(IDHW_GARY));
      }
      if(sysentry = genentry(first,&previous,&syscnt)) {
         _sprintf(sysentry->syse_line,"ChunkyPlanar %s, BattClock %s",MyIdHardware(IDHW_CHUNKYPLANAR),MyIdHardware(IDHW_BATTCLOCK));
      }
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"Mother RAM");
         _sprintf(sysentry->syse_line,"%s Bit, %s Access, %s CAS, %sx Bandwidth",MyIdHardware(IDHW_RAMWIDTH),MyIdHardware(IDHW_RAMACCESS),MyIdHardware(IDHW_RAMCAS),MyIdHardware(IDHW_RAMBANDWIDTH));
      }
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"Frequencies");
         _sprintf(sysentry->syse_line,"VBlank %s, Power %s, E %s",MyIdHardware(IDHW_VBLANKFREQ),MyIdHardware(IDHW_POWERFREQ),MyIdHardware(IDHW_ECLOCK));
      }

      if(attn & AFF_68010) {
         ULONG cache = CacheControl(0,0);

         genentry(first,&previous,&syscnt);
         genentry(first,&previous,&syscnt);
         if(sysentry = genentry(first,&previous,&syscnt)) {
            strcpy (sysentry->syse_line,ESC "c" ESC "8-- REGISTERS --");
         }
         genentry(first,&previous,&syscnt);

         if(sysentry = genentry(first,&previous,&syscnt)) {
            _sprintf(sysentry->syse_line,"VBR  $%08lx",GetVBR());
         }

         if(attn & AFF_68020)
         {
            if(sysentry = genentry(first,&previous,&syscnt)) {
               _sprintf(sysentry->syse_line,"CACR $%08lx",cacr);
            }
         }

         if(attn & AFF_68060)
         {
            if(sysentry = genentry(first,&previous,&syscnt)) {
               _sprintf(sysentry->syse_line,"PCR  $%08lx",pcr);
            }
         }
         genentry(first,&previous,&syscnt);

         if(attn & AFF_68020) {
            if(sysentry = genentry(first,&previous,&syscnt)) {
               strcpy(sysentry->syse_desc,"Inst Cache");
               if(cache&CACRF_EnableI){
                  strcpy(sysentry->syse_line,"Enabled ");
                  if((attn&AFF_68030) && (!(attn&AFF_68040)) && (cache&CACRF_IBE)) strcat(sysentry->syse_line,"Burst ");
                  if(cache&CACRF_FreezeI) strcat(sysentry->syse_line,"Frozen ");
                  if((attn&AFF_68060) && (cacr&(1<<13))) strcat(sysentry->syse_line,"Half ");
                }else{
                  strcpy(sysentry->syse_line,"Disabled");
                }
            }
         }

         if(attn & AFF_68030) {
            if(sysentry = genentry(first,&previous,&syscnt)) {
               strcpy(sysentry->syse_desc,"Data Cache");
               if(cache&CACRF_EnableD){
                  strcpy(sysentry->syse_line,"Enabled ");
                  if((attn&AFF_68030) && (!(attn&AFF_68040)) && (cache&CACRF_DBE)) strcat(sysentry->syse_line,"Burst ");
                  if(cache&CACRF_FreezeD) strcat(sysentry->syse_line,"Frozen ");
                  if((attn&AFF_68060) && (cacr&(1<<27))) strcat(sysentry->syse_line,"Half ");
                  if((attn&AFF_68030) && (!(attn&AFF_68040)) && (cache&CACRF_WriteAllocate)) strcat(sysentry->syse_line,"Allocate ");
                }else{
                  strcpy(sysentry->syse_line,"Disabled");
                }
            }
         }

         if((attn&AFF_68040) && (!(attn&AFF_68060)) && (cache&CACRF_CopyBack)){
            if(sysentry = genentry(first,&previous,&syscnt)) {
               strcpy(sysentry->syse_line,"CopyBack");
            }
         }
         if(cache&CACRF_EnableE){
            if(sysentry = genentry(first,&previous,&syscnt)) {
               strcpy(sysentry->syse_line,"External Cache");
            }
         }
         if(attn & AFF_68060)
         {
            if(pcr&(1<<0))
              if(sysentry = genentry(first,&previous,&syscnt)) {
                 strcpy(sysentry->syse_line,"Superscalar");
              }
            if(cacr&(1<<23))
              if(sysentry = genentry(first,&previous,&syscnt)) {
                 strcpy(sysentry->syse_line,"Branch Cache");
              }
            if(cacr&(1<<29))
              if(sysentry = genentry(first,&previous,&syscnt)) {
                 strcpy(sysentry->syse_line,"Store Buffer");
              }
            if(cacr&(1<<28))
              if(sysentry = genentry(first,&previous,&syscnt)) {
                 strcpy(sysentry->syse_line,"CPUSH invalidation disabled");
              }
            if(pcr&(1<<1))
              if(sysentry = genentry(first,&previous,&syscnt)) {
                 strcpy(sysentry->syse_line,"FPU disabled");
              }
         }
      }

      genentry(first,&previous,&syscnt);
      genentry(first,&previous,&syscnt);
      if(sysentry = genentry(first,&previous,&syscnt)) {
         BPTR lock;

         if (lock = Lock("RAM:", SHARED_LOCK)) {
            struct InfoData *id;

            if (id = AllocVec(sizeof(struct InfoData), MEMF_CLEAR)) {
                if (Info(lock, id)) {
                    struct DeviceList *ramDev;
                    ULONG currentTime, bootTime, upTime;
                    struct DateStamp ds;
                    struct DateTime dt;
                    UBYTE dateStr[LEN_DATSTRING], timeStr[LEN_DATSTRING];

                    ramDev = BADDR(id->id_VolumeNode);
                    DateToInt(&ramDev->dl_VolumeDate, &bootTime);

                    DateStamp(&ds);
                    DateToInt(&ds, &currentTime);

                    upTime = currentTime - bootTime;

                    strcpy (sysentry->syse_line,ESC "c" ESC "8-- UPTIME --");

                    genentry(first,&previous,&syscnt);
                    if(sysentry = genentry(first,&previous,&syscnt)) {
                        IntToDate(currentTime, &dt.dat_Stamp);
                        dt.dat_Format = FORMAT_DOS;
                        dt.dat_Flags = 0;
                        dt.dat_StrDay = NULL;
                        dt.dat_StrDate = dateStr;
                        dt.dat_StrTime = timeStr;
                        DateToStr(&dt);

                        strcpy (sysentry->syse_desc,"Current time");
                        _sprintf(sysentry->syse_line,"%s %s", dateStr, timeStr);
                    }
                    if(sysentry = genentry(first,&previous,&syscnt)) {
                        IntToDate(bootTime, &dt.dat_Stamp);
                        dt.dat_Format = FORMAT_DOS;
                        dt.dat_Flags = 0;
                        dt.dat_StrDay = NULL;
                        dt.dat_StrDate = dateStr;
                        dt.dat_StrTime = timeStr;
                        DateToStr(&dt);

                        strcpy (sysentry->syse_desc,"Boot time");
                        _sprintf(sysentry->syse_line,"%s %s", dateStr, timeStr);
                    }
                    if(sysentry = genentry(first,&previous,&syscnt)) {
                        IntToDate(upTime, &dt.dat_Stamp);
                        dt.dat_Format = FORMAT_DOS;
                        dt.dat_Flags = 0;
                        dt.dat_StrDay = NULL;
                        dt.dat_StrDate = NULL;
                        dt.dat_StrTime = timeStr;
                        DateToStr(&dt);

                        strcpy (sysentry->syse_desc,"Uptime");
                        _sprintf(sysentry->syse_line,"%4ld days, %s hours", dt.dat_Stamp.ds_Days, timeStr);
                    }
                }

                FreeVec(id);
            }

            UnLock(lock);
         }
      }

      genentry(first,&previous,&syscnt);
      genentry(first,&previous,&syscnt);
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_line,ESC "c" ESC "8-- VERSIONS --");
      }
      genentry(first,&previous,&syscnt);

      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"AmigaOS");
         _sprintf(sysentry->syse_line,"%s (= %s), SetPatch %s, ROM %s",MyIdHardware(IDHW_OSNR),MyIdHardware(IDHW_OSVER),MyIdHardware(IDHW_SETPATCHVER),MyIdHardware(IDHW_ROMSIZE));
      }
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"Exec");
         _sprintf(sysentry->syse_line,"%s",MyIdHardware(IDHW_EXECVER));
      }
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"Workbench");
         _sprintf(sysentry->syse_line,"%s",MyIdHardware(IDHW_WBVER));
      }
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"Graphics");
         _sprintf(sysentry->syse_line,"%s",MyIdHardware(IDHW_GFXSYS));
      }
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"Audio");
         _sprintf(sysentry->syse_line,"%s",MyIdHardware(IDHW_AUDIOSYS));
      }
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"TCP/IP");
         _sprintf(sysentry->syse_line,"%s",MyIdHardware(IDHW_TCPIP));
      }
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"PowerPC-OS");
         _sprintf(sysentry->syse_line,"%s",MyIdHardware(IDHW_PPCOS));
      }

      genentry(first,&previous,&syscnt);
      genentry(first,&previous,&syscnt);
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_line,ESC "c" ESC "8-- MEMORY --");
      }
      genentry(first,&previous,&syscnt);
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy(sysentry->syse_line," Chip       Fast      TOTAL");
      }
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"Plain");
         _sprintf(sysentry->syse_line,"%9s  %9s  %9s",MyIdHardware(IDHW_PLNCHIPRAM),MyIdHardware(IDHW_PLNFASTRAM),MyIdHardware(IDHW_PLNRAM));
      }
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"Virtual");
         _sprintf(sysentry->syse_line,"%9s  %9s  %9s",MyIdHardware(IDHW_VMMCHIPRAM),MyIdHardware(IDHW_VMMFASTRAM),MyIdHardware(IDHW_VMMRAM));
      }
      if(sysentry = genentry(first,&previous,&syscnt)) {
         strcpy (sysentry->syse_desc,"TOTAL");
         _sprintf(sysentry->syse_line,"%9s  %9s  %9s",MyIdHardware(IDHW_CHIPRAM),MyIdHardware(IDHW_FASTRAM),MyIdHardware(IDHW_RAM));
      }
      genentry(first,&previous,&syscnt);
      if(sysentry = genentry(first,&previous,&syscnt)) {
         _sprintf(sysentry->syse_line,"SlowRAM: %s",MyIdHardware(IDHW_SLOWRAM));
      }

      lastalert = IdHardwareNum(IDHW_LASTALERT,NULL);
      if(lastalert != 0xFFFFFFFF) {
         genentry(first,&previous,&syscnt);
         genentry(first,&previous,&syscnt);
         if(sysentry = genentry(first,&previous,&syscnt)) {
            strcpy (sysentry->syse_line,ESC "c" ESC "8-- LAST ALERT --");
         }
         genentry(first,&previous,&syscnt);
         if(sysentry = genentry(first,&previous,&syscnt)) {
            strcpy (sysentry->syse_desc,"Alert Code");
            _sprintf(sysentry->syse_line,"%08lx",lastalert);
         }
         if(sysentry = genentry(first,&previous,&syscnt)) {
            strcpy (sysentry->syse_desc,"Deadend");
            IdAlertTags(lastalert,IDTAG_DeadStr,sysentry->syse_line,TAG_DONE);
         }
         if(sysentry = genentry(first,&previous,&syscnt)) {
            strcpy (sysentry->syse_desc,"Sub System");
            IdAlertTags(lastalert,IDTAG_SubsysStr,sysentry->syse_line,TAG_DONE);
         }
         if(sysentry = genentry(first,&previous,&syscnt)) {
            strcpy (sysentry->syse_desc,"General");
            IdAlertTags(lastalert,IDTAG_GeneralStr,sysentry->syse_line,TAG_DONE);
         }
         if(sysentry = genentry(first,&previous,&syscnt)) {
            strcpy (sysentry->syse_desc,"Specific");
            IdAlertTags(lastalert,IDTAG_SpecStr,sysentry->syse_line,TAG_DONE);
         }
      }
   }
   return (syscnt);
}

void PrintSystem (char *filename) {
   int   i=1;
   BPTR  handle;
   struct SystemEntry *entryp = NULL;
   STRPTR desc,line;

   handle = HandlePrintStart (filename);
   if (handle) {
      if (! WI_System) {
         i = GetSystem (&entryp);
      }
      if (i) {
         for (i=0;;i++) {
            if (WI_System)
               DoMethod (syslist,MUIM_List_GetEntry,i,&entryp);
            if (!entryp) break;

            desc = entryp->syse_desc; while(*desc=='\33') desc+=2;
            line = entryp->syse_line; while(*line=='\33') line+=2;

            _sprintf (tmpstr2, " %15s  %s\n",desc,line);
            if (! (PrintOneLine (handle, tmpstr2)))
               break;

            if (! WI_System)
               entryp = entryp->syse_next;
         }
      }
   }
   HandlePrintStop();
}

void ShowSystem (void) {
   struct SystemEntry *syse;

   ApplicationSleep();
   set (syslist,MUIA_List_Quiet,TRUE);

   FreeSystem();
   GetSystem(&syse);

   while (syse) {
      InsertBottomEntry (syslist, (APTR *) &syse);
      syse = syse->syse_next;
   }

   AwakeApplication();
   set (syslist,MUIA_List_Quiet,FALSE);
}

void SendSystemList (void) {
   struct   SystemEntry   *syse;

   FreeSystem();
   GetSystem (&syse);

   while (syse) {
      SendEncodedEntry ((UBYTE *) syse, sizeof (struct SystemEntry));
      syse = syse->syse_next;
   }
   FreeSystem();
}


APTR WI_System, syslist, systext;

char system_title[WINDOWTITLELEN];

void SystemWindow (BOOL state) {
   if (state) {
      if (WI_System) {
         ShowSystem();
      } else {
         WI_System = WindowObject,
         MUIA_Window_Title, MyGetWindowTitle (system_title, "SYSTEM"),
         MUIA_HelpNode, SystemText,
         MUIA_Window_ID, MakeListID('S','Y','S','T'),
         WindowContents, VGroup,
            Child, syslist = ListviewObject,
               MUIA_Listview_Input, FALSE,
               MUIA_Listview_List, ListObject,
                  MUIA_Font, MUIV_Font_Fixed,
                  ReadListFrame,
                  MUIA_List_Format, "DELTA=8 BAR P=\33r,",
                  MUIA_List_DisplayHook, &syslist_dsphook,
               End,
               MUIA_CycleChain, TRUE,
            End,
            Child, MyVSpace(2),
            Child, HGroup, MUIA_Group_SameSize, TRUE,
               Child, BT_SysUpdate = KeyButtonA (UpdateText,ID_SYSUPDATE),
               Child, BT_SysPrint  = KeyButtonA (PrintText ,ID_SYSPRINT),
               Child, BT_SysExit   = KeyButtonA (ExitText  ,ID_SYSEXIT),
            End,
         End, End;


         DoMethod (AP_Scout,OM_ADDMEMBER,WI_System);
         DoMethod (WI_System,MUIM_Window_SetCycleChain,syslist,BT_SysUpdate, BT_SysPrint,BT_SysExit,NULL);
         SetCloseRequest (WI_System,ID_SYSEXIT);

         ShowSystem();

         SetWindowOpen (WI_System,syslist,ID_SYSEXIT);
      }
   } else if ((! state) && (WI_System)) {
      SetWindowClose (WI_System,TRUE);
      FreeSystem();
      DoMethod (AP_Scout,OM_REMMEMBER,WI_System);
      MUI_DisposeObject (WI_System);
      WI_System = NULL;
   }
}

