#include "AvailMem.h"



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

int  wbmain(struct WBStartup *wbs);
int  main(void);
int  RealMain(void);
int openall(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(openall());
}



/*********
*  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(openall());
}



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

int RealMain(void)
{
   AP_Test = ApplicationObject,
      MUIA_Application_Title, "AvailMem",
      MUIA_Application_Version, "$VER: AvailMem 1.02 (08.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",
         MUIA_Window_Activate, FALSE,
         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);
      return(0);
   } else {
      MUI_Request(0, 0, 0, 0, "Ok", "Couldn't start application");
      return(10);
   }
}



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

int openall(void)
{
   int return_value = 0;

   if ((MUIMasterBase = OpenLibrary(MUIMASTER_NAME, MUIMASTER_VMIN)) != NULL) {
      if ((TimerMP = CreatePort(NULL, NULL)) != NULL) {
         if ((TimerIO = (struct timerequest *) CreateExtIO(TimerMP, sizeof(struct timerequest))) != NULL) {
            if ((TimerError = OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *) TimerIO, 0L)) == NULL) {
               if ((IntuitionBase = OpenLibrary("intuition.library", 37)) != NULL) {
                  if ((UtilityBase = OpenLibrary("utility.library", 37)) != NULL) {
                     if ((GaugeMemClass = InitGaugeMemClass()) != NULL) {

                        return_value = RealMain();

                        AbortIO((struct IORequest *) TimerIO);
                        WaitIO((struct IORequest *) TimerIO);
                        FreeGaugeMemClass();
                     } else {
                        MUI_Request(0, 0, 0, 0, "Ok", "Couldn't init GaugeMem");
                        return_value = 5;
                     }
                     CloseLibrary(UtilityBase);
                  } else {
                     MUI_Request(0, 0, 0, 0, "Ok", "Couldn't open utility-library");
                     return_value = 5;
                  }
                  CloseLibrary((struct Library *)IntuitionBase);
               } else {
                  MUI_Request(0, 0, 0, 0, "Ok", "Can't open intuition-library",RETURN_FAIL);
                  return_value = 5;
               }
               CloseDevice((struct IORequest *) TimerIO);
            } else {
               MUI_Request(0, 0, 0, 0, "Ok", "Couldn't open TimerDevice");
               return_value = 5;
            }
            DeleteExtIO((struct IORequest *) TimerIO);
         } else {
            MUI_Request(0, 0, 0, 0, "Ok", "Couldn't create ExtIO");
            return_value = 5;
         }
         DeletePort(TimerMP);
      } else {
         MUI_Request(0, 0, 0, 0, "Ok", "Couldn't create Port");
         return_value = 5;
      }
      CloseLibrary(MUIMasterBase);
   } else {
      MUI_Request(0, 0, 0, 0, "Ok", "Couldn't open MUIMasterBase");
      return_value = 5;
   }
   return(return_value);
}



/*******************
*  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;
         double   SizeAvail = 0, SizeLargest = 0;

         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);
         }

         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);
}

