/*
 * This is an example of adding a gadget to the clock area
 */

#define __USE_SYSBASE
long    __oslibversion = 37L;

#include <exec/types.h>
#include <exec/exec.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <graphics/gfx.h>
#include <devices/timer.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/graphics.h>

#include "messages.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define AREA_SIZE 200

VOID ShutDown(STRPTR, UBYTE);
VOID SendStartMsg(START_MSG *, APTR, UBYTE);
BOOL StartTimer(VOID);
VOID CloseTimer(VOID);
VOID DrawMeter(VOID);
VOID SetUpRP(VOID);
BOOL DoReq(VOID);

UBYTE ver[] = {"$VER: MemMeter v1.0 "__AMIGADATE__};

struct RastPort     rp;         /* A RastPort for drawing */
PLANEPTR            t_plane;    /* For the TmpRas */
NEW_BITMAP          nbm;        /* A BitMap to attatch to the RastPort */
START_MSG           smsg;       /* The message to send */
START_MENU_GADGET   gad;        /* The gadget */
struct MsgPort     *my_port;    /* A port for StartMenu to send messages to */

struct timerequest *TimerIO;
struct MsgPort     *TimerPort;
struct Library     *TimerBase;

ULONG mem_total, mem_fast, mem_chip;

UWORD total_min, total_max,
      fast_min, fast_max,
      chip_min, chip_max;

main()
{
    struct Message *m;
    struct MsgPort *start_port;
    START_MSG      *rep_smsg;
    ULONG t_sig, p_sig, sig, cnt = 0;
    BOOL done = FALSE;
    UWORD space;

    /* Set up fixed variables */
    mem_total = AvailMem(MEMF_TOTAL);
    mem_fast  = AvailMem(MEMF_FAST|MEMF_TOTAL);
    mem_chip  = AvailMem(MEMF_CHIP|MEMF_TOTAL);

    /* Set up the timer */
    if (!(StartTimer()))
        ShutDown("Could not get timer device", 5);

    /* Try to find StartMenu */
    while (cnt++ < 10) {
        if (start_port = FindPort(STARTMENU_PORT_NAME)) break;
        else Delay(250);
    }
    if (!start_port)
        ShutDown("Could not find StartMenu.", 5);

    /* Create a port */
    if (!(my_port = CreatePort(0, 0)))
        ShutDown("Could not create a message port.", 5);

    /* Prepare the Message */
    smsg.sm_Msg.mn_Node.ln_Type = NT_MESSAGE;
    smsg.sm_Msg.mn_Length = START_MSG_SIZE;
    smsg.sm_Msg.mn_ReplyPort = my_port;

    /* Ask StartMenu to create a BitMap */
    SendStartMsg(&smsg, (APTR)(&nbm), CREATE_NEW_BITMAP);

    /* Check the BitMap */
    if (NULL == nbm.nbm_BitMap)
        ShutDown("Could not create BitMap.", 5);

    /* Set up some drawing variables */
    space = ((nbm.nbm_Size - 2) / 3) - 1;
    chip_min = 1;
    chip_max = chip_min + space;
    fast_min = chip_max + 1;
    fast_max = fast_min + space;
    total_min = fast_max + 1;
    total_max = fast_max + space;
    while (total_max < (nbm.nbm_Size - 1)) total_max++;

    /* Setup the RastPort for drawing */
    SetUpRP();

    /* Ask StartMenu to create a gadget */
    gad.smg_BitMap = nbm.nbm_BitMap;
    gad.smg_Port   = my_port;
    SendStartMsg(&smsg, (APTR)(&gad), ADD_CLOCK_GADGET);

    /* Check the gadget */
    if (NULL == gad.smg_Gadget)
        ShutDown("Could not create gadget.", 5);

    DrawMeter();

    /* Everything ok. Start loop */
    t_sig = 1L << TimerPort->mp_SigBit;
    p_sig = 1L << my_port->mp_SigBit;
    while (!done) {
        sig = Wait(t_sig | p_sig);

        /* A message from StartMenu - copy and reply ASAP!!! */
        if (sig & p_sig) {
            while (rep_smsg = (START_MSG *)GetMsg(my_port)) {
                if (rep_smsg->sm_Command == GADGET_STATUS) {
                    /* An IDCMP message */
                    ReplyMsg((struct Message *)rep_smsg);
                    done = DoReq();
                }
                else {
                    /* Any other message is a quit message */
                    ReplyMsg((struct Message *)rep_smsg);
                    gad.smg_Gadget = NULL;  /* Set this so we don't free it */
                    done = TRUE;
                }
            }
        }
        else if (sig & t_sig) {
            while (m = GetMsg(TimerPort)) /* NULL */ ;

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

    ShutDown(NULL, 0);
}

VOID ShutDown(STRPTR s, UBYTE rc)
{
    CloseTimer();

    /* Remove our gadget */
    if (gad.smg_Gadget) {
        RE_GADGET re;
        re.re_Gadget = gad.smg_Gadget;
        SendStartMsg(&smsg, (APTR)(&re), REM_CLOCK_GADGET);
    }

    if (my_port) DeletePort(my_port);

    if (t_plane) FreeRaster(t_plane, nbm.nbm_Size, nbm.nbm_Size);
    
    if (s) fprintf(stderr, "MemMeter: %s\n", s);

    exit(rc);
}

VOID SendStartMsg(START_MSG *msg, APTR data, UBYTE command)
{
    struct MsgPort *start_port;
    
    if (start_port = FindPort(STARTMENU_PORT_NAME)) {
        msg->sm_Command = command;
        msg->sm_Data    = data;
        PutMsg(start_port, (struct Message *)msg);
        WaitPort(my_port);           /* StartMenu replies to ALL messages!! */
        GetMsg(my_port);
    }
}

BOOL StartTimer(VOID)
{
    if (TimerPort = CreatePort(0,0)) {
        if (TimerIO = (struct timerequest *)CreateIORequest(TimerPort, sizeof(struct timerequest))) {
            if (!OpenDevice( "timer.device", UNIT_VBLANK, (struct IORequest *)TimerIO, NULL )) {
                TimerIO->tr_node.io_Command = TR_ADDREQUEST;
                TimerIO->tr_time.tv_secs    = 10;
                TimerIO->tr_time.tv_micro   = 0;
                SendIO((struct IORequest *)TimerIO);
                TimerBase = (struct Library *)TimerIO->tr_node.io_Device;
                return(TRUE);
            }
            DeleteIORequest((struct IORequest *)TimerIO);
            TimerIO = NULL;
        }
        DeletePort(TimerPort);
        TimerPort = NULL;
    }
    return(FALSE);
}

VOID CloseTimer(VOID)
{
    if (TimerIO) {
        if (TimerIO->tr_node.io_Command == TR_ADDREQUEST)
            AbortIO((struct IORequest *)TimerIO);
        CloseDevice((struct IORequest *)TimerIO);
        DeleteIORequest((struct IORequest *)TimerIO);
        TimerIO = NULL;
    }
    if (TimerPort) {
        DeletePort(TimerPort);
        TimerPort = NULL;
    }
}

VOID DrawMeter(VOID)
{
    ULONG free_chip, free_fast, free_total, chip_start, fast_start, total_start;
    FLOAT perc;
    RE_GADGET re;
    static ULONG old_chip = 0, old_fast = 0;

    free_chip = AvailMem(MEMF_CHIP);
    perc = 1.0 - ((FLOAT)free_chip / (FLOAT)mem_chip);
    chip_start = perc * (nbm.nbm_Size - 2);

    free_fast = AvailMem(MEMF_FAST);
    perc = 1.0 - ((FLOAT)free_fast / (FLOAT)mem_fast);
    fast_start = perc * (nbm.nbm_Size - 2);

    free_total = free_chip + free_fast;
    perc = 1.0 - ((FLOAT)free_total / (FLOAT)mem_total);
    total_start = perc * (nbm.nbm_Size - 2);

    /* Olny refresh if there's a change */
    if ((old_chip != chip_start) || (old_fast != fast_start)) {
        old_chip  = chip_start;
        old_fast  = fast_start;
        SetRast(&rp, 0);
        SetAPen(&rp, 1);
        RectFill(&rp, chip_min, chip_start, chip_max, nbm.nbm_Size - 1);
        SetAPen(&rp, 2);
        RectFill(&rp, fast_min, fast_start, fast_max, nbm.nbm_Size - 1);
        SetAPen(&rp, 3);
        RectFill(&rp, total_min, total_start, total_max, nbm.nbm_Size - 1);

        SetAPen(&rp, nbm.nbm_FrontPen);
        Move(&rp, 0, 0);
        Draw(&rp, nbm.nbm_Size - 1, 0);
        Draw(&rp, nbm.nbm_Size - 1, nbm.nbm_Size - 1);
        Draw(&rp, 0, nbm.nbm_Size - 1);
        Draw(&rp, 0, 0);

        re.re_Gadget = gad.smg_Gadget;
        SendStartMsg(&smsg, (APTR)(&re), REFRESH_CLOCK_GADGET);
    }
}

VOID SetUpRP(VOID)
{
    static struct AreaInfo areaInfo = {0};
    static WORD areaBuffer[AREA_SIZE];
    static struct TmpRas tmpRas;

    InitRastPort(&rp);
    rp.BitMap = nbm.nbm_BitMap;

    InitArea(&areaInfo, areaBuffer, (AREA_SIZE * 2) / 5);
    rp.AreaInfo = &areaInfo;

    if (!(t_plane = AllocRaster(nbm.nbm_Size, nbm.nbm_Size)))
        ShutDown("Could not allocate temp raster.", 5);
    InitTmpRas(&tmpRas, t_plane, nbm.nbm_Size * nbm.nbm_Size);
    rp.TmpRas = &tmpRas;
}

BOOL DoReq(VOID)
{
    struct EasyStruct es = {
        sizeof(struct EasyStruct),
        0,
        "MemMeter",
        "Chip Mem : %ld\nFast Mem : %ld\nTotalMem: %ld",
        "Okay|Quit MM"
    };
    ULONG mchip, mfast,mtotal;

    mfast  = AvailMem(MEMF_FAST);
    mchip  = AvailMem(MEMF_CHIP);
    mtotal = mfast + mchip;

    if (0 == EasyRequest(NULL, &es, NULL, mchip, mfast, mtotal))
        return(TRUE);
    return(FALSE);
}
