/**
 * 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"

struct AllocationsCallbackUserData {
    APTR ud_CIAAUsers;
    APTR ud_CIABUsers;
    APTR ud_PortsUsers;
};

static UBYTE alcnonalc[] = "<free>";
static UBYTE alcunknown[] = "<allocated>";
static UBYTE alcnotavail[] = "<not available>";

static ULONG __asm __saveds __interrupt DummyIntFunc( void )
{
    return 0;
}

static UBYTE *AlcMiscUser( ULONG unit )
{
    struct Library *MiscBase;

    if (MiscBase = OpenResource(MISCNAME)) {
        UBYTE *name;

        if (name = AllocMiscResource(unit, "Scout")) {
            return name;
        } else {
            FreeMiscResource(unit);
            return alcnonalc;
        }
    } else {
        return alcnotavail;
    }
}

static UBYTE *AlcCIAUser( UBYTE *res,
                          WORD unit )
{
    struct Library *CiaBase;

    if (CiaBase = OpenResource(res)) {
        struct Interrupt dummyInt, *intr;

        memset(&dummyInt, 0x00, sizeof(struct Interrupt));
        dummyInt.is_Node.ln_Type = NT_INTERRUPT;
        dummyInt.is_Node.ln_Pri = -127;
        dummyInt.is_Node.ln_Name = "« Scout's Dummy Int »";
        dummyInt.is_Code = (void (*)())DummyIntFunc;

        Disable();
        if ((intr = AddICRVector(CiaBase, unit, &dummyInt)) == NULL) RemICRVector(CiaBase, unit, &dummyInt);
        Enable();

        if (intr) {
            return (intr->is_Node.ln_Name) ? (UBYTE *)intr->is_Node.ln_Name : alcunknown;
        } else {
            return alcnonalc;
        }
    } else {
        return alcnotavail;
    }
}

static void ReceiveList( void (* callback)( struct AllocationEntry *ae, void *userData ),
                         void *userData )
{
    struct AllocationEntry *ae;

    if (ae = tbAllocVecPooled(globalPool, sizeof(struct AllocationEntry))) {
        if (SendDaemon("GetAlcList")) {
            while (ReceiveDecodedEntry((UBYTE *)ae, sizeof(struct AllocationEntry))) {
                callback(ae, userData);
            }
        }

        tbFreeVecPooled(globalPool, ae);
    }
}

static void IterateList( void (* callback)( struct AllocationEntry *ae, void *userData ),
                         void *userData )
{
    struct AllocationEntry *ae;

    if (ae = tbAllocVecPooled(globalPool, sizeof(struct AllocationEntry))) {
        ULONG i;

        Forbid();

        for (i = 0; i < 5; i++) stccpy(ae->ae_CIAAUsers[i], AlcCIAUser(CIAANAME, i), sizeof(ae->ae_CIAAUsers[i]));
        for (i = 0; i < 5; i++) stccpy(ae->ae_CIABUsers[i], AlcCIAUser(CIABNAME, i), sizeof(ae->ae_CIABUsers[i]));
        stccpy(ae->ae_PortsUsers[0], AlcMiscUser(MR_SERIALPORT), sizeof(ae->ae_PortsUsers[0]));
        stccpy(ae->ae_PortsUsers[1], AlcMiscUser(MR_SERIALBITS), sizeof(ae->ae_PortsUsers[1]));
        stccpy(ae->ae_PortsUsers[2], AlcMiscUser(MR_PARALLELPORT), sizeof(ae->ae_PortsUsers[2]));
        stccpy(ae->ae_PortsUsers[3], AlcMiscUser(MR_PARALLELBITS), sizeof(ae->ae_PortsUsers[3]));

        Permit();

        callback(ae, userData);

        tbFreeVecPooled(globalPool, ae);
    }
}

static void UpdateCallback( struct AllocationEntry *ae,
                            void *userData )
{
    struct AllocationsCallbackUserData *ud = (struct AllocationsCallbackUserData *)userData;

    MySetContents(ud->ud_CIAAUsers, "%s\n%s\n%s\n%s\n%s", ae->ae_CIAAUsers[0], ae->ae_CIAAUsers[1], ae->ae_CIAAUsers[2], ae->ae_CIAAUsers[3], ae->ae_CIAAUsers[4]);
    MySetContents(ud->ud_CIABUsers, "%s\n%s\n%s\n%s\n%s", ae->ae_CIABUsers[0], ae->ae_CIABUsers[1], ae->ae_CIABUsers[2], ae->ae_CIABUsers[3], ae->ae_CIABUsers[4]);
    MySetContents(ud->ud_PortsUsers, "%s\n%s\n%s\n%s", ae->ae_PortsUsers[0], ae->ae_PortsUsers[1], ae->ae_PortsUsers[2], ae->ae_PortsUsers[3]);
}

static void PrintCallback( struct AllocationEntry *ae,
                           void *userData )
{
    PrintFOneLine((BPTR)userData, "\nCIA A:\n\n");
    PrintFOneLine((BPTR)userData, "Timer A: %s\nTimer B: %s\n  Alarm: %s\n Serial: %s\n   Flag: %s\n", ae->ae_CIAAUsers[0], ae->ae_CIAAUsers[1], ae->ae_CIAAUsers[2], ae->ae_CIAAUsers[3], ae->ae_CIAAUsers[4]);

    PrintFOneLine((BPTR)userData, "\n\nCIA B:\n\n");
    PrintFOneLine((BPTR)userData, "Timer A: %s\nTimer B: %s\n  Alarm: %s\n Serial: %s\n   Flag: %s\n", ae->ae_CIABUsers[0], ae->ae_CIABUsers[1], ae->ae_CIABUsers[2], ae->ae_CIABUsers[3], ae->ae_CIABUsers[4]);

    PrintFOneLine((BPTR)userData, "\n\nSerial & Parallel Ports:\n\n");
    PrintFOneLine((BPTR)userData, "     Serial Port: %s\n  Serial Control: %s\n   Parallel Port: %s\nParallel Control: %s\n", ae->ae_PortsUsers[0], ae->ae_PortsUsers[1], ae->ae_PortsUsers[2], ae->ae_PortsUsers[3]);
}

static void SendCallback( struct AllocationEntry *ae,
                          void *userData )
{
    SendEncodedEntry((UBYTE *)ae, sizeof(struct AllocationEntry));
}

static ULONG __saveds mNew( struct IClass *cl,
                            Object *obj,
                            struct opSet *msg )
{
    static UBYTE *allocationsPages[] = { "CIA", "Ports", NULL };
    APTR pages, ciaaText, ciabText, portsText, updateButton, printButton, exitButton;

    if (obj = (Object *)DoSuperNew(cl, obj,
        MUIA_HelpNode, AllocationsText,
        MUIA_Window_ID, MakeID('A','L','L','C'),
        WindowContents, VGroup,

            Child, pages = RegisterGroup(allocationsPages),

                Child, HGroup,
                    Child, HGroup,
                        GroupFrameT("CIA A"),
                        Child, HGroup,
                            Child, MyLabel2(MUIX_R "Timer A:\nTimer B:\nAlarm:\nSerial:\nFlag:"),
                            Child, ciaaText = MyTextObject(),
                        End,
                    End,
                    Child, HGroup,
                        GroupFrameT("CIA B"),
                        Child, HGroup,
                            Child, MyLabel2(MUIX_R "Timer A:\nTimer B:\nAlarm:\nSerial:\nFlag:"),
                            Child, ciabText = MyTextObject(),
                        End,
                    End,
                End,

                Child, HGroup,
                    GroupFrameT("Ports"),
                    Child, HGroup,
                        Child, MyLabel2(MUIX_R "Serial Port:\nSerial Control:\nParallel Port:\nParallel Control:"),
                        Child, portsText = MyTextObject(),
                    End,
                End,
            End,

            Child, MyVSpace(4),

            Child, HGroup, MUIA_Group_SameSize, TRUE,
                Child, updateButton = MakeButton(txtUpdate),
                Child, printButton  = MakeButton(txtPrint),
                Child, exitButton   = MakeButton(txtExit),
            End,
        End,
        TAG_MORE, msg->ops_AttrList))
    {
        struct AllocationsWinData *awd = INST_DATA(cl, obj);
        APTR parent;

        awd->awd_CIAAText = ciaaText;
        awd->awd_CIABText = ciabText;
        awd->awd_PortsText = portsText;

        parent = (APTR)GetTagData(MUIA_Window_ParentWindow, (ULONG)NULL, msg->ops_AttrList);

        set(obj, MUIA_Window_Title, MyGetWindowTitle("ALLOCATIONS", awd->awd_Title, sizeof(awd->awd_Title)));
        set(obj, MUIA_Window_ActiveObject, pages);
        set(pages, MUIA_CycleChain, TRUE);
        MySetContents(awd->awd_CIAAText, "$%08lx\n$%08lx\n$%08lx\n$%08lx\n$%08lx", 0, 0, 0, 0, 0);
        MySetContents(awd->awd_CIABText, "$%08lx\n$%08lx\n$%08lx\n$%08lx\n$%08lx", 0, 0, 0, 0, 0);
        MySetContents(awd->awd_PortsText, "$%08lx\n$%08lx\n$%08lx\n$%08lx", 0, 0, 0, 0);

        DoMethod(parent,       MUIM_Window_AddChildWindow, obj);
        DoMethod(obj,          MUIM_Notify, MUIA_Window_CloseRequest, TRUE,  MUIV_Notify_Application, 5, MUIM_Application_PushMethod, parent, 2, MUIM_Window_RemChildWindow, obj);
        DoMethod(updateButton, MUIM_Notify, MUIA_Pressed,             FALSE, obj,                     1, MUIM_AllocationsWin_Update);
        DoMethod(printButton,  MUIM_Notify, MUIA_Pressed,             FALSE, obj,                     1, MUIM_AllocationsWin_Print);
        DoMethod(exitButton,   MUIM_Notify, MUIA_Pressed,             FALSE, obj,                     3, MUIM_Set, MUIA_Window_CloseRequest, TRUE);
    }

    return (ULONG)obj;
}

static ULONG __saveds mDispose( struct IClass *cl,
                                Object *obj,
                                struct opSet *msg )
{
    set(obj, MUIA_Window_Open, FALSE);

    return (DoSuperMethodA(cl, obj, msg));
}

static ULONG __saveds mUpdate( struct IClass *cl,
                               Object *obj,
                               Msg msg )
{
    struct AllocationsWinData *awd = INST_DATA(cl, obj);
    struct AllocationsCallbackUserData ud;

    ud.ud_CIAAUsers = awd->awd_CIAAText;
    ud.ud_CIABUsers = awd->awd_CIABText;
    ud.ud_PortsUsers = awd->awd_PortsText;

    if (clientstate) {
        ReceiveList(UpdateCallback, &ud);
    } else {
        IterateList(UpdateCallback, &ud);
    }

    return 0;
}

static ULONG __saveds mPrint( struct IClass *cl,
                              Object *obj,
                              Msg msg )
{
    PrintAllocations(NULL);

    return 0;
}

ULONG __asm __saveds AllocationsWinDispatcher( register __a0 struct IClass *cl,
                                               register __a2 Object *obj,
                                               register __a1 Msg msg )
{
    switch (msg->MethodID) {
        case OM_NEW:                     return (mNew(cl, obj, (APTR)msg));
        case OM_DISPOSE:                 return (mDispose(cl, obj, (APTR)msg));
        case MUIM_AllocationsWin_Update: return (mUpdate(cl, obj, (APTR)msg));
        case MUIM_AllocationsWin_Print:  return (mPrint(cl, obj, (APTR)msg));
    }

    return (DoSuperMethodA(cl, obj, msg));
}

void PrintAllocations( char *filename )
{
    BPTR handle;

    if (handle = HandlePrintStart(filename)) {
        IterateList(PrintCallback, (void *)handle);
    }

    HandlePrintStop();
}

void SendAlcList( void )
{
    IterateList(SendCallback, NULL);
}

