/*
**	$RCSfile: Timer.c,v $
**	$Filename: Timer.c $
**	$Revision: 1.1 $
**	$Date: 2001/04/28 17:30:19 $
**
**	sysmon command timer (version 1.1)
**
**	(C) Copyright 1995-2001 by Etienne Vogt
*/

#include <dos/rdargs.h>
#include <dos/dostags.h>
#include <dos/dosextens.h>
#include <workbench/startup.h>
#include <devices/timer.h>
#define __USE_SYSBASE
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/timer.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "sysmon.h"
#include "sysmon_protos.h"
#include "sysmon_pragmas.h"

struct Library *SysmonBase;
struct Device *TimerBase;
static struct RDArgs *myrda;
static struct WBStartup *wbmsg;
static struct timerequest mytimereq;

int main(void);
static void cleanexit(int rc);
static struct EClockVal subEClockVal(struct EClockVal e2, struct EClockVal e1);
static char *uptimestr(struct EClockVal *clockval, ULONG clockrate);
static char *cputimestr(struct EClockVal *clockval, ULONG clockrate);
static char *cpuloadstr(struct EClockVal *cpu, struct EClockVal *time);
static ULONG EClockDivide(struct EClockVal *numerator, ULONG denominator, ULONG *remainder);
static __inline APTR SPrintf(STRPTR buffer, STRPTR format, ...);
void __asm __saveds comExit(register __d0 int rc, register __d1 struct EClockVal *data);

static UBYTE template[] = "COMMAND/F/A";
static UBYTE version[] = "$VER: Timer 1.1 (28.4.2001)";

#define OPT_COMMAND	0
#define OPTMAX		1


int main(void)
{ LONG opts[OPTMAX];
  struct CommandLineInterface *mycli;

  SysmonBase = NULL;
  TimerBase = NULL;
  wbmsg = NULL;
  myrda = NULL;

  if (DOSBase->dl_lib.lib_Version < 37)
  { Printf("Requires AmigaOS V37 or higher.\n");
    exit(30);
  }

  if (!(mycli = Cli()))
    cleanexit(20);		/* If started from WB, exit cleanly */
  else
  { struct EClockVal StartTime, EndTime, ElapsedTime;
    static struct EClockVal ElapsedCPU;
    ULONG EClockRate;

    memset((char *)opts, 0, sizeof(opts));
    if ((myrda = ReadArgs(template, opts, NULL)) == NULL)
    { PrintFault(IoErr(),NULL);
      cleanexit(20);
    }

    if ((SysmonBase = OpenLibrary("sysmon.library",0)) == NULL)
    { PutStr("timer : Couldn't open sysmon.library\n");
      cleanexit(20);
    }

    if (OpenDevice(TIMERNAME, UNIT_ECLOCK, (struct IORequest *)&mytimereq, 0L))
      cleanexit(100);
    TimerBase = mytimereq.tr_node.io_Device;

    EClockRate = ReadEClock(&StartTime);
    
    if (SystemTags((STRPTR)opts[OPT_COMMAND],
		SYS_UserShell,	TRUE,
		NP_StackSize,	mycli->cli_DefaultStack << 2,
		NP_ExitCode,	comExit,
		NP_ExitData,    &ElapsedCPU,
		TAG_DONE ))
    { PrintFault(IoErr(), "Error starting command");
      cleanexit(10);
    }

    ReadEClock(&EndTime);
    ElapsedTime = subEClockVal(EndTime, StartTime);

    Printf("Elapsed Time: %s , CPU Time: %s , CPU Load: %s\n",
	uptimestr(&ElapsedTime, EClockRate),
	cputimestr(&ElapsedCPU, EClockRate),
	cpuloadstr(&ElapsedCPU, &ElapsedTime));
  }
  cleanexit(0);
}

static void cleanexit(int rc)
{
  if (SysmonBase) CloseLibrary(SysmonBase);
  if (TimerBase) CloseDevice((struct IORequest *)&mytimereq);
  if (myrda) FreeArgs(myrda);
  exit(rc);
}

static struct EClockVal subEClockVal(struct EClockVal e2, struct EClockVal e1)
{ BOOL carry = (e1.ev_lo > e2.ev_lo);

  e2.ev_lo -= e1.ev_lo;
  e2.ev_hi -= e1.ev_hi;
  if (carry) e2.ev_hi -= 1;
  return e2;
}

static char *uptimestr(struct EClockVal *clockval, ULONG clockrate)
{ static char buffer[16];
  ULONG seconds;
  UWORD updays, uphours, upminutes, upseconds;

  seconds = EClockDivide(clockval, clockrate, NULL);
  updays = seconds / (24*60*60);
  seconds %= 24*60*60;
  uphours = seconds / (60*60);
  seconds %= 60*60;
  upminutes = seconds / 60;
  upseconds = seconds % 60;
  SPrintf(buffer, "%3lu-%02lu:%02lu:%02lu", updays, uphours, upminutes, upseconds);
  return buffer; 
}

static char *cputimestr(struct EClockVal *clockval, ULONG clockrate)
{ static char buffer[18];
  ULONG seconds, clockremain;
  UWORD cpudays, cpuhours, cpuminutes, cpuseconds, cpumillis;

  seconds = EClockDivide(clockval, clockrate, &clockremain);
  cpudays = seconds / (24*60*60);
  seconds %= 24*60*60;
  cpuhours = seconds / (60*60);
  seconds %= 60*60;
  cpuminutes = seconds / 60;
  cpuseconds = seconds % 60;
  cpumillis = clockremain / (clockrate / 1000);
  if (cpumillis == 1000) cpumillis--;
  SPrintf(buffer, "%3lu-%02lu:%02lu:%02lu.%03lu", cpudays, cpuhours, cpuminutes, cpuseconds, cpumillis);
  return buffer;
}

static ULONG EClockDivide(struct EClockVal *numerator, ULONG denominator, ULONG *remainder)
{ int bits;
  ULONG d1,d2,d3;

  d1 = 0x0;
  d2 = numerator->ev_hi;
  d3 = numerator->ev_lo;

  for (bits = 0; bits < 64; bits++)
  { /* now left-shift d1,d2,d3 */
    d1 <<= 1;
    if (d2 & 0x80000000) d1 |= 0x1;
    d2 <<= 1;
    if (d3 & 0x80000000) d2 |= 0x1;
    d3 <<= 1;

    if (d1 >= denominator) 
    { d1 -= denominator;
      d3 |= 0x1;
    }
  }

  /* fill in the remainder */
  if (remainder) *remainder = d1;

  /* return the quotient, or the low part of it, at least */
  return d3;
}

static char *cpuloadstr(struct EClockVal *cpu, struct EClockVal *time)
{ static char buffer[10];
  double cpuload;

  cpuload = 100.0 * (cpu->ev_hi * 4294967296.0 + cpu->ev_lo) / (time->ev_hi * 4294967296.0 + time->ev_lo);
  sprintf(buffer, "%5.1f %%", cpuload);
  return buffer;
}

static __inline APTR SPrintf(STRPTR buffer, STRPTR format, ...)
{ return smVSPrintf(buffer, format, &format + 1);
}

void __asm __saveds comExit(register __d0 int rc, register __d1 struct EClockVal *data)
{ struct TaskInfo *mytinfo;

  mytinfo = smFindTaskInfo(NULL);
  *data = mytinfo->ti_CPUTime;
}
