/**
 * 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 TimerPool = NULL;

__asm __saveds LONG timerlist_dspfunc(register __a2 char **array, register __a1 struct TimerEntry *timerentry, register __a0 struct Hook *hook)
{
   if (timerentry) {
      *array++ = timerentry->timer_address;
      *array++ = timerentry->timer_replyport;
      *array++ = timerentry->timer_timeout;
      *array++ = timerentry->timer_unit;
      *array   = timerentry->timer_name;
   } else {
      *array++ = ESC "bAddress";
      *array++ = ESC "bReplyPort";
      *array++ = ESC "bTime";
      *array++ = ESC "bUnit";
      *array   = ESC "bTask";
   }
   return(0);
}

struct Hook timerlist_dsphook = {
 {NULL, NULL},
 (ULONG (* )())timerlist_dspfunc,
 NULL, NULL
};

__asm __saveds LONG timerlist_cmpfunc(register __a1 struct TimerEntry *timerentry1, register __a2 struct TimerEntry *timerentry2)
{
   LONG cmp;

   cmp = strcmpi(timerentry1->timer_name, timerentry2->timer_name);
   if (cmp == 0) cmp = strcmpi(timerentry1->timer_address, timerentry2->timer_address);

   return cmp;
}

struct Hook timerlist_cmphook = {
 {NULL, NULL},
 (ULONG (* )())timerlist_cmpfunc,
 NULL, NULL
};

void FreeTimer (void)
{
    MyFreePoolStructs(&TimerPool, timertext, NULL, timerlist);
}

int GetTimer (struct TimerEntry **first) {
   struct   timerequest *tr;
   struct   timerequest *search;
   struct   MsgPort *mp;
   struct   TimerEntry  *timerentry,*previous = NULL;
   // char     code[FILENAMELENGTH + 1];
   ULONG    time, eclk, std, min, sec;
   struct   EClockVal eclock;

   int timercnt = 0;
   *first = 0;

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

   if (clientstate) {
      if (SendDaemon ("GetTimerList")) {
         while ((timerentry = tbAllocPooled(TimerPool, sizeof(struct TimerEntry))) \
           && (ReceiveDecodedEntry ((UBYTE *) timerentry, sizeof (struct TimerEntry)))) {
            IsHex (timerentry->timer_address, (long *) &timerentry->timer_adr);

            if (! *first)
               *first = timerentry;
            if (previous)
               previous->timer_next = timerentry;

            timercnt++;
            previous = timerentry;
         }
      }
   } else {
      if (mp = CreateMsgPort()) {
         if (tr = CreateIORequest(mp,sizeof(struct timerequest))) {
            if (!OpenDevice("timer.device",UNIT_MICROHZ,(struct IORequest *)tr,0)) {
               TimerBase = tr->tr_node.io_Device;
               tr->tr_node.io_Command = TR_ADDREQUEST;
               tr->tr_time.tv_secs = 0;
               tr->tr_time.tv_micro = 10000;    /* 10ms */
               Forbid();
               SendIO((struct IORequest *)tr);
               for(search = tr;
                   search->tr_node.io_Message.mn_Node.ln_Pred;
                   search = (struct timerequest *)search->tr_node.io_Message.mn_Node.ln_Pred);

               eclk = ReadEClock(&eclock);
               eclk /= 100;

               search = (struct timerequest *) search->tr_node.io_Message.mn_Node.ln_Succ;
               while (search->tr_node.io_Message.mn_Node.ln_Succ != 0) {
                 if (search!=tr) {
                     if (!(timerentry = tbAllocPooled(TimerPool, sizeof(struct TimerEntry)))) break;
                     if (! *first)
                        *first = timerentry;
                     if (previous)
                        previous->timer_next = timerentry;

                     time = search->tr_time.tv_micro;
                     if(time > eclock.ev_lo)
                        time -= eclock.ev_lo;
                     else
                        time = 0;
                     time /= eclk;

                     sec = search->tr_time.tv_secs + (time/100);
                     min = sec/60; sec %= 60;
                     std = min/60; min %= 60;

                     timerentry->timer_adr = search;
                     _sprintf (timerentry->timer_address, "$%08lx", search);
                     // strncpy (timerentry->timer_name, nonetest (((struct Node *)search->tr_node.io_Message.mn_ReplyPort->mp_SigTask)->ln_Name), 27);
                     strncpy (timerentry->timer_name, nonetest(GetTaskName(search->tr_node.io_Message.mn_ReplyPort->mp_SigTask)), 27);
                     _sprintf (timerentry->timer_replyport, "$%08lx", search->tr_node.io_Message.mn_ReplyPort);
                     strcpy  (timerentry->timer_unit, "MicroHz");
                     _sprintf (timerentry->timer_timeout, "%ld:%02ld'%02ld.%02ld\"", std, min, sec, time%100);
                     timercnt++;

                     previous = timerentry;
                  }
                  search = (struct timerequest *) search->tr_node.io_Message.mn_Node.ln_Succ;
               }
               Permit();
               WaitIO((struct IORequest *)tr);
               CloseDevice((struct IORequest *)tr);
            }
            if (!OpenDevice("timer.device",UNIT_VBLANK,(struct IORequest *)tr,0)) {
               TimerBase = tr->tr_node.io_Device;
               tr->tr_node.io_Command = TR_ADDREQUEST;
               tr->tr_time.tv_secs = 0;
               tr->tr_time.tv_micro = 20000;    /* 20ms */
               Forbid();
               SendIO((struct IORequest *)tr);
               for(search = tr;
                   search->tr_node.io_Message.mn_Node.ln_Pred;
                   search = (struct timerequest *)search->tr_node.io_Message.mn_Node.ln_Pred);

               eclk = ReadEClock(&eclock);

               search = (struct timerequest *) search->tr_node.io_Message.mn_Node.ln_Succ;
               while (search->tr_node.io_Message.mn_Node.ln_Succ != 0) {
                 if (search!=tr) {
                     if (!(timerentry = tbAllocPooled(TimerPool, sizeof(struct TimerEntry)))) break;
                     if (! *first)
                        *first = timerentry;
                     if (previous)
                        previous->timer_next = timerentry;

                     time = search->tr_time.tv_micro;

                     if(time > eclock.ev_lo)
                        time = ((time-eclock.ev_lo)*100)/eclk;
                     else
                        time = 0;

                     sec = search->tr_time.tv_secs + (time/100);
                     min = sec/60; sec %= 60;
                     std = min/60; min %= 60;

                     timerentry->timer_adr = search;
                     _sprintf (timerentry->timer_address, "$%08lx", search);
                     // strncpy (timerentry->timer_name, nonetest (((struct Node *)search->tr_node.io_Message.mn_ReplyPort->mp_SigTask)->ln_Name), 27);
                     strncpy (timerentry->timer_name, GetTaskName(search->tr_node.io_Message.mn_ReplyPort->mp_SigTask), 27);
                     _sprintf (timerentry->timer_replyport, "$%08lx", search->tr_node.io_Message.mn_ReplyPort);
                     strcpy  (timerentry->timer_unit, "VBlank");
                     _sprintf (timerentry->timer_timeout, "%ld:%02ld'%02ld.%02ld\"", std, min, sec, time%100);
                     timercnt++;

                     previous = timerentry;
                  }
                  search = (struct timerequest *) search->tr_node.io_Message.mn_Node.ln_Succ;
               }
               Permit();
               WaitIO((struct IORequest *)tr);
               CloseDevice((struct IORequest *)tr);
            }
            DeleteIORequest((struct IORequest *)tr);
         }
         DeleteMsgPort(mp);
      }
   }
   return (timercnt);
}

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

   handle = HandlePrintStart (filename);
   if ((handle) && (PrintOneLine (handle, "\n  Address  ReplyPort    Time       Unit    Name\n\n"))) {
      if (! WI_Timer) {
         i = GetTimer (&entryp);
      }
      if (i) {
         for (i=0;;i++) {
            if (WI_Timer)
               DoMethod (timerlist,MUIM_List_GetEntry,i,&entryp);
            if (!entryp) break;

            _sprintf (tmpstr2, " %s %s %13s %7s %s\n", entryp->timer_address, entryp->timer_replyport, entryp->timer_timeout, entryp->timer_unit, entryp->timer_name);
            if (! (PrintOneLine (handle, tmpstr2)))
               break;

            if (! WI_Timer)
               entryp = entryp->timer_next;
         }
      }
   }
   HandlePrintStop();
}

void ShowTimer (void) {
   struct TimerEntry *timer;

   ApplicationSleep();
   set (timerlist,MUIA_List_Quiet,TRUE);
//*   set (BT_TimerRemove, MUIA_Disabled, TRUE);

   FreeTimer();
   timercnt = GetTimer (&timer);

   while (timer) {
      InsertSortedEntry (timerlist, (APTR *) &timer);
      timer = timer->timer_next;
   }

   DoMethod(timerlist,MUIM_List_Sort);

   SetCountText (timercount, timercnt);
   AwakeApplication();
   set (timerlist,MUIA_List_Quiet,FALSE);
}

void SendTimerList (void) {
   struct TimerEntry *timer;

   FreeTimer();
   timercnt = GetTimer (&timer);

   while (timer) {
      SendEncodedEntry ((UBYTE *) timer, sizeof (struct TimerEntry));
      timer = timer->timer_next;
   }
   FreeTimer();
}


char timer_title[WINDOWTITLELEN];

void TimerWindow (BOOL state) {
   if (state) {
      if (WI_Timer) {
         ShowTimer();
      } else {
         WI_Timer = WindowObject,
         MUIA_Window_Title, MyGetWindowTitle (timer_title, "TIMER REQUESTS"),
         MUIA_HelpNode, TimerText,
         MUIA_Window_ID, MakeListID('T','I','M','R'),
         WindowContents, VGroup,
            Child, timerlist = MySortedListviewObject ("DELTA=8,DELTA=8,DELTA=8 P=\33r,DELTA=8 P=\33c,",&timerlist_dsphook, &timerlist_cmphook),
            Child, MyBelowListview (&timertext, &timercount),
            Child, MyVSpace(2),
            Child, HGroup, MUIA_Group_SameSize, TRUE,
               Child, BT_TimerUpdate    = KeyButtonA (UpdateText  ,ID_TIMERUPDATE),
               Child, BT_TimerPrint     = KeyButtonA (PrintText   ,ID_TIMERPRINT),
//*               Child, BT_TimerRemove    = KeyButtonA (RemoveText  ,ID_TIMERREMOVE),
               Child, BT_TimerExit      = KeyButtonA (ExitText    ,ID_TIMEREXIT),
            End,
         End, End;

         DoMethod (AP_Scout,OM_ADDMEMBER,WI_Timer);
         DoMethod (WI_Timer,MUIM_Window_SetCycleChain,timerlist,BT_TimerUpdate,BT_TimerPrint,BT_TimerExit,NULL);

         SetCloseRequest (WI_Timer,ID_TIMEREXIT);
         SetListActive (timerlist,ID_TIMERLV_ACTIVE);

         ShowTimer();

         SetWindowOpen (WI_Timer,timerlist,ID_TIMEREXIT);
      }
   } else if ((! state) && (WI_Timer)) {
      SetWindowClose (WI_Timer,TRUE);

      FreeTimer();

      DoMethod (AP_Scout,OM_REMMEMBER,WI_Timer);
      MUI_DisposeObject (WI_Timer);
      WI_Timer = NULL;
      timerlist = NULL;
   }
}

