#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <libraries/gadtools.h>
#include <libraries/asl.h>
#include <workbench/icon.h>
#include <workbench/workbench.h>
#include <dos/dosextens.h>
#include <dos/dosasl.h>

#include <stdlib.h>
#include "amigan.h"

MODULE void iconconvert(ABOOL gui);
MODULE void icon_eachwildcard(STRPTR subpattern);
MODULE void icon_wildcard(STRPTR pattern);
MODULE void iconasl(void);

MODULE struct Gadget *MX121_Type        = NULL,
                     *TE121_Type        = NULL,
                     *ST121_Pathname    = NULL,
                     *BU121_PathnameASL = NULL,
                     *CB121_Optimize    = NULL,
                     *CB121_Planar      = NULL,
                     *CB121_NewIcon     = NULL,
                     *TE121_Preserve    = NULL;
MODULE ABOOL               stop;

// from rp.c
IMPORT ABOOL               fillwindows;
IMPORT SBYTE               page;
IMPORT SWORD               y;
IMPORT TEXT                asldir[VLONGFIELD + 1];
IMPORT ULONG               fillcolour;
IMPORT struct NewGadget*   Gadget;
IMPORT struct Gadget      *BU99_Left,
                          *BU99_Right,
                          *PrevGadPtr;
IMPORT struct Window*      MainWindowPtr;
IMPORT struct Screen*      ScreenPtr;
IMPORT struct SharedStruct shared;

AGLOBAL struct IconBase*   IconBase = NULL;

AGLOBAL struct IconStruct  icon =
{   TRUE, FALSE, FALSE, WBTOOL - 1, "", "", "", ""
};

MODULE STRPTR IconOptions[9] =
{   "Disk",
    "Drawer",
    "Tool",
    "Project",
    "Trashcan",
    "Device",
    "Kickstart",
    "AppIcon",
    NULL
};
/* icon.type is 1 less than the actual value used by the system (those
start from 1, ours start from 0). */

AGLOBAL void icon1(void)
{   verynewwindow
    (   ICON1WIDTH, ICON1HEIGHT,
        "Report+: Icon Processor",
        BUTTONIDCMP | STRINGIDCMP | MXIDCMP
    );

    setgadget(250, 34, 100, 12, "Icon _Type:", PLACETEXT_ABOVE);
    TE121_Type = PrevGadPtr = (struct Gadget *) CreateGadget
    (   TEXT_KIND,
        PrevGadPtr,
        &Gadget,
        GT_Underscore, '_',
        TAG_DONE
    );
    setgadget(250, 40, 0, 0, NULL, PLACETEXT_RIGHT);
    MX121_Type = PrevGadPtr = (struct Gadget *) CreateGadget
    (   MX_KIND,
        PrevGadPtr,
        &Gadget,
        GTMX_Labels, &IconOptions,
        GTMX_Active, icon.type,
        TAG_DONE
    );

    /* pathname(s) */
    setgadget(132, 128, 240, 12, "_Pathname(s):", NULL);
    ST121_Pathname = PrevGadPtr = (struct Gadget *) CreateGadget
    (   STRING_KIND,
        PrevGadPtr,
        &Gadget,
        GTST_String, &(icon.pathname),
        GTST_MaxChars, LONGESTFIELD,
        GA_TabCycle, TRUE,
        GT_Underscore, '_',
        TAG_DONE
    );
    /* pathname... */
    setgadget(374, 128, 28, 12, "_...", NULL);
    BU121_PathnameASL = PrevGadPtr = (struct Gadget *) CreateGadget
    (   BUTTON_KIND,
        PrevGadPtr,
        &Gadget,
        GT_Underscore, '_',
        TAG_DONE
    );

    // 'convert tabs to spaces?'
    setgadget(180, 30, 0, 0, "_Optimize colours?", NULL);
    CB121_Optimize = PrevGadPtr = (struct Gadget *) CreateGadget
    (   CHECKBOX_KIND,
        PrevGadPtr,
        &Gadget,
        GTCB_Checked, icon.optimize,
        GT_Underscore, '_',
        TAG_DONE
    );

    setgadget(108, 84, 100, 12, "Preserve data:", PLACETEXT_ABOVE);
    TE121_Preserve = PrevGadPtr = (struct Gadget *) CreateGadget
    (   TEXT_KIND,
        PrevGadPtr,
        &Gadget,
        GT_Underscore, '_',
        TAG_DONE
    );

    setgadget(180, 90, 0, 0, "P_lanar?", NULL);
    CB121_Planar = PrevGadPtr = (struct Gadget *) CreateGadget
    (   CHECKBOX_KIND,
        PrevGadPtr,
        &Gadget,
        GTCB_Checked, icon.planar,
        GT_Underscore, '_',
        TAG_DONE
    );
    setgadget(180, 102, 0, 0, "_NewIcon?", NULL);
    CB121_NewIcon = PrevGadPtr = (struct Gadget *) CreateGadget
    (   CHECKBOX_KIND,
        PrevGadPtr,
        &Gadget,
        GTCB_Checked, icon.newicon,
        GT_Underscore, '_',
        TAG_DONE
    );

    /* There are 20 gadgets:
       BU99_Right        1
       BU99_Left         1
       TE121_Type        1
       MX121_Dest        8
       ST121_Pathname    2
       BU121_PathnameASL 1
       CB121_Optimize    2
       CB121_Planar      2
       CB121_NewIcon     2 */
    drawgadgets(20);

    SetAPen(MainWindowPtr->RPort, WHITE);
    ActivateGadget(ST121_Pathname, MainWindowPtr, NULL);
    loop();
    strcpy
    (   icon.pathname,
        ((struct StringInfo *) ST121_Pathname->SpecialInfo)->Buffer
    );
    closewindow();
}

AGLOBAL void icon2(void)
{   verynewwindow
    (   ICON2WIDTH, ICON2HEIGHT,
        "Report+: Icon Processor",
        BUTTONIDCMP | STRINGIDCMP
    );
    drawgadgets(2);

    GT_SetGadgetAttrs // the `stop' button
    (   BU99_Left,
        MainWindowPtr, NULL,
        GA_Disabled, FALSE,
        GT_Underscore, '_',
        TAG_DONE
    );
    GT_SetGadgetAttrs // the `menu' button
    (   BU99_Right,
        MainWindowPtr, NULL,
        GA_Disabled, TRUE,
        GT_Underscore, '_',
        TAG_DONE
    );

    if (!(IconBase = (struct IconBase *) OpenLibrary("icon.library", 44L)))
    {   rq("Need icon.library V44+!\n");
        cleanexit(EXIT_FAILURE);
    }

    icon_do(TRUE);
    y = 46;
    if (!stop)
    {   say("All done.", WHITE);
    }

    GT_SetGadgetAttrs // the `stop' button
    (   BU99_Left,
        MainWindowPtr, NULL,
        GA_Disabled, TRUE,
        TAG_DONE
    );
    GT_SetGadgetAttrs // the `next' button
    (   BU99_Right,
        MainWindowPtr,
        NULL,
        GA_Disabled, FALSE,
        TAG_DONE
    );

    loop();
    closewindow();
    if (IconBase)
    {   CloseLibrary(IconBase);
        IconBase = NULL;
}   }

MODULE void iconconvert(ABOOL gui)
{   TEXT                 temp[LONGFIELD + 1];
    ULONG                length = strlen(icon.thisfile), class, signals;
    struct DiskObject*   IconPtr;
    struct Gadget*       addr;
    struct IntuiMessage* MsgPtr;

    if (!gui)
    {   if (!(IconBase = (struct IconBase *) OpenLibrary("icon.library", 44L)))
        {   Printf("Report+: Need icon.library V44+!\n");
            cleanexit(EXIT_FAILURE);
    }   }

    if (length <= 5) // in case we get passed an empty string
    {   return;
    }
    if (!stricmp(&icon.thisfile[length - 5], ".info"))
    {   icon.thisfile[length - 5] = 0;
    }

    strcpy(temp, "Processing ");
    strcat(temp, icon.thisfile);
    strcat(temp, ".info...");
    if (gui)
    {   getfillcolour();
        SetAPen(MainWindowPtr->RPort, fillcolour);
        RectFill(MainWindowPtr->RPort, 8, 22, ICON2WIDTH - 8, 32); /* clear the text area */
        y = 30;
        say(temp, WHITE);
    } else
    {   Printf("%s", temp);
        Flush(Output());
        temp[0] = 0;
    }

    if (IconPtr = GetIconTags(icon.thisfile, TAG_END))
    {   IconPtr->do_Type = icon.type + 1;
        if (PutIconTags
        (   icon.thisfile, IconPtr,
            ICONPUTA_OptimizeImageSpace,   (BOOL) icon.optimize,
            ICONPUTA_DropPlanarIconImage,  (BOOL) (!icon.planar),
            ICONPUTA_DropNewIconToolTypes, (BOOL) (!icon.newicon),
            TAG_END
        ))
        {   strcat(temp, "done.");
        } else strcat(temp, "failed to write!");
    } else strcat(temp, "failed to read!");

    if (gui)
    {   getfillcolour();
        SetAPen(MainWindowPtr->RPort, fillcolour);
        RectFill(MainWindowPtr->RPort, 8, 22, EOL2WIDTH - 8, 32); /* clear the text area */
        y = 30;
        say(temp, WHITE);
    } else
    {   Printf("%s\n", temp);
    }

    signals = SetSignal(0L, 0L);
    if (signals & SIGBREAKF_CTRL_C)
    {   stop = TRUE;
        SetSignal(0L, SIGBREAKF_CTRL_C); // clear the Ctrl-C signal
        strcpy(temp, "Aborted by user!");
        if (gui)
        {   say(temp, WHITE);
        } else
        {   Printf("%s\n", temp);
            Flush(Output());
    }   }

    while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
    {   class  = MsgPtr->Class;                       
        addr   = MsgPtr->IAddress;
        GT_ReplyIMsg(MsgPtr);
        switch(class)
        {
        case IDCMP_CLOSEWINDOW:
            cleanexit(EXIT_SUCCESS);
        break;
        case IDCMP_GADGETUP:
            if (addr == BU99_Left)
            {   // assert(gui);
                strcpy(temp, "Aborted by user!");
                say(temp, WHITE);
            };
            stop = TRUE;
        break;
        default:
        break;
    }   }
    if (!gui)
    {   CloseLibrary(IconBase);
        IconBase = NULL;
}   }

MODULE void iconasl(void)
{   struct FileRequester* ASLRqPtr;
    ULONG                 i;
    TEXT                  quotestring[2];

    quotestring[0] = QUOTE;
    quotestring[1] = 0;

    /* It would also be good to correctly
    handle selection of directories. */

    if (!(ASLRqPtr = AllocAslRequestTags(ASL_FileRequest, ASL_Pattern, "#?.info", ASL_Window, MainWindowPtr, TAG_DONE)))
        rq("Can't create ASL request!");
    if (AslRequestTags(ASLRqPtr, ASL_Dir, asldir, ASL_Hail, "Report+ file multiselector", ASL_FuncFlags, FILF_PATGAD | FILF_MULTISELECT, TAG_DONE) && *(ASLRqPtr->rf_File) != 0)
    {   if (ASLRqPtr->rf_NumArgs)
        {   /* rf_ArgList is an array of WBArg structures. Each entry in
            this array corresponds to one of the files the user selected
            (in alphabetical order). The user multiselected; step through
            the list of selected files. */

            strcpy(asldir, ASLRqPtr->rf_Dir);
            strcpy(icon.pathname, quotestring);
            strcat(icon.pathname, ASLRqPtr->rf_Dir);
            if (!AddPart(icon.pathname, (ASLRqPtr->rf_ArgList)[0].wa_Name, VLONGFIELD))
                rq("Can't add filename to pathname!");
            strcat(icon.pathname, quotestring);
            strcat(icon.pathname, " ");
            for (i = 1; i < ASLRqPtr->rf_NumArgs; i++)
            {   strcat(icon.pathname, quotestring);
                strcat(icon.pathname, ASLRqPtr->rf_Dir);
                if (!AddPart(icon.pathname, (ASLRqPtr->rf_ArgList)[i].wa_Name, VLONGFIELD))
                   rq("Can't add filename to pathname!");
                strcat(icon.pathname, quotestring);
                if (i < ASLRqPtr->rf_NumArgs - 1)
                    strcat(icon.pathname, " ");
        }   }
        else
        {   /* The user didn't multiselect; use the normal way to get the
            filename. */

            strcpy(icon.pathname, quotestring);
            strcat(icon.pathname, ASLRqPtr->rf_Dir);
            if (!AddPart(icon.pathname, ASLRqPtr->rf_File, VLONGFIELD))
                rq("Can't add filename to pathname!");
            strcat(icon.pathname, quotestring);
    }   }
    if (ASLRqPtr)
        FreeAslRequest(ASLRqPtr);
}

AGLOBAL void icon_do(ABOOL gui)
{   ABOOL flag = FALSE, quoted = FALSE;
    ULONG i, j = 0;

    /* At this point we have a list of pathnames, separated by spaces and
    NULL-terminated. If they came from ASL, they will be quoted, otherwise,
    they may not. We need to parse this. flag is a boolean value
    representing whether or not we have at least part of a pathname in
    construction; ie. it's to prevent problems from adjacent spaces.

    Names which match more than one of the wildcards are processed
    twice (or more). This is unnecessary. */

    icon_wildcard(icon.pathname);
    for (i = 0; i <= strlen(icon.finallist); i++)
        if (icon.finallist[i] == 0)
        {   if (flag && !quoted)
            {   // if we're in the middle of an unquoted name
                {   icon.thisfile[j] = 0; // then it is ended
                    iconconvert(gui);
                    j = 0;
                    flag = FALSE;
        }   }   }
        elif (icon.finallist[i] == ' ' && (flag && !quoted))
        {   // if we're in the middle of an unquoted name
            {   icon.thisfile[j] = 0; // then it is ended
                iconconvert(gui);
                j = 0;
                flag = FALSE;
        }   }
        elif (icon.finallist[i] == QUOTE)
        {   if (!flag && !quoted)
            {   flag = TRUE; /* we've started on a name. */
                quoted = TRUE;
            } elif (flag && quoted)
                quoted = FALSE;
        } else
        {   icon.thisfile[j++] = icon.finallist[i];
            flag = TRUE;
}       }

AGLOBAL void icon_loop(ULONG class, struct Gadget* addr, UWORD code, UWORD qual)
{   if (page == 121)
    {   if (class == IDCMP_VANILLAKEY)
        {   code = toupper(code);
            if (code == 'T')
            {   if (!(qual & IEQUALIFIER_LSHIFT) && !(qual & IEQUALIFIER_RSHIFT))
                {   if (icon.type < 7)
                        icon.type++;
                    else icon.type = 0;
                } else
                {   if (icon.type > 0)
                        icon.type--;
                    else icon.type = 7;
                }
                GT_SetGadgetAttrs
                (   MX121_Type,
                    MainWindowPtr,
                    NULL,
                    GTMX_Active, icon.type,
                    TAG_DONE
                );
            } elif (code == 'O')
            {   if (CB121_Optimize->Flags & GFLG_SELECTED)
                {   icon.optimize = FALSE;
                } else
                {   icon.optimize = TRUE;
                }
                GT_SetGadgetAttrs
                (   CB121_Optimize,
                    MainWindowPtr, NULL,
                    GTCB_Checked, icon.optimize,
                    TAG_DONE
                );
            } elif (code == 'L')
            {   if (CB121_Planar->Flags & GFLG_SELECTED)
                {   icon.planar = FALSE;
                } else
                {   icon.planar = TRUE;
                }
                GT_SetGadgetAttrs
                (   CB121_Planar,
                    MainWindowPtr, NULL,
                    GTCB_Checked, icon.planar,
                    TAG_DONE
                );
            } elif (code == 'N')
            {   if (CB121_NewIcon->Flags & GFLG_SELECTED)
                {   icon.newicon = FALSE;
                } else
                {   icon.newicon = TRUE;
                }
                GT_SetGadgetAttrs
                (   CB121_NewIcon,
                    MainWindowPtr, NULL,
                    GTCB_Checked, icon.newicon,
                    TAG_DONE
                );
            } elif (code == 'C')
            {   page = 122;
            } elif (code == 'P')
            {   ActivateGadget(ST121_Pathname, MainWindowPtr, NULL);
            } elif (code == ESCAPE)
            {   page = 0;
            } elif (code == '.')
            {   iconasl();
                GT_SetGadgetAttrs
                (   ST121_Pathname,
                    MainWindowPtr,
                    NULL,
                    GTST_String, icon.pathname,
                    TAG_DONE
                );
        }   }
        elif (class == IDCMP_GADGETDOWN)
        {   // radio gadgets
            if (addr == MX121_Type)
            {   icon.type = code;
        }   }
        elif (class == IDCMP_GADGETUP)
        {   // button, etc. gadgets
            if (addr == BU99_Left)
                page = 0;
            elif (addr == BU99_Right)
                page = 122;
            elif (addr == BU121_PathnameASL)
            {   iconasl();
                GT_SetGadgetAttrs
                (   ST121_Pathname,
                    MainWindowPtr,
                    NULL,
                    GTST_String, icon.pathname,
                    TAG_DONE
                );
            } elif (addr == CB121_Optimize)
            {   if (CB121_Optimize->Flags & GFLG_SELECTED)
                {   icon.optimize = TRUE;
                } else icon.optimize = FALSE;
            } elif (addr == CB121_Planar)
            {   if (CB121_Planar->Flags & GFLG_SELECTED)
                {   icon.planar = TRUE;
                } else icon.planar = FALSE;
            } elif (addr == CB121_NewIcon)
            {   if (CB121_NewIcon->Flags & GFLG_SELECTED)
                {   icon.newicon = TRUE;
                } else icon.newicon = FALSE;
    }   }   }
    else
    {   // assert(page == 122);
        if (class == IDCMP_GADGETUP)
        {   if (addr == BU99_Right)
                page = 0;
        } elif (class == IDCMP_VANILLAKEY)
        {   if (code == ESCAPE)
                page = 0;
}   }   }

MODULE void icon_wildcard(STRPTR pattern)
{   ULONG i, j = 0;
    ABOOL flag = FALSE, quoted = FALSE;

    icon.finallist[0] = 0;

    for (i = 0; i <= strlen(icon.pathname); i++)
    {   if (icon.pathname[i] == 0)
        {   if (flag && !quoted)
            {   // if we're in the middle of an unquoted name
                {   icon.thatfile[j] = 0; // then it is hereby ended
                    icon_eachwildcard(icon.thatfile);
                    j = 0;
                    flag = FALSE;
        }   }   }
        elif (icon.pathname[i] == ' ' && flag && !quoted)
        {   // if we're in the middle of an unquoted name
            {   icon.thatfile[j] = 0; // then it is ended
                icon_eachwildcard(icon.thatfile);
                j = 0;
                flag = FALSE;
        }   }
        elif (icon.pathname[i] == QUOTE)
        {   if (!flag && !quoted)
            {   flag = TRUE; /* we've started on a name. */
                quoted = TRUE;
            } elif (flag && quoted)
                quoted = FALSE;
        } else
        {   icon.thatfile[j++] = icon.pathname[i];
            flag = TRUE;
}   }   }

MODULE void icon_eachwildcard(STRPTR subpattern)
{   struct AnchorPath* AnchorPathPtr;
    BPTR               OldDir;
    TEXT               dirbuffer[VLONGFIELD + 1];
    BOOL               result;
    ABOOL              done;

    dirbuffer[0] = NULL;
    if (!(AnchorPathPtr = AllocMem(sizeof(struct AnchorPath), MEMF_ANY | MEMF_PUBLIC)))
        return;

    AnchorPathPtr->ap_BreakBits = NULL;
    AnchorPathPtr->ap_Flags = NULL;
    AnchorPathPtr->ap_Strlen = 0;
    result = MatchFirst(subpattern, AnchorPathPtr);
    if (result == 0)
    {   OldDir = CurrentDir(AnchorPathPtr->ap_Current->an_Lock);
        if (!NameFromLock(AnchorPathPtr->ap_Current->an_Lock, dirbuffer, VLONGFIELD))
        {   MatchEnd(AnchorPathPtr);
            return;
        }
        if (!AddPart(dirbuffer, AnchorPathPtr->ap_Info.fib_FileName, VLONGFIELD))
        {   MatchEnd(AnchorPathPtr);
            return;
        }
        CurrentDir(OldDir);
        strcat(icon.finallist, "\"");
        strcat(icon.finallist, dirbuffer);
        strcat(icon.finallist, "\" ");
    } elif (result != ERROR_NO_MORE_ENTRIES)
    {   MatchEnd(AnchorPathPtr);
        return;
    }

    done = FALSE;
    while (!done)
    {   result = MatchNext(AnchorPathPtr);
        if (result == 0)
        {   OldDir = CurrentDir(AnchorPathPtr->ap_Current->an_Lock);
            if (!NameFromLock(AnchorPathPtr->ap_Current->an_Lock, dirbuffer, VLONGFIELD))
            {   MatchEnd(AnchorPathPtr);
                return;
            }
            if (!AddPart(dirbuffer, AnchorPathPtr->ap_Info.fib_FileName, VLONGFIELD))
            {   MatchEnd(AnchorPathPtr);
                return;
            }
            CurrentDir(OldDir);
            strcat(icon.finallist, "\"");
            strcat(icon.finallist, dirbuffer);
            strcat(icon.finallist, "\" ");
        } elif (result != ERROR_NO_MORE_ENTRIES)
        {   MatchEnd(AnchorPathPtr);
            return;
        } else done = TRUE;
    }
    MatchEnd(AnchorPathPtr);
    FreeMem(AnchorPathPtr, sizeof(struct AnchorPath));
}
