/**
 * 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 ResetCallbackUserData {
    APTR ud_List;
    ULONG ud_Count;
};

static __asm __saveds LONG resetlist_con2func(register __a2 Object *obj, register __a1 struct NList_ConstructMessage *msg, register __a0 struct Hook *hook)
{
    return AllocListEntry(msg->pool, msg->entry, sizeof(struct ResetHandlerEntry));
}

MakeHook(resetlist_con2hook, resetlist_con2func);

static __asm __saveds LONG resetlist_des2func(register __a2 Object *obj, register __a1 struct NList_DestructMessage *msg, register __a0 struct Hook *hook)
{
    FreeListEntry(msg->pool, &msg->entry);

    return 0;
}

MakeHook(resetlist_des2hook, resetlist_des2func);

static __asm __saveds LONG resetlist_dsp2func(register __a2 Object *obj, register __a1 struct NList_DisplayMessage *msg, register __a0 struct Hook *hook)
{
    struct ResetHandlerEntry *rhe = (struct ResetHandlerEntry *)msg->entry;

    if (rhe) {
        msg->strings[0] = rhe->rhe_Address;
        msg->strings[1] = rhe->rhe_Name;
        msg->strings[2] = rhe->rhe_Type;
        msg->strings[3] = rhe->rhe_Pri;
        msg->strings[4] = rhe->rhe_Data;
        msg->strings[5] = rhe->rhe_Code;
    } else {
        msg->strings[0] = "Address";
        msg->strings[1] = "ln_Name";
        msg->strings[2] = "ln_Type";
        msg->strings[3] = "ln_Pri";
        msg->strings[4] = "is_Data";
        msg->strings[5] = "is_Code";
        msg->preparses[0] = MUIX_B;
        msg->preparses[1] = MUIX_B;
        msg->preparses[2] = MUIX_B;
        msg->preparses[3] = MUIX_B;
        msg->preparses[4] = MUIX_B;
        msg->preparses[5] = MUIX_B;
    }

    return 0;
}

MakeHook(resetlist_dsp2hook, resetlist_dsp2func);

static LONG resetlist_cmp2colfunc( struct ResetHandlerEntry *rhe1,
                                   struct ResetHandlerEntry *rhe2,
                                   ULONG column )
{
    LONG pri1, pri2;

    switch (column) {
        case 0: return stricmp(rhe1->rhe_Address, rhe2->rhe_Address);
        case 1: return stricmp(rhe1->rhe_Name, rhe2->rhe_Name);
        case 2: return stricmp(rhe1->rhe_Type, rhe2->rhe_Type);
        case 3: IsDec(rhe1->rhe_Pri, &pri1); IsDec(rhe2->rhe_Pri, &pri2); return pri2 - pri1;
        case 4: return stricmp(rhe1->rhe_Data, rhe2->rhe_Data);
        case 5: return stricmp(rhe1->rhe_Code, rhe2->rhe_Code);
    }
}

static __asm __saveds LONG resetlist_cmp2func(register __a2 Object *obj, register __a1 struct NList_CompareMessage *msg, register __a0 struct Hook *hook)
{
    LONG cmp;
    struct ResetHandlerEntry *rhe1, *rhe2;
    ULONG col1, col2;

    rhe1 = (struct ResetHandlerEntry *)msg->entry1;
    rhe2 = (struct ResetHandlerEntry *)msg->entry2;
    col1 = msg->sort_type & MUIV_NList_TitleMark_ColMask;
    col2 = msg->sort_type2 & MUIV_NList_TitleMark2_ColMask;

    if (msg->sort_type == MUIV_NList_SortType_None) return 0;

    if (msg->sort_type & MUIV_NList_TitleMark_TypeMask) {
        cmp = resetlist_cmp2colfunc(rhe2, rhe1, col1);
    } else {
        cmp = resetlist_cmp2colfunc(rhe1, rhe2, col1);
    }

    if (cmp != 0 || col1 == col2) return cmp;

    if (msg->sort_type2 & MUIV_NList_TitleMark2_TypeMask) {
        cmp = resetlist_cmp2colfunc(rhe2, rhe1, col2);
    } else {
        cmp = resetlist_cmp2colfunc(rhe1, rhe2, col2);
    }

    return cmp;
}

MakeHook(resetlist_cmp2hook, resetlist_cmp2func);

static void __asm __saveds dummyIRQFunc( register __a1 APTR handlerData )
{
    return;
}

static void ReceiveList( void (* callback)( struct ResetHandlerEntry *rhe, void *userData ),
                         void *userData )
{
    struct ResetHandlerEntry *rhe;

    if (rhe = tbAllocVecPooled(globalPool, sizeof(struct ResetHandlerEntry))) {
        if (SendDaemon("GetResetList")) {
            while (ReceiveDecodedEntry((UBYTE *)rhe, sizeof(struct ResetHandlerEntry))) {
                callback(rhe, userData);
            }
        }

        tbFreeVecPooled(globalPool, rhe);
    }
}

static void IterateList( void (* callback)( struct ResetHandlerEntry *rhe, void *userData ),
                         void *userData )
{
    struct MinList tmplist;
    struct ResetHandlerEntry *rhe, *_rhe;
    struct MsgPort *mp;

    NewList((struct List *)&tmplist);

    if (mp = CreateMsgPort()) {
        struct IOStdReq *io;

        if (io = (struct IOStdReq *)CreateIORequest(mp, sizeof(struct IOStdReq))) {
            if (OpenDevice("keyboard.device", 0, io, 0) == 0) {
                struct Interrupt *myirq;

                if (myirq = tbAllocVecPooled(globalPool, sizeof(struct Interrupt))) {
                    struct List *irqlist;
                    struct Interrupt *irq;

                    myirq->is_Node.ln_Type = NT_INTERRUPT;
                    myirq->is_Node.ln_Pri = -32;
                    myirq->is_Node.ln_Name = "Scout";
                    myirq->is_Code = (void (*)())dummyIRQFunc;
                    myirq->is_Data = NULL;

                    io->io_Command = KBD_ADDRESETHANDLER;
                    io->io_Data = myirq;

                    DoIO(io);

                    Forbid();

                    irqlist = FindListOfNode(myirq);

                    ITERATE_LIST(irqlist, struct Interrupt *, irq) {
                        if (irq != myirq) {
                            if (rhe = AllocVec(sizeof(struct ResetHandlerEntry), MEMF_PUBLIC)) {
                                rhe->rhe_Addr = irq;

                                _snprintf(rhe->rhe_Address, sizeof(rhe->rhe_Address), "$%08lx", irq);
                                stccpy(rhe->rhe_Name, nonetest(irq->is_Node.ln_Name), sizeof(rhe->rhe_Name));
                                stccpy(rhe->rhe_Type, GetNodeType(irq->is_Node.ln_Type), sizeof(rhe->rhe_Type));
                                _snprintf(rhe->rhe_Pri, sizeof(rhe->rhe_Pri), "%4ld", irq->is_Node.ln_Pri);
                                _snprintf(rhe->rhe_Data, sizeof(rhe->rhe_Data), "$%08lx", irq->is_Data);
                                if (points2ram((APTR)irq->is_Code)) {
                                   _snprintf(rhe->rhe_Code, sizeof(rhe->rhe_Code), MUIX_PH "$%08lx" MUIX_PT, irq->is_Code);
                                } else {
                                   _snprintf(rhe->rhe_Code, sizeof(rhe->rhe_Code), "$%08lx", irq->is_Code);
                                }

                                AddTail((struct List *)&tmplist, (struct Node *)rhe);
                            }
                        }
                    }

                    Permit();

                    io->io_Command = KBD_REMRESETHANDLER;
                    io->io_Data = myirq;
                    DoIO(io);

                    tbFreeVecPooled(globalPool, myirq);
                }

                CloseDevice(io);
            }

            DeleteIORequest(io);
        }

        DeleteMsgPort(mp);
    }

    ITERATE_CHANGING_LIST(&tmplist, struct ResetHandlerEntry *, rhe, _rhe) {
        callback(rhe, userData);
        FreeVec(rhe);
    }
}

static void UpdateCallback( struct ResetHandlerEntry *rhe,
                            void *userData )
{
    struct ResetCallbackUserData *ud = (struct ResetCallbackUserData *)userData;

    InsertBottomEntry(ud->ud_List, rhe);
    ud->ud_Count++;
}

static void PrintCallback( struct ResetHandlerEntry *rhe,
                           void *userData )
{
    if (points2ram((APTR)rhe->rhe_Addr->is_Code)) {
        PrintFOneLine((BPTR)userData, " %s %-9s %4s %s  %-9.9s  %s\n", rhe->rhe_Address, rhe->rhe_Type, rhe->rhe_Pri, rhe->rhe_Data, rhe->rhe_Code + 2, rhe->rhe_Name);
    } else {
        PrintFOneLine((BPTR)userData, " %s %-9s %4s %s  %-9.9s  %s\n", rhe->rhe_Address, rhe->rhe_Type, rhe->rhe_Pri, rhe->rhe_Data, rhe->rhe_Code, rhe->rhe_Name);
    }
}

static void SendCallback( struct ResetHandlerEntry *rhe,
                          void *userData )
{
    SendEncodedEntry((UBYTE *)rhe, sizeof(struct ResetHandlerEntry));
}

static ULONG __saveds mNew( struct IClass *cl,
                            Object *obj,
                            struct opSet *msg )
{
    APTR resetlist, resettext, resetcount, updateButton, printButton, removeButton, priorityButton, exitButton;

    if (obj = (Object *)DoSuperNew(cl, obj,
        MUIA_HelpNode, ResetHandlersText,
        MUIA_Window_ID, MakeID('R','S','T','H'),
        WindowContents, VGroup,

            Child, resetlist = MyNListviewObject(MakeID('R','S','L','V'), "BAR,BAR,BAR P=" MUIX_C ",BAR P=" MUIX_R ",BAR,BAR", &resetlist_con2hook, &resetlist_des2hook, &resetlist_dsp2hook, &resetlist_cmp2hook, TRUE),
            Child, MyBelowListview(&resettext, &resetcount),

            Child, MyVSpace(4),

            Child, HGroup, MUIA_Group_SameSize, TRUE,
                Child, updateButton   = MakeButton(txtUpdate),
                Child, printButton    = MakeButton(txtPrint),
                Child, removeButton   = MakeButton(txtRemove),
                Child, priorityButton = MakeButton(txtPriority),
                Child, exitButton     = MakeButton(txtExit),
            End,
        End,
        TAG_MORE, msg->ops_AttrList))
    {
        struct ResetHandlersWinData *rhwd = INST_DATA(cl, obj);
        APTR parent;

        rhwd->rhwd_ResetHandlerList = resetlist;
        rhwd->rhwd_ResetHandlerText = resettext;
        rhwd->rhwd_ResetHandlerCount = resetcount;
        rhwd->rhwd_RemoveButton = removeButton;
        rhwd->rhwd_PriorityButton = priorityButton;

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

        set(obj, MUIA_Window_Title, MyGetWindowTitle("RESETHANDLERS", rhwd->rhwd_Title, sizeof(rhwd->rhwd_Title)));
        set(obj, MUIA_Window_ActiveObject, resetlist);
        set(removeButton, MUIA_Disabled, TRUE);
        set(priorityButton, MUIA_Disabled, TRUE);

        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(resetlist,      MUIM_Notify, MUIA_NList_Active,        MUIV_EveryTime, obj,                     1, MUIM_ResetHandlersWin_ListChange);
        DoMethod(updateButton,   MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_ResetHandlersWin_Update);
        DoMethod(printButton,    MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_ResetHandlersWin_Print);
        DoMethod(removeButton,   MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_ResetHandlersWin_Remove);
        DoMethod(priorityButton, MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_ResetHandlersWin_Priority);
        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 )
{
    struct ResetHandlersWinData *rhwd = INST_DATA(cl, obj);

    set(obj, MUIA_Window_Open, FALSE);
    DoMethod(rhwd->rhwd_ResetHandlerList, MUIM_NList_Clear);

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

static ULONG __saveds mUpdate( struct IClass *cl,
                               Object *obj,
                               Msg msg )
{
    struct ResetHandlersWinData *rhwd = INST_DATA(cl, obj);
    struct ResetCallbackUserData ud;

    ApplicationSleep(TRUE);
    set(rhwd->rhwd_ResetHandlerList, MUIA_NList_Quiet, TRUE);
    DoMethod(rhwd->rhwd_ResetHandlerList, MUIM_NList_Clear);

    ud.ud_List = rhwd->rhwd_ResetHandlerList;
    ud.ud_Count = 0;

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

    SetCountText(rhwd->rhwd_ResetHandlerCount, ud.ud_Count);
    MySetContents(rhwd->rhwd_ResetHandlerText, "");

    set(rhwd->rhwd_ResetHandlerList, MUIA_NList_Quiet, FALSE);
    set(rhwd->rhwd_RemoveButton, MUIA_Disabled, TRUE);
    set(rhwd->rhwd_PriorityButton, MUIA_Disabled, TRUE);
    ApplicationSleep(FALSE);

    return 0;
}

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

    return 0;
}

static ULONG __saveds mRemove( struct IClass *cl,
                               Object *obj,
                               Msg msg )
{
    struct ResetHandlersWinData *rhwd = INST_DATA(cl, obj);
    struct ResetHandlerEntry *rhe;

    if (rhe = (struct ResetHandlerEntry *)GetActiveEntry(rhwd->rhwd_ResetHandlerList)) {
        if (MyRequest(msgYesNo, msgWantToRemoveResetHandler, rhe->rhe_Name)) {
            MyDoCommand("RemoveResetHandler %s", rhe->rhe_Address);
            DoMethod(obj, MUIM_ResetHandlersWin_Update);
        }
    }

    return 0;
}

static ULONG __saveds mPriority( struct IClass *cl,
                                 Object *obj,
                                 Msg msg )
{
    struct ResetHandlersWinData *rhwd = INST_DATA(cl, obj);
    struct ResetHandlerEntry *rhe;

    if (rhe = (struct ResetHandlerEntry *)GetActiveEntry(rhwd->rhwd_ResetHandlerList)) {
        LONG pri;

        pri = atol(rhe->rhe_Pri);
        if (GetPriority(rhe->rhe_Name, &pri)) {
            if (MyDoCommand("SetPriority RESETHANDLER \"%s\" %ld", rhe->rhe_Name, pri)) {
                DoMethod(obj, MUIM_ResetHandlersWin_Update);
            }
        }
    }

    return 0;
}

static ULONG __saveds mListChange( struct IClass *cl,
                                   Object *obj,
                                   Msg msg )
{
    struct ResetHandlersWinData *rhwd = INST_DATA(cl, obj);
    struct ResetHandlerEntry *rhe;

    if (rhe = (struct ResetHandlerEntry *)GetActiveEntry(rhwd->rhwd_ResetHandlerList)) {
        MySetContents(rhwd->rhwd_ResetHandlerText, "%s \"%s\"", rhe->rhe_Address, rhe->rhe_Name);
        set(rhwd->rhwd_RemoveButton, MUIA_Disabled, FALSE);
        set(rhwd->rhwd_PriorityButton, MUIA_Disabled, FALSE);
    }

    return 0;
}

ULONG __asm __saveds ResetHandlersWinDispatcher( 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_ResetHandlersWin_Update:     return (mUpdate(cl, obj, (APTR)msg));
        case MUIM_ResetHandlersWin_Print:      return (mPrint(cl, obj, (APTR)msg));
        case MUIM_ResetHandlersWin_Remove:     return (mRemove(cl, obj, (APTR)msg));
        case MUIM_ResetHandlersWin_Priority:   return (mPriority(cl, obj, (APTR)msg));
        case MUIM_ResetHandlersWin_ListChange: return (mListChange(cl, obj, (APTR)msg));
    }

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

void PrintResetHandlers( char *filename )
{
    BPTR handle;

    if (handle = HandlePrintStart(filename)) {
        PrintFOneLine(handle, "\n  Address  Type       Pri    Data       Code     Name\n\n");
        IterateList(PrintCallback, (void *)handle);
    }

    HandlePrintStop();
}

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

