#include "AvailMem.h"



/***************
*  Prototypes  *
***************/

int  wbmain(struct WBStartup *wbs);
int  main(void);
int  RealMain(void);
void openall(void);
void closeout(UBYTE *errstring, LONG rc);
void closeall(void);
void usage(void);
void UpdateMemBar(void);
Class *InitGaugeMemClass(void);
BOOL FreeGaugeMemClass(void);


/************
*  Defines  *
************/

#define TEMPLATE  "L=LEFTEDGE/N/K,T=TOPEDGE/N/K,W=WIDTH/N/K,R=REFRESH/N/K,C=COLOR/N/K"
#define MG_Fast   (TAG_USER+0x1100)


/**************
*  Variables  *
**************/

char     Title[80];
APTR     AP_Test;
APTR     WI_Test;
APTR     GA_Chip;
APTR     GA_Fast;

Class   *GaugeMemClass = NULL;

BYTE                    TimerError     = 0;
struct MsgPort         *TimerMP        = NULL ;
struct timerequest     *TimerIO        = NULL ;
struct Message         *TimerMSG       = NULL;

extern struct Library  *UtilityBase;
extern struct ExecBase *SysBase;
struct MemHeader       *memhdr;
APTR                    mem_start;
APTR                    mem_end;
ULONG                   mem_len;

struct TextAttr         myTextAttr;
struct RastPort        *rp             = NULL;
struct IntuitionBase   *IntuitionBase  = NULL;
struct Library         *MUIMasterBase  = NULL;
struct Library         *IconBase       = NULL;
struct Window          *window         = NULL;

APTR                    VisualInfo     = NULL;
int                     WindowWidth    = 230;
int                     TimeToWait     = 5;
int                     WindowLeftEdge = 0;
int                     WindowTopEdge  = 0;
int                     ColorNumber    = 1;

struct IntuiMessage    *imsg           = NULL;
struct I0ExtSer        *reply          = NULL;
struct InputEvent       inputevent     = {0};
BOOL                    Done           = FALSE;
LONG                    windowsignal;
LONG                    timersignal;
long                    mask;
int                     i;




/***************
**  wbmain()  **
***************/

int wbmain(struct WBStartup *wbs)
{
   struct DiskObject *dobj = NULL;
   char             **toolsarray;
   char              *s = NULL;


   if ((IconBase = OpenLibrary("icon.library", 37l)) == NULL)
      return(10);

   CurrentDir(wbs->sm_ArgList->wa_Lock);
   if (dobj = (struct DiskObject *) GetDiskObject(wbs->sm_ArgList->wa_Name)) {
      toolsarray = (char **) dobj->do_ToolTypes;

      if (s = (char *) FindToolType(toolsarray, "LEFTEDGE"))
         WindowLeftEdge = atoi(s);
      if (s = (char *) FindToolType(toolsarray, "TOPEDGE"))
         WindowTopEdge = atoi(s);
      if (s = (char *) FindToolType(toolsarray, "WIDTH"))
         WindowWidth = atoi(s);
      if (s = (char *) FindToolType(toolsarray, "REFRESH"))
         TimeToWait = atoi(s);

      if (s = (char *) FindToolType(toolsarray, "COLOR"))
         ColorNumber = atoi(s);

      FreeDiskObject(dobj);
   }

   if (IconBase)
      CloseLibrary(IconBase);

   return(RealMain());
}



/*********
*  Main  *
*********/

int main(void)
{
   struct RDArgs       *args       = NULL;
   long                 options[5];

   
   for (i=0; i<=4; i++)
      options[i] = 0L;

   args = ReadArgs(TEMPLATE, options, NULL);

   if (args) {
      if (options[0])
         WindowLeftEdge = *(int *) options[0];
      if (options[1])
         WindowTopEdge = *(int *) options[1];
      if (options[2]) {
         WindowWidth = *(int *) options[2];
         if (WindowWidth > 640 || WindowWidth < 100)
            WindowWidth = 200;
      }
      if (options[3])
         TimeToWait = *(int *) options[3];
      if (options[4])
         ColorNumber = *(int *) options[4];
      FreeArgs(args);
   }
   return(RealMain());
}



/*****************
**  RealMain()  **
*****************/

int RealMain(void)
{
   openall();    /* open Fonts, Libraries, VisualInfo, etc */

   AP_Test = ApplicationObject,
      MUIA_Application_Title, "AvailMem",
      MUIA_Application_Version, "$VER: AvailMem 1.01 (02.12.93)",
      MUIA_Application_Copyright, "Copyright by Dirk Böhmer",
      MUIA_Application_Author, "Dirk Böhmer",
      MUIA_Application_Description, "It shows how much memory you have.",
      MUIA_Application_Base, "AVAILMEM",
      MUIA_Application_SingleTask, FALSE,
      SubWindow, WI_Test = WindowObject,
         MUIA_Window_Title, "AvailMem",
         WindowContents, VGroup,
            Child, ColGroup(2),
               Child, GA_Fast = BoopsiObject,
                  GaugeFrame, 
                  MUIA_Boopsi_Class, GaugeMemClass,
                  MUIA_Boopsi_MinWidth, 100,
                  MUIA_Boopsi_MinHeight, 0,
                  MG_Fast, TRUE,
                  GA_LEFT, 0,
                  GA_TOP, 0,
                  GA_WIDTH, 0,
                  GA_HEIGHT, 0,
                  End,
               Child, Label2("Fast"),
               Child, GA_Chip = BoopsiObject,
                  GaugeFrame, 
                  MUIA_Boopsi_Class, GaugeMemClass,
                  MUIA_Boopsi_MinWidth, 50,
                  MUIA_Boopsi_MinHeight, 10,
                  MG_Fast, FALSE,
                  GA_LEFT, 0,
                  GA_TOP, 0,
                  GA_WIDTH, 0,
                  GA_HEIGHT, 0,
                  End,
               Child, Label2("Chip"),
            End,
         End,
      End,
   End;

   if (AP_Test) {
      DoMethod(WI_Test, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
         AP_Test, 2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);

      set(WI_Test, MUIA_Window_LeftEdge, WindowLeftEdge);
      set(WI_Test, MUIA_Window_TopEdge, WindowTopEdge);
      set(WI_Test, MUIA_Window_Width, WindowWidth);
      set(WI_Test, MUIA_Window_ScreenTitle, "AvailMem");
      set(WI_Test, MUIA_Window_Open, TRUE);

      timersignal  = 1L << TimerMP->mp_SigBit;

      TimerIO->tr_node.io_Command = TR_ADDREQUEST;
      TimerIO->tr_time.tv_secs = TimeToWait;
      TimerIO->tr_time.tv_micro = 0;
      SendIO((struct IORequest *) TimerIO);

      UpdateMemBar();

      while(!Done) {
         ULONG ID, Signal;
         BOOL  Timer;

         ID = DoMethod(AP_Test, MUIM_Application_Input, &Signal);
         switch(ID) {
            case MUIV_Application_ReturnID_Quit:
               Done = TRUE;
               break;
            default:
               break;
         }

         if (!Done && Signal) Wait(Signal | timersignal);

         Timer = FALSE;
         while(reply = (struct I0ExtSer *)GetMsg(TimerMP)) {
            Timer = TRUE;
         }

         if (Timer) {
            AbortIO((struct IORequest *) TimerIO);
            WaitIO((struct IORequest *) TimerIO);
            TimerIO->tr_node.io_Command = TR_ADDREQUEST;
            TimerIO->tr_time.tv_secs = TimeToWait;
            TimerIO->tr_time.tv_micro = 0;
            SendIO((struct IORequest *) TimerIO);

            UpdateMemBar();
         }
      }

      set(WI_Test, MUIA_Window_Open, FALSE);

      MUI_DisposeObject(AP_Test);
      closeall();
      return(0);
   } else {
      printf("Konnte das Fenster nicht öffnen!\n");
      return(10);
   }
}



/**************
*  openall()  *
**************/

void openall(void)
{
   if ((MUIMasterBase = OpenLibrary(MUIMASTER_NAME, MUIMASTER_VMIN)) == NULL)
      closeout("Couldn't open MUI", RETURN_FAIL);

   if ((TimerMP = CreatePort(NULL, NULL)) == NULL)
      closeout("Couldn't create messageport", RETURN_FAIL);

   if ((TimerIO = (struct timerequest *) CreateExtIO(TimerMP, sizeof(struct timerequest))) == NULL)
      closeout("Couldn't create IOrequest", RETURN_FAIL);

   if ((TimerError = OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *) TimerIO, 0L)) != NULL)
      closeout("Couldn't open timer.device", RETURN_FAIL);

   if ((IntuitionBase = OpenLibrary("intuition.library", 37)) == NULL)
      closeout("Can't open intuition",RETURN_FAIL);

   if ((UtilityBase = OpenLibrary("utility.library", 37)) == NULL)
      closeout("Couldn't open utility-library", RETURN_FAIL);
   
   if ((GaugeMemClass = InitGaugeMemClass()) == NULL)
      closeout("Couldn't create the Boopsi-object", RETURN_FAIL);
}



/***************
*  closeall()  *
***************/

void closeall(void)
{
   AbortIO((struct IORequest *) TimerIO);
   WaitIO((struct IORequest *) TimerIO);

   if (GaugeMemClass)
      FreeGaugeMemClass();

   if (UtilityBase)
      CloseLibrary(UtilityBase);
   
   if (IntuitionBase)
      CloseLibrary((struct Library *)IntuitionBase);

   if (!TimerError)
      CloseDevice((struct IORequest *) TimerIO);

   if (TimerIO)
      DeleteExtIO((struct IORequest *) TimerIO);

   if (TimerMP)
      DeletePort(TimerMP);

   if (MUIMasterBase)
      CloseLibrary(MUIMasterBase);

   exit(0);
}



/***************
*  closeout()  *
***************/

void closeout(UBYTE *errstring, LONG rc)
{
   struct EasyStruct myES = {
      sizeof(struct EasyStruct),
      0,
      "Error",
      "Error: %s\n",
      "OK",
   };

   if (*errstring)
      EasyRequest(NULL, &myES, NULL, errstring);

   closeall();
   exit(rc);
}



/*******************
*  UpdateMemBar()  *
*******************/

void UpdateMemBar(void)
{
   static char              Title[80];

   struct Window *MainWindow;
   struct Gadget *GaugeMemGadget;

   get(WI_Test, MUIA_Window_Window, &MainWindow);

   get(GA_Chip, MUIA_Boopsi_Object, &GaugeMemGadget);
   if (MainWindow && GaugeMemGadget)
      RefreshGList(GaugeMemGadget, MainWindow, NULL, 1);

   get(GA_Fast, MUIA_Boopsi_Object, &GaugeMemGadget);
   if (MainWindow && GaugeMemGadget)
      RefreshGList(GaugeMemGadget, MainWindow, NULL, 1);

   sprintf(Title, "%.1fK Chip  %.1fK Fast", (double)AvailMem(MEMF_CHIP)/1024, (double)AvailMem(MEMF_FAST)/1024);
   set(WI_Test, MUIA_Window_Title, Title);
}



/**************************
**  Boopsi Definitionen  **
**************************/

struct GaugeMemData {
   BOOL  Fast;
   ULONG MemLen;
};

Class *PrivateClass = NULL;



__geta4 ULONG DispatchGaugeMem(__a0 Class *cl, __a2 Object *o, __a1 Msg msg)
{
   ULONG retval = FALSE;
   Object   *object;
   struct    GaugeMemData *this;

   switch (msg->MethodID) {
      case OM_NEW:
         if (object = (Object *)DoSuperMethodA(cl, o, msg)) {
            this = INST_DATA(cl, object);
            this->Fast = GetTagData(MG_Fast, FALSE, ((struct opSet *) msg)->ops_AttrList);

            memhdr = SysBase->MemList.lh_Head;
            mem_start = memhdr->mh_Lower;
            mem_end = memhdr->mh_Upper;
            mem_len = (ULONG)mem_end - (ULONG)mem_start;

            if (this->Fast)
               this->MemLen = mem_len;
            else {
               memhdr = memhdr->mh_Node.ln_Succ;
               if (memhdr->mh_Node.ln_Succ == NULL)
                  memhdr = SysBase->MemList.lh_Head;
               mem_start = memhdr->mh_Lower;
               mem_end = memhdr->mh_Upper;
               mem_len = (ULONG)mem_end - (ULONG)mem_start;

               this->MemLen = mem_len;
            }
            retval = (ULONG)object;
         }
         break;

      case GM_RENDER:
         struct gpRender *gpr = (struct gpRender *)msg;
         struct Gadget   *g = (struct Gadget *)o;
         struct RastPort *rp = gpr->gpr_RPort;
         struct ColorMap *cm = gpr->gpr_GInfo->gi_Screen->ViewPort.ColorMap;
         double   SizeAvail = 0, SizeLargest = 0;
         ULONG Pen;

         UWORD PatternLargest[] = {
            0xaaaa,
            0x5555,
            0,0,
            0,0,
            0,0,
            0,0,
            0,0,
            0,0
         };

         UWORD PatternForDeleting[] = {
            0x5555,
            0xaaaa,
            0,0,
            0,0,
            0,0,
            0,0,
            0,0,
            0,0
         };

         this = INST_DATA(cl, o);
         retval = TRUE;
      
         if (this->Fast) {
            SizeAvail =   ((double)AvailMem(MEMF_FAST)              / (double)this->MemLen) * (double)(g->Width-1);
            SizeLargest = ((double)AvailMem(MEMF_FAST|MEMF_LARGEST) / (double)this->MemLen) * (double)(g->Width-1);
         } else {
            SizeAvail =   ((double)AvailMem(MEMF_CHIP)              / (double)this->MemLen) * (double)(g->Width-1);
            SizeLargest = ((double)AvailMem(MEMF_CHIP|MEMF_LARGEST) / (double)this->MemLen) * (double)(g->Width-1);
         }

         Pen = ObtainBestPen(cm, 0x63333333, 0x63333333, 0x63333333, TAG_END);

//         SetAPen(rp, Pen);
         SetAPen(rp, ColorNumber);
         SetAfPt(rp, PatternLargest, 1);
         RectFill(rp, g->LeftEdge,              g->TopEdge,
                      g->LeftEdge+SizeLargest-1,g->TopEdge+g->Height-1);

         SetAPen(rp, 0);
         SetAfPt(rp, PatternForDeleting, 1);
         RectFill(rp, g->LeftEdge,              g->TopEdge,
                      g->LeftEdge+SizeLargest-1,g->TopEdge+g->Height-1);

         SetAPen(rp, ColorNumber);
         SetAfPt(rp, NULL, 1);
         RectFill(rp, g->LeftEdge+SizeLargest,  g->TopEdge,
                      g->LeftEdge+SizeAvail-1,  g->TopEdge+g->Height-1);

         SetAPen(rp, 0);
         RectFill(rp, g->LeftEdge+SizeAvail,    g->TopEdge,
                      g->LeftEdge+g->Width-1,   g->TopEdge+g->Height-1);

         break;

      default:
         retval = DoSuperMethodA(cl, o, msg);
         break;
   }
   return retval;
}



Class *InitGaugeMemClass(void)
{
   Class *cl;

   if (!PrivateClass)
      if (cl = MakeClass(NULL, GADGETCLASS, NULL, sizeof(struct GaugeMemData), 0)) {
         cl->cl_Dispatcher.h_Entry = (ULONG (*)()) DispatchGaugeMem;
         PrivateClass = cl;
      }
   return PrivateClass;
}



BOOL FreeGaugeMemClass(void)
{
   return FreeClass(PrivateClass);
}

