/**
 * 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 MUIP_FunctionsWin_ShowFunctionsMessage {
    ULONG MethodID;
    ULONG nodeType;
    APTR base;
    UBYTE *name;
};

struct FunctionsEntry {
    UBYTE fe_Address[16];
    ULONG fe_Offset;
    UBYTE fe_OffsetDec[16];
    UBYTE fe_OffsetHex[16];
    UBYTE fe_FuncName[64];
};

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

MakeHook(funclist_con2hook, funclist_con2func);

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

    return 0;
}

MakeHook(funclist_des2hook, funclist_des2func);

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

    if (fe) {
        msg->strings[0] = fe->fe_OffsetDec;
        msg->strings[1] = fe->fe_OffsetHex;
        msg->strings[2] = fe->fe_Address;
        msg->strings[3] = fe->fe_FuncName;
    } else {
        msg->strings[0] = MUIX_B "Offset";
        msg->strings[1] = MUIX_B "Hex";
        msg->strings[2] = MUIX_B "Address";
        msg->strings[3] = MUIX_B "Name";
    }

    return 0;
}

MakeHook(funclist_dsp2hook, funclist_dsp2func);

static LONG funclist_cmp2colfunc( struct FunctionsEntry *fe1,
                                  struct FunctionsEntry *fe2,
                                  ULONG column )
{
    switch (column) {
        case 0:
        case 1: return (LONG)fe1->fe_Offset - (LONG)fe2->fe_Offset;
        case 2: return stricmp(fe1->fe_Address, fe2->fe_Address);
        case 3: return stricmp(fe1->fe_FuncName, fe2->fe_FuncName);
    }
}

static __asm __saveds LONG funclist_cmp2func(register __a2 Object *obj, register __a1 struct NList_CompareMessage *msg, register __a0 struct Hook *hook)
{
    LONG cmp;
    struct FunctionsEntry *fe1, *fe2;
    ULONG col1, col2;

    fe1 = (struct FunctionsEntry *)msg->entry1;
    fe2 = (struct FunctionsEntry *)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 = funclist_cmp2colfunc(fe2, fe1, col1);
    } else {
        cmp = funclist_cmp2colfunc(fe1, fe2, col1);
    }

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

    if (msg->sort_type2 & MUIV_NList_TitleMark2_TypeMask) {
        cmp = funclist_cmp2colfunc(fe2, fe1, col2);
    } else {
        cmp = funclist_cmp2colfunc(fe1, fe2, col2);
    }

    return cmp;
}

MakeHook(funclist_cmp2hook, funclist_cmp2func);

static ULONG __saveds mNew( struct IClass *cl,
                            Object *obj,
                            struct opSet *msg )
{
    APTR funclist;

    if (obj = (Object *)DoSuperNew(cl, obj,
        MUIA_Window_ID, MakeID('.','L','F','T'),
        WindowContents, VGroup,
            Child, funclist = MyNListviewObject(MakeID('F','T','L','V'), "BAR P=" MUIX_R ",BAR P=" MUIX_R ",BAR,BAR", &funclist_con2hook, &funclist_des2hook, &funclist_dsp2hook, &funclist_cmp2hook, FALSE),
        End,
        TAG_MORE, msg->ops_AttrList))
    {
        struct FunctionsWinData *fwd = INST_DATA(cl, obj);
        APTR parent;

        fwd->fwd_FunctionsList = funclist;

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

        set(obj, MUIA_Window_ActiveObject, funclist);

        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(funclist, 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 FunctionsWinData *fwd = INST_DATA(cl, obj);

    set(obj, MUIA_Window_Open, FALSE);
    DoMethod(fwd->fwd_FunctionsList, MUIM_NList_Clear);

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

static ULONG __saveds mShowFunctions( struct IClass *cl,
                                      Object *obj,
                                      Msg msg )
{
    struct FunctionsWinData *fwd = INST_DATA(cl, obj);
    struct MUIP_FunctionsWin_ShowFunctionsMessage *sfm = (struct MUIP_FunctionsWin_ShowFunctionsMessage *)msg;

    if (sfm->nodeType != MUIV_FunctionsWin_NodeType_Resource ||
        (stricmp(sfm->name, FSRNAME) != 0 &&
         stricmp(sfm->name, CARDRESNAME) != 0 &&
         stricmp(sfm->name, KEYMAPRESNAME) != 0)) {

        struct FunctionsEntry *fe;

        if (fe = tbAllocVecPooled(globalPool, sizeof(struct FunctionsEntry))) {
            struct Library *lib = (struct Library *)sfm->base;
            ULONG max, offset;
            BOOL useIdLib = (IdentifyBase != NULL);

            max = lib->lib_NegSize;

            set(fwd->fwd_FunctionsList, MUIA_NList_Quiet, TRUE);

            for (offset = LIB_VECTSIZE; offset <= max; offset += LIB_VECTSIZE) {
                struct JumpEntry *je;

                je = (struct JumpEntry *)((UBYTE *)lib - offset);
                if (je->je_JMPInstruction == 0x4ef9) {
                    if (points2ram(je->je_JumpAddress)) {
                        _snprintf(fe->fe_Address, sizeof(fe->fe_Address), MUIX_PH "$%08lx" MUIX_PT, je->je_JumpAddress);
                    } else {
                        _snprintf(fe->fe_Address, sizeof(fe->fe_Address), "$%08lx", je->je_JumpAddress);
                    }
                } else {
                    stccpy(fe->fe_Address, txtNoJump, sizeof(fe->fe_Address));
                }

                fe->fe_Offset = offset;
                _snprintf(fe->fe_OffsetDec, sizeof(fe->fe_OffsetDec), "-%ld", offset);
                _snprintf(fe->fe_OffsetHex, sizeof(fe->fe_OffsetHex), "-%04lx", offset);

                if ((offset <= LIB_VECTSIZE * 6 && sfm->nodeType == MUIV_FunctionsWin_NodeType_Device) ||
                    (offset <= LIB_VECTSIZE * 4 && sfm->nodeType == MUIV_FunctionsWin_NodeType_Library)) {
                    UBYTE *help;

                    switch (offset) {
                        case LIB_VECTSIZE * 1: help = "Open";    break;
                        case LIB_VECTSIZE * 2: help = "Close";   break;
                        case LIB_VECTSIZE * 3: help = "Expunge"; break;
                        case LIB_VECTSIZE * 4: help = "ExtFunc"; break;
                        case LIB_VECTSIZE * 5: help = "BeginIO"; break;
                        case LIB_VECTSIZE * 6: help = "AbortIO"; break;
                        default:               help = ""; break;
                    }
                    stccpy(fe->fe_FuncName, help, sizeof(fe->fe_FuncName));
                } else if (useIdLib) {
                    ULONG error;

                    if (sfm->name) {
                        if (stricmp(sfm->name, CIAANAME) == 0 || stricmp(sfm->name, CIABNAME) == 0) {
                            error = IdFunctionTags("cia.resource", offset, IDTAG_FuncNameStr, fe->fe_FuncName,
                                                                           IDTAG_StrLength, sizeof(fe->fe_FuncName),
                                                                           TAG_DONE);
                        } else {
                            error = IdFunctionTags(sfm->name, offset, IDTAG_FuncNameStr, fe->fe_FuncName,
                                                                      IDTAG_StrLength, sizeof(fe->fe_FuncName),
                                                                      TAG_DONE);
                        }
                        switch (error) {
                            case IDERR_NOFD:
                                stccpy(fe->fe_FuncName, txtNoFDFile, sizeof(fe->fe_FuncName));
                                break;

                            case IDERR_NOMEM:
                                fe->fe_FuncName[0] = 0x00;
                                useIdLib = FALSE;
                                break;

                            case IDERR_OFFSET:
                                stccpy(fe->fe_FuncName, txtNoFunctionEntry, sizeof(fe->fe_FuncName));
                                break;
                        }
                    } else {
                        stccpy(fe->fe_FuncName, txtNoFDFile, sizeof(fe->fe_FuncName));
                    }
                } else {
                    fe->fe_FuncName[0] = 0x00;
                }

                InsertBottomEntry(fwd->fwd_FunctionsList, fe);
            }

            set(fwd->fwd_FunctionsList, MUIA_NList_Quiet, FALSE);

            set(obj, MUIA_Window_Title, MyGetChildWindowTitle("FUNCTIONS", sfm->name, fwd->fwd_Title, sizeof(fwd->fwd_Title)));
        }

        tbFreeVecPooled(globalPool, fe);
    }

    set(obj, MUIA_Window_Open, TRUE);

    return 0;
}

ULONG __asm __saveds FunctionsWinDispatcher( 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_FunctionsWin_ShowFunctions: return (mShowFunctions(cl, obj, (APTR)msg));
    }

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

