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

struct MUIP_CommoditiesWin_CxCommand {
    ULONG method;
    UBYTE *command;
};

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

MakeHook(cxlist_con2hook, cxlist_con2func);

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

    return 0;
}

MakeHook(cxlist_des2hook, cxlist_des2func);

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

    if (cxe) {
        msg->strings[0] = cxe->cxe_Address;
        msg->strings[1] = cxe->cxe_Name;
        msg->strings[2] = cxe->cxe_Type;
        msg->strings[3] = cxe->cxe_Pri;
        msg->strings[4] = cxe->cxe_Flags;
        msg->strings[5] = cxe->cxe_Port;
    } else {
        msg->strings[0] = MUIX_B "Address";
        msg->strings[1] = MUIX_B "Name";
        msg->strings[2] = MUIX_B "ln_Type";
        msg->strings[3] = MUIX_B "ln_Pri";
        msg->strings[4] = MUIX_B "Flags";
        msg->strings[5] = MUIX_B "Port";
    }

    return 0;
}

MakeHook(cxlist_dsp2hook, cxlist_dsp2func);

static LONG cxlist_cmp2colfunc( struct CxEntry *cxe1,
                                struct CxEntry *cxe2,
                                ULONG column )
{
    LONG pri1, pri2;

    switch (column) {
        case 0: return stricmp(cxe1->cxe_Address, cxe2->cxe_Address);
        case 1: return stricmp(cxe1->cxe_Name, cxe2->cxe_Name);
        case 2: return stricmp(cxe1->cxe_Type, cxe2->cxe_Type);
        case 3: IsDec(cxe1->cxe_Pri, &pri1); IsDec(cxe2->cxe_Pri, &pri2); return pri2 - pri1;
        case 4: return stricmp(cxe1->cxe_Flags, cxe2->cxe_Flags);
        case 5: return stricmp(cxe1->cxe_Port, cxe2->cxe_Port);
    }
}

static __asm __saveds LONG cxlist_cmp2func(register __a2 Object *obj, register __a1 struct NList_CompareMessage *msg, register __a0 struct Hook *hook)
{
    LONG cmp;
    struct CxEntry *cxe1, *cxe2;
    ULONG col1, col2;

    cxe1 = (struct CxEntry *)msg->entry1;
    cxe2 = (struct CxEntry *)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 = cxlist_cmp2colfunc(cxe2, cxe1, col1);
    } else {
        cmp = cxlist_cmp2colfunc(cxe1, cxe2, col1);
    }

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

    if (msg->sort_type2 & MUIV_NList_TitleMark2_TypeMask) {
        cmp = cxlist_cmp2colfunc(cxe2, cxe1, col2);
    } else {
        cmp = cxlist_cmp2colfunc(cxe1, cxe2, col2);
    }

    return cmp;
}

MakeHook(cxlist_cmp2hook, cxlist_cmp2func);

UBYTE *GetCxNodeType( UBYTE type )
{
    static UBYTE *CxNodeTypeText[] = {
        "Invalid",
        "Filter",
        "Typefilter",
        "Send",
        "Signal",
        "Translate",
        "Broker",
        "Debug",
        "Custom",
        "Zero",
        "< ??? >"
    };

    if (type > CX_ZERO) {
        return "< ??? >";
    } else {
        return CxNodeTypeText[type];
    }
}

static void ReceiveList( void (* callback)( struct CxEntry *cxe, void *userData ),
                         void *userData )
{
    struct CxEntry *cxe;

    if (cxe = tbAllocVecPooled(globalPool, sizeof(struct CxEntry))) {
        if (SendDaemon("GetCxList")) {
            while (ReceiveDecodedEntry((UBYTE *)cxe, sizeof(struct CxEntry))) {
                callback(cxe, userData);
            }
        }

        tbFreeVecPooled(globalPool, cxe);
    }
}

static void IterateList( void (* callback)( struct CxEntry *cxe, void *userData ),
                         void *userData )
{
    struct CxEntry *cxe;

    if (cxe = tbAllocVecPooled(globalPool, sizeof(struct CxEntry))) {
        struct MsgPort *cxport;

        if (cxport = CreateMsgPort()) {
            struct NewBroker cxnewbroker = { NB_VERSION, "« Scout Dummy »", "« Scout Dummy »", "Dummy Broker", 0, 0, 0, NULL, 0 };
            struct PrivateCxObj *cxbroker;

            cxnewbroker.nb_Port = cxport;

            if (cxbroker = (struct PrivateCxObj *)CxBroker(&cxnewbroker, NULL)) {
                struct Node *broker;

                Forbid();

                for (broker= &cxbroker->mco_Node; broker->ln_Pred; broker = broker->ln_Pred);

                for (broker = broker->ln_Succ; broker->ln_Succ; broker = broker->ln_Succ) {
                    struct PrivateCxObj *mco = (struct PrivateCxObj *)broker;

                    if (mco == cxbroker) continue;

                    cxe->cxe_Addr = mco;
                    _snprintf(cxe->cxe_Address, sizeof(cxe->cxe_Address), "$%08lx", mco);
                    stccpy(cxe->cxe_Type, GetCxNodeType(CxObjType((CxObj *)mco)), sizeof(cxe->cxe_Type));
                    _snprintf(cxe->cxe_Pri, sizeof(cxe->cxe_Pri), "%4ld", mco->mco_Node.ln_Pri);
                    stccpy(cxe->cxe_Name, mco->mco_Name, sizeof(cxe->cxe_Name));
                    stccpy(cxe->cxe_Title, mco->mco_Title, sizeof(cxe->cxe_Title));
                    stccpy(cxe->cxe_Description, mco->mco_Descr, sizeof(cxe->cxe_Description));
                    _snprintf(cxe->cxe_Task, sizeof(cxe->cxe_Task), "$%08lx", mco->mco_Task);
                    _snprintf(cxe->cxe_Port, sizeof(cxe->cxe_Port), "$%08lx", mco->mco_Port);
                    _snprintf(cxe->cxe_Unique, sizeof(cxe->cxe_Unique), "$%08lx", mco->mco_dummy3);
                    cxe->cxe_FlagsInt = mco->mco_Flags;
                    _snprintf(cxe->cxe_Flags, sizeof(cxe->cxe_Flags), "$%02lx", mco->mco_Flags);

                    callback(cxe, userData);
                }

                Permit();

                DeleteCxObjAll((CxObj *)cxbroker);
            }

            DeleteMsgPort(cxport);
        }

        tbFreeVecPooled(globalPool, cxe);
    }
}

static void UpdateCallback( struct CxEntry *cxe,
                            void *userData )
{
    struct CxCallbackUserData *ud = (struct CxCallbackUserData *)userData;

    InsertSortedEntry(ud->ud_List, cxe);
    ud->ud_Count++;
}

static void PrintCallback( struct CxEntry *cxe,
                           void *userData )
{
    PrintFOneLine((BPTR)userData, " %s %-7.7s %3s %3s   %s %s\n", cxe->cxe_Address, cxe->cxe_Type, cxe->cxe_Pri, cxe->cxe_Flags, cxe->cxe_Port, cxe->cxe_Name);
}

static void SendCallback( struct CxEntry *cxe,
                          void *userData )
{
    SendEncodedEntry((UBYTE *)cxe, sizeof(struct CxEntry));
}

static ULONG __saveds mNew( struct IClass *cl,
                            Object *obj,
                            struct opSet *msg )
{
    APTR cxlist, cxtext, cxcount, appearButton, disappearButton, enableButton, disableButton, killButton, listChgButton, uniqueButton, updateButton, printButton, removeButton, priorityButton, moreButton, exitButton;

    if (obj = (Object *)DoSuperNew(cl, obj,
        MUIA_HelpNode, CommoditiesText,
        MUIA_Window_ID, MakeID('C','D','I','T'),
        WindowContents, VGroup,

            Child, cxlist = MyNListviewObject(MakeID('C','D','L','V'), "BAR,BAR,BAR P=" MUIX_C ",BAR P=" MUIX_R ",BAR P="MUIX_C ",BAR", &cxlist_con2hook, &cxlist_des2hook, &cxlist_dsp2hook, &cxlist_cmp2hook, TRUE),
            Child, MyBelowListview(&cxtext, &cxcount),

            Child, MyVSpace(4),

            Child, HGroup, MUIA_Group_SameSize, TRUE,
                Child, appearButton    = MakeButton(txtAppear),
                Child, disappearButton = MakeButton(txtDisappear),
                Child, enableButton    = MakeButton(txtEnable),
                Child, disableButton   = MakeButton(txtDisable),
                Child, killButton      = MakeButton(txtKill),
                Child, listChgButton   = MakeButton(txtListChange),
                Child, uniqueButton    = MakeButton(txtUnique),
            End,

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

        cwd->cwd_CxList = cxlist;
        cwd->cwd_CxText = cxtext;
        cwd->cwd_CxCount = cxcount;
        cwd->cwd_RemoveButton = removeButton;
        cwd->cwd_PriorityButton = priorityButton;
        cwd->cwd_MoreButton = moreButton;
        cwd->cwd_AppearButton = appearButton;
        cwd->cwd_DisappearButton = disappearButton;
        cwd->cwd_EnableButton = enableButton;
        cwd->cwd_DisableButton = disableButton;
        cwd->cwd_KillButton = killButton;
        cwd->cwd_ListChgButton = listChgButton;
        cwd->cwd_UniqueButton = uniqueButton;

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

        set(obj, MUIA_Window_Title, MyGetWindowTitle("COMMODITIES", cwd->cwd_Title, sizeof(cwd->cwd_Title)));
        set(obj, MUIA_Window_ActiveObject, cxlist);
        set(removeButton, MUIA_Disabled, TRUE);
        set(priorityButton, MUIA_Disabled, TRUE);
        set(moreButton, MUIA_Disabled, TRUE);
        set(appearButton, MUIA_Disabled, TRUE);
        set(disappearButton, MUIA_Disabled, TRUE);
        set(enableButton, MUIA_Disabled, TRUE);
        set(disableButton, MUIA_Disabled, TRUE);
        set(killButton, MUIA_Disabled, TRUE);
        set(listChgButton, MUIA_Disabled, TRUE);
        set(uniqueButton, 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(cxlist,          MUIM_Notify, MUIA_NList_Active,        MUIV_EveryTime, obj,                     1, MUIM_CommoditiesWin_ListChange);
        DoMethod(cxlist,          MUIM_Notify, MUIA_NList_DoubleClick,   MUIV_EveryTime, obj,                     1, MUIM_CommoditiesWin_More);
        DoMethod(updateButton,    MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_CommoditiesWin_Update);
        DoMethod(printButton,     MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_CommoditiesWin_Print);
        DoMethod(removeButton,    MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_CommoditiesWin_Remove);
        DoMethod(moreButton,      MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_CommoditiesWin_More);
        DoMethod(priorityButton,  MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_CommoditiesWin_Priority);
        DoMethod(exitButton,      MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     3, MUIM_Set, MUIA_Window_CloseRequest, TRUE);
        DoMethod(appearButton,    MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     2, MUIM_CommoditiesWin_CxCommand, "Appear");
        DoMethod(disappearButton, MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     2, MUIM_CommoditiesWin_CxCommand, "Disappear");
        DoMethod(enableButton,    MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     2, MUIM_CommoditiesWin_CxCommand, "Enable");
        DoMethod(disableButton,   MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     2, MUIM_CommoditiesWin_CxCommand, "Disable");
        DoMethod(killButton,      MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     2, MUIM_CommoditiesWin_CxCommand, "Kill");
        DoMethod(listChgButton,   MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     2, MUIM_CommoditiesWin_CxCommand, "ListChg");
        DoMethod(uniqueButton,    MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     2, MUIM_CommoditiesWin_CxCommand, "Unique");
        DoMethod(cxlist,          MUIM_NList_Sort3, MUIV_NList_Sort3_SortType_1, MUIV_NList_SortTypeAdd_None, MUIV_NList_Sort3_SortType_Both);
    }

    return (ULONG)obj;
}

static ULONG __saveds mDispose( struct IClass *cl,
                                Object *obj,
                                struct opSet *msg )
{
    struct CommoditiesWinData *cwd = INST_DATA(cl, obj);

    set(obj, MUIA_Window_Open, FALSE);
    DoMethod(cwd->cwd_CxList, MUIM_NList_Clear);

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

static ULONG __saveds mUpdate( struct IClass *cl,
                               Object *obj,
                               Msg msg )
{
    struct CommoditiesWinData *cwd = INST_DATA(cl, obj);
    struct CxCallbackUserData ud;

    ApplicationSleep(TRUE);
    set(cwd->cwd_CxList, MUIA_NList_Quiet, TRUE);
    DoMethod(cwd->cwd_CxList, MUIM_NList_Clear);

    ud.ud_List = cwd->cwd_CxList;
    ud.ud_Count = 0;

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

    SetCountText(cwd->cwd_CxCount, ud.ud_Count);
    MySetContents(cwd->cwd_CxText, "");

    set(cwd->cwd_CxList, MUIA_NList_Quiet, FALSE);
    set(cwd->cwd_PriorityButton, MUIA_Disabled, TRUE);
    set(cwd->cwd_RemoveButton, MUIA_Disabled, TRUE);
    set(cwd->cwd_MoreButton, MUIA_Disabled, TRUE);
    set(cwd->cwd_AppearButton, MUIA_Disabled, TRUE);
    set(cwd->cwd_DisappearButton, MUIA_Disabled, TRUE);
    set(cwd->cwd_EnableButton, MUIA_Disabled, TRUE);
    set(cwd->cwd_DisableButton, MUIA_Disabled, TRUE);
    set(cwd->cwd_KillButton, MUIA_Disabled, TRUE);
    set(cwd->cwd_ListChgButton, MUIA_Disabled, TRUE);
    set(cwd->cwd_UniqueButton, MUIA_Disabled, TRUE);
    ApplicationSleep(FALSE);

    return 0;
}

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

    return 0;
}

static ULONG __saveds mRemove( struct IClass *cl,
                               Object *obj,
                               Msg msg )
{
    struct CommoditiesWinData *cwd = INST_DATA(cl, obj);
    struct CxEntry *cxe;

    if (cxe = (struct CxEntry *)GetActiveEntry(cwd->cwd_CxList)) {
        if (MyRequest(msgYesNo, msgWantToRemoveCommoditiy, cxe->cxe_Name)) {
            MyDoCommand("RemoveCx %s", cxe->cxe_Address);
            DoMethod(obj, MUIM_CommoditiesWin_Update);
        }
    }

    return 0;
}

static ULONG __saveds mPriority( struct IClass *cl,
                                 Object *obj,
                                 Msg msg )
{
    struct CommoditiesWinData *cwd = INST_DATA(cl, obj);
    struct CxEntry *cxe;

    if (cxe = (struct CxEntry *)GetActiveEntry(cwd->cwd_CxList)) {
        LONG pri;

        pri = atol(cxe->cxe_Pri);
        if (GetPriority(cxe->cxe_Name, &pri)) {
            if (MyDoCommand("SetCxPri %s %ld", cxe->cxe_Address, pri)) {
                _snprintf(cxe->cxe_Pri, sizeof(cxe->cxe_Pri), "%4ld", pri);
                RedrawActiveEntry(cwd->cwd_CxList);
            }
        }
    }

    return 0;
}

static ULONG __saveds mMore( struct IClass *cl,
                             Object *obj,
                             Msg msg )
{
    struct CommoditiesWinData *cwd = INST_DATA(cl, obj);
    struct CxEntry *cxe;

    if (cxe = (struct CxEntry *)GetActiveEntry(cwd->cwd_CxList)) {
        APTR detailWin;

        if (detailWin = CommoditiesDetailWindowObject,
                MUIA_Window_ParentWindow, obj,
                MUIA_Window_MaxChildWindowCount, (opts.SingleWindows) ? 1 : 0,
            End) {
            set(detailWin, MUIA_CommoditiesDetailWin_Commodity, cxe);
            set(detailWin, MUIA_Window_Open, TRUE);
        }
    }

    return 0;
}

static ULONG __saveds mListChange( struct IClass *cl,
                                   Object *obj,
                                   Msg msg )
{
    struct CommoditiesWinData *cwd = INST_DATA(cl, obj);
    struct CxEntry *cxe;

    if (cxe = (struct CxEntry *)GetActiveEntry(cwd->cwd_CxList)) {
        MySetContents(cwd->cwd_CxText, "%s \"%s\"", cxe->cxe_Address, cxe->cxe_Name);
        set(cwd->cwd_PriorityButton, MUIA_Disabled, FALSE);
        set(cwd->cwd_RemoveButton, MUIA_Disabled, FALSE);
        if (!clientstate) set(cwd->cwd_MoreButton, MUIA_Disabled, FALSE);
        set(cwd->cwd_AppearButton, MUIA_Disabled, !(cxe->cxe_FlagsInt & COF_SHOW_HIDE));
        set(cwd->cwd_DisappearButton, MUIA_Disabled, !(cxe->cxe_FlagsInt & COF_SHOW_HIDE));
        set(cwd->cwd_EnableButton, MUIA_Disabled, FALSE);
        set(cwd->cwd_DisableButton, MUIA_Disabled, FALSE);
        set(cwd->cwd_KillButton, MUIA_Disabled, FALSE);
        set(cwd->cwd_ListChgButton, MUIA_Disabled, FALSE);
        set(cwd->cwd_UniqueButton, MUIA_Disabled, FALSE);
    }

    return 0;
}

static ULONG __saveds mCxCommand( struct IClass *cl,
                                  Object *obj,
                                  Msg msg )
{
    struct CommoditiesWinData *cwd = INST_DATA(cl, obj);
    struct CxEntry *cxe;

    if (cxe = (struct CxEntry *)GetActiveEntry(cwd->cwd_CxList)) {
        MyDoCommand("Cx%s \"%s\"", ((struct MUIP_CommoditiesWin_CxCommand *)msg)->command, cxe->cxe_Name);
        Delay(25);
        DoMethod(obj, MUIM_CommoditiesWin_Update);
    }

    return 0;
}

ULONG __asm __saveds CommoditiesWinDispatcher( 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_CommoditiesWin_Update:     return (mUpdate(cl, obj, (APTR)msg));
        case MUIM_CommoditiesWin_Print:      return (mPrint(cl, obj, (APTR)msg));
        case MUIM_CommoditiesWin_Remove:     return (mRemove(cl, obj, (APTR)msg));
        case MUIM_CommoditiesWin_Priority:   return (mPriority(cl, obj, (APTR)msg));
        case MUIM_CommoditiesWin_More:       return (mMore(cl, obj, (APTR)msg));
        case MUIM_CommoditiesWin_ListChange: return (mListChange(cl, obj, (APTR)msg));
        case MUIM_CommoditiesWin_CxCommand:  return (mCxCommand(cl, obj, (APTR)msg));
    }

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

void PrintCx( char *filename )
{
    BPTR handle;

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

    HandlePrintStop();
}

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

