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

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

MakeHook(fontslist_con2hook, fontslist_con2func);

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

    return 0;
}

MakeHook(fontslist_des2hook, fontslist_des2func);

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

    if (fe) {
        msg->strings[0] = fe->fe_Address;
        msg->strings[1] = fe->fe_Name;
        msg->strings[2] = fe->fe_YSize;
        msg->strings[3] = fe->fe_XSize;
        msg->strings[4] = fe->fe_Style;
        msg->strings[5] = fe->fe_Flags;
        msg->strings[6] = fe->fe_Count;
        msg->strings[7] = fe->fe_LoChar;
        msg->strings[8] = fe->fe_HiChar;
        msg->strings[9] = fe->fe_Place;
    } else {
        msg->strings[0] = MUIX_B "Address";
        msg->strings[1] = MUIX_B "tf_Name";
        msg->strings[2] = MUIX_B "tf_YSize";
        msg->strings[3] = MUIX_B "tf_XSize";
        msg->strings[4] = MUIX_B "tf_Style";
        msg->strings[5] = MUIX_B "tf_Flags";
        msg->strings[6] = MUIX_B "tf_Count";
        msg->strings[7] = MUIX_B "tf_LoChar";
        msg->strings[8] = MUIX_B "tf_HiChar";
        msg->strings[9] = MUIX_B "Type";
    }

    return 0;
}

MakeHook(fontslist_dsp2hook, fontslist_dsp2func);

static LONG fontslist_cmp2colfunc( struct FontEntry *fe1,
                                   struct FontEntry *fe2,
                                   ULONG column )
{
    switch (column) {
        case 0: return stricmp(fe1->fe_Address, fe2->fe_Address);
        case 1: return stricmp(fe1->fe_Name, fe2->fe_Name);
        case 2: return stricmp(fe1->fe_YSize, fe2->fe_YSize);
        case 3: return stricmp(fe1->fe_XSize, fe2->fe_XSize);
        case 4: return stricmp(fe1->fe_Style, fe2->fe_Style);
        case 5: return stricmp(fe1->fe_Flags, fe2->fe_Flags);
        case 6: return stricmp(fe1->fe_Count, fe2->fe_Count);
        case 7: return stricmp(fe1->fe_LoChar, fe2->fe_LoChar);
        case 8: return stricmp(fe1->fe_HiChar, fe2->fe_HiChar);
        case 9: return stricmp(fe1->fe_Place, fe2->fe_Place);
    }
}

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

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

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

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

    return cmp;
}

MakeHook(fontslist_cmp2hook, fontslist_cmp2func);

void FlushFonts (void) {
    struct TextFont *font, *next;

    font = FIRSTFONT;

    Forbid();
    while (next = (struct TextFont *)font->tf_Message.mn_Node.ln_Succ) {
        if ((! font->tf_Accessors) && (font->tf_Flags & FPF_DISKFONT)) {
            RemFont (font);
            Remove ((struct Node *) font);
        }
        font = next;
    }
    Permit();
}

static void ReceiveList( void (* callback)( struct FontEntry *fe, void *userData ),
                         void *userData )
{
    struct FontEntry *fe;

    if (fe = tbAllocVecPooled(globalPool, sizeof(struct FontEntry))) {
        if (SendDaemon("GetFontList")) {
            while (ReceiveDecodedEntry((UBYTE *)fe, sizeof(struct FontEntry))) {
                callback(fe, userData);
            }
        }

        tbFreeVecPooled(globalPool, fe);
    }
}

static void IterateList( void (* callback)( struct FontEntry *fe, void *userData ),
                         void *userData )
{
    struct FontEntry *fe;

    if (fe = tbAllocVecPooled(globalPool, sizeof(struct FontEntry))) {
        struct TextFont *tf;

        tf = FIRSTFONT;
        while (tf->tf_Message.mn_Node.ln_Succ != NULL) {
            fe->fe_Addr = tf;

            _snprintf(fe->fe_Address, sizeof(fe->fe_Address), "$%08lx", tf);
            _snprintf(fe->fe_YSize, sizeof(fe->fe_YSize), "%3ld", tf->tf_YSize);
            _snprintf(fe->fe_XSize, sizeof(fe->fe_XSize), "%3ld", tf->tf_XSize);
            _snprintf(fe->fe_Style, sizeof(fe->fe_Style), "$%02lx", tf->tf_Style);
            _snprintf(fe->fe_Flags, sizeof(fe->fe_Flags), "$%02lx", tf->tf_Flags);
            _snprintf(fe->fe_LoChar, sizeof(fe->fe_LoChar), "%3ld", tf->tf_LoChar);
            _snprintf(fe->fe_HiChar, sizeof(fe->fe_HiChar), "%3ld", tf->tf_HiChar);
            _snprintf(fe->fe_Count, sizeof(fe->fe_Count), "%6lD", tf->tf_Accessors);
            if (tf->tf_Flags & FPF_ROMFONT) {
                stccpy(fe->fe_Place, "ROMFONT", sizeof(fe->fe_Place));
            } else {
                stccpy(fe->fe_Place, "DISKFONT", sizeof(fe->fe_Place));
            }
            stccpy(fe->fe_Name, tf->tf_Message.mn_Node.ln_Name, sizeof(fe->fe_Name));

            callback(fe, userData);

            tf = (struct TextFont *)tf->tf_Message.mn_Node.ln_Succ;
        }

        tbFreeVecPooled(globalPool, fe);
    }
}

static void UpdateCallback( struct FontEntry *fe,
                            void *userData )
{
    struct FontCallbackUserData *ud = (struct FontCallbackUserData *)userData;

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

static void PrintCallback( struct FontEntry *fe,
                           void *userData )
{
    PrintFOneLine((BPTR)userData, " %s  %3s   %3s  %s  %s %3s    %3s    %3s  %-8s %s\n", fe->fe_Address, fe->fe_YSize, fe->fe_XSize, fe->fe_Style, fe->fe_Flags, fe->fe_Count, fe->fe_LoChar, fe->fe_HiChar, fe->fe_Place, fe->fe_Name);
}

static void SendCallback( struct FontEntry *fe,
                          void *userData )
{
    SendEncodedEntry((UBYTE *)fe, sizeof(struct FontEntry));
}

static ULONG __saveds mNew( struct IClass *cl,
                            Object *obj,
                            struct opSet *msg )
{
    APTR fontslist, fontstext, fontscount, updateButton, printButton, closeButton, removeButton, moreButton, exitButton;

    if (obj = (Object *)DoSuperNew(cl, obj,
        MUIA_HelpNode, FontsText,
        MUIA_Window_ID, MakeID('F','O','N','T'),
        WindowContents, VGroup,

            Child, fontslist = MyNListviewObject(MakeID('F','O','L','V'), "BAR,BAR,BAR P=" MUIX_R ",BAR P=" MUIX_R ",BAR P=" MUIX_C ",BAR P=" MUIX_C ",BAR P=" MUIX_R ",BAR P=" MUIX_R ",BAR P=" MUIX_R ",BAR", &fontslist_con2hook, &fontslist_des2hook, &fontslist_dsp2hook, &fontslist_cmp2hook, TRUE),
            Child, MyBelowListview(&fontstext, &fontscount),

            Child, MyVSpace(4),

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

        fwd->fwd_FontList = fontslist;
        fwd->fwd_FontText = fontstext;
        fwd->fwd_FontCount = fontscount;
        fwd->fwd_CloseButton = closeButton;
        fwd->fwd_RemoveButton = removeButton;
        fwd->fwd_MoreButton = moreButton;

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

        set(obj, MUIA_Window_Title, MyGetWindowTitle("FONTS", fwd->fwd_Title, sizeof(fwd->fwd_Title)));
        set(obj, MUIA_Window_ActiveObject, fontslist);
        set(closeButton, MUIA_Disabled, TRUE);
        set(removeButton, MUIA_Disabled, TRUE);
        set(moreButton, 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(fontslist,       MUIM_Notify, MUIA_NList_Active,        MUIV_EveryTime, obj,                     1, MUIM_FontsWin_ListChange);
        DoMethod(fontslist,       MUIM_Notify, MUIA_NList_DoubleClick,   MUIV_EveryTime, obj,                     1, MUIM_FontsWin_More);
        DoMethod(updateButton,    MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_FontsWin_Update);
        DoMethod(printButton,     MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_FontsWin_Print);
        DoMethod(closeButton,     MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_FontsWin_Close);
        DoMethod(removeButton,    MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_FontsWin_Remove);
        DoMethod(moreButton,      MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     1, MUIM_FontsWin_More);
        DoMethod(exitButton,      MUIM_Notify, MUIA_Pressed,             FALSE,          obj,                     3, MUIM_Set, MUIA_Window_CloseRequest, TRUE);
        DoMethod(fontslist,       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 FontsWinData *fwd = INST_DATA(cl, obj);

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

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

static ULONG __saveds mUpdate( struct IClass *cl,
                               Object *obj,
                               Msg msg )
{
    struct FontsWinData *fwd = INST_DATA(cl, obj);
    struct FontCallbackUserData ud;

    ApplicationSleep(TRUE);
    set(fwd->fwd_FontList, MUIA_NList_Quiet, TRUE);
    DoMethod(fwd->fwd_FontList, MUIM_NList_Clear);

    ud.ud_List = fwd->fwd_FontList;
    ud.ud_Count = 0;

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

    SetCountText(fwd->fwd_FontCount, ud.ud_Count);
    MySetContents(fwd->fwd_FontText, "");

    set(fwd->fwd_FontList, MUIA_NList_Quiet, FALSE);
    set(fwd->fwd_FontList, MUIA_NList_Active, MUIV_NList_Active_Off);
    set(fwd->fwd_CloseButton, MUIA_Disabled, TRUE);
    set(fwd->fwd_RemoveButton, MUIA_Disabled, TRUE);
    set(fwd->fwd_MoreButton, MUIA_Disabled, TRUE);
    ApplicationSleep(FALSE);

    return 0;
}

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

    return 0;
}

static ULONG __saveds mClose( struct IClass *cl,
                              Object *obj,
                              Msg msg )
{
    struct FontsWinData *fwd = INST_DATA(cl, obj);
    struct FontEntry *fe;

    if (fe = (struct FontEntry *)GetActiveEntry(fwd->fwd_FontList)) {
        LONG cnt;

        if (IsDec(fe->fe_Count, &cnt) && cnt > 0) {
            ULONG answer;

            if (answer = MyRequest(msgOnceAllCancel, msgWantToCloseFont, fe->fe_Name)) {
                if (answer == 1) {
                    MyDoCommand("CloseFont %s", fe->fe_Address);
                    cnt--;
                } else if (answer == 2) {
                    while (cnt > 0) {
                        if (!MyDoCommand("CloseFont $%08lx", fe->fe_Addr)) break;
                        cnt--;
                    }
                }

                _snprintf(fe->fe_Count, sizeof(fe->fe_Count), "%6lD", cnt);
                RedrawActiveEntry(fwd->fwd_FontList);
            }
        } else {
            MyRequest(msgErrorContinue, msgAccessorCountIsZero);
        }
    }

    return 0;
}

static ULONG __saveds mRemove( struct IClass *cl,
                               Object *obj,
                               Msg msg )
{
    struct FontsWinData *fwd = INST_DATA(cl, obj);
    struct FontEntry *fe;

    if (fe = (struct FontEntry *)GetActiveEntry(fwd->fwd_FontList)) {
        if (fe->fe_Addr->tf_Accessors > 0) {
            if (MyRequest(msgYesNo, msgWantToRemoveFont, fe->fe_Name)) {
                MyDoCommand("RemoveFont %s", fe->fe_Address);
                DoMethod(obj, MUIM_FontsWin_Update);
            }
        } else {
            MyRequest(msgErrorContinue, msgAccessorCountIsNotZero);
        }
    }

    return 0;
}

static ULONG __saveds mMore( struct IClass *cl,
                             Object *obj,
                             Msg msg )
{
    struct FontsWinData *fwd = INST_DATA(cl, obj);
    struct FontEntry *fe;

    if (fe = (struct FontEntry *)GetActiveEntry(fwd->fwd_FontList)) {
        APTR detailWin;

        if (detailWin = FontsDetailWindowObject,
                MUIA_Window_ParentWindow, obj,
            End) {
            set(detailWin, MUIA_FontsDetailWin_Font, fe);
            set(detailWin, MUIA_Window_Open, TRUE);
        }
    }

    return 0;
}

static ULONG __saveds mListChange( struct IClass *cl,
                                   Object *obj,
                                   Msg msg )
{
    struct FontsWinData *fwd = INST_DATA(cl, obj);
    struct FontEntry *fe;

    if (fe = (struct FontEntry *)GetActiveEntry(fwd->fwd_FontList)) {
        MySetContents(fwd->fwd_FontText, "%s \"%s\"", fe->fe_Address, fe->fe_Name);
        set(fwd->fwd_CloseButton, MUIA_Disabled, FALSE);
        set(fwd->fwd_RemoveButton, MUIA_Disabled, FALSE);
        if (!clientstate) set(fwd->fwd_MoreButton, MUIA_Disabled, FALSE);
    }

    return 0;
}

ULONG __asm __saveds FontsWinDispatcher( 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_FontsWin_Update:     return (mUpdate(cl, obj, (APTR)msg));
        case MUIM_FontsWin_Print:      return (mPrint(cl, obj, (APTR)msg));
        case MUIM_FontsWin_Close:      return (mClose(cl, obj, (APTR)msg));
        case MUIM_FontsWin_Remove:     return (mRemove(cl, obj, (APTR)msg));
        case MUIM_FontsWin_More:       return (mMore(cl, obj, (APTR)msg));
        case MUIM_FontsWin_ListChange: return (mListChange(cl, obj, (APTR)msg));
    }

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

void PrintFonts( char *filename )
{
    BPTR handle;

    if (handle = HandlePrintStart(filename)) {
        PrintFOneLine(handle, "\n  Address  YSize XSize Style Flags  Count LoChar HiChar  Type     Name\n\n");
        IterateList(PrintCallback, (void *)handle);
    }

    HandlePrintStop();
}

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

