// system includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>
#include <graphics/gfxbase.h>
#include <graphics/text.h>
#include <graphics/gfxmacros.h>
#include <utility/tagitem.h>
#include <utility/hooks.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>

#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>
#include <proto/wb.h>
#include <proto/icon.h>

#include <clib/alib_protos.h>
#include <clib/texteditor_protos.h>

// ReAction includes
#define ALL_REACTION_CLASSES
#define ALL_REACTION_MACROS
#include <reaction/reaction.h>

#include <gadgets/texteditor.h>
#include <pragmas/texteditor_pragmas.h>

#include "amigan.h"

#define SCAN_ESCAPE (0x45)
#define ASM         __asm
#define REG(x)      register __ ## x

void InitHook(struct Hook* hook, ULONG (*func)(), void* data);
ULONG RawKeyHook(struct Hook* h, void* o, void* m);
                                                                 
enum
{   GID_MAIN = 0,
    GID_TEXTEDITOR1,
    GID_TEXTEDITOR2,
    GID_TEXTEDITOR3,
    GID_TEXTEDITOR4,
    GID_TEXTEDITOR5,
    GID_TEXTEDITOR6,
    GID_TEXTEDITOR7,
    GID_TEXTEDITOR8,
    GID_TEXTEDITOR9,
    GID_TEXTEDITOR10,
    GID_TEXTEDITOR11,
    GID_TEXTEDITOR12,
    GID_TEXTEDITOR13,
    GID_TEXTEDITOR14,
    GID_LISTBROWSER,
    GID_DOWN,
    GID_UP,
    GID_FINISH,
    GID_LAST
};

enum
{   WID_MAIN = 0,
    WID_LAST
};

enum
{   OID_MAIN = 0,
    OID_LAST
};

IMPORT struct Library      *WindowBase,
                           *LayoutBase,
                           *ButtonBase,
                           *TextEditorBase,
                           *LabelBase,
                           *ListBrowserBase;
IMPORT ABOOL                pal;
IMPORT SBYTE                page;
IMPORT ULONG                elements;
IMPORT ULONG                returncode, inputs;
IMPORT APTR                 OldWindowPtr;
IMPORT STRPTR               textBuffer[MAXELEMENTS + 1];
IMPORT struct Screen*       ScreenPtr;
IMPORT struct Process*      ProcessPtr;
IMPORT struct ReportStruct  report;
IMPORT struct AutodocStruct autodoc;
IMPORT struct NewGadget     Gadget; // only for the Gadget.ng_TextAttr field
IMPORT struct List          FilesList;

MODULE struct Gadget*       gadgets[GID_LAST];
MODULE        Object*       objects[OID_LAST];
MODULE ABOOL                done;
MODULE struct Window*       windows[WID_LAST];

AGLOBAL void textedit(void)
{   struct MsgPort* AppPort;

    TEXT            GadTitle[MAXELEMENTS + 1][VLONGFIELD + 1];
    ULONG           i, index, wrapsize;
    ABOOL           doublecolumn;

    struct TagItem  windowtag[14],
                    rootlayouttag[7],
                    bothlayouttag[8],
                    supercaptiontag[8],
                    subcaptiontag[3],
                    texteditortag[4],
                    buttontag[4],
                     leftlayouttag[4 + (3 * (MAXELEMENTS + 1))],
                    rightlayouttag[4 + (3 * (MAXELEMENTS + 1))];

    if (page == 15)
    {   doublecolumn = FALSE;
        wrapsize = 75;
        switch(report.type)
        {
        case BUG:
            strcpy(GadTitle[0], "Brief bug description:");
            strcpy(GadTitle[1], "Bug generation procedure or example:");
            strcpy(GadTitle[2], "If this works differently on other versions or hardware, explain:");
            strcpy(GadTitle[3], "Related problems:");
            elements = 3;
        break;
        case COM:
            if (report.severity == 3)
            {   strcpy(GadTitle[0], "If this works differently on other versions or hardware, explain:");
                elements = 0;
            } else
            {   strcpy(GadTitle[0], "Brief bug description:");
                strcpy(GadTitle[1], "Bug generation procedure or example:");
                strcpy(GadTitle[2], "If this works differently on other versions or hardware, explain:");
                strcpy(GadTitle[3], "What developer is doing in that area of software or hardware:");
                strcpy(GadTitle[4], "Related problems:");
                elements = 4;
            }
        break;
        case ENH:
            strcpy(GadTitle[0], "Enhancement request:");
            elements = 0;
        break;
        default:
        break;
    }   }
    elif (page == 22)
    {   strcpy(GadTitle[0], "Long description:");
        elements = 0;
        doublecolumn = FALSE;
        wrapsize = 75;
    } else
    {   // assert(page == 42);
        strcpy(GadTitle[0], "Function:");
        for (i = 1; i <= inputs; i++)
        {   strcpy(GadTitle[i], autodoc.var[i].name);
            strcat(GadTitle[i], ":");
        }
        if (returncode)
            strcpy(GadTitle[inputs + 1], "Result:");
        strcpy(GadTitle[inputs + 1 + returncode], "Example:");
        strcpy(GadTitle[inputs + 2 + returncode], "Notes:");
        strcpy(GadTitle[inputs + 3 + returncode], "Bugs:");
        strcpy(GadTitle[inputs + 4 + returncode], "See Also:");
        elements =      inputs + 4 + returncode;
        doublecolumn = TRUE;
        wrapsize = 72;
    }

    /* make sure our classes opened... */
    if (!(WindowBase = OpenLibrary("window.class", 44)))
        rq("OpenLibrary(\"window.class\") failed!");
    if (!(LayoutBase = OpenLibrary("gadgets/layout.gadget", 44)))
        rq("OpenLibrary(\"gadgets/layout.gadget\") failed!");
    if (!(ButtonBase = OpenLibrary("gadgets/button.gadget", 44)))
        rq("OpenLibrary(\"gadgets/button.gadget\") failed!");
    if (!(TextEditorBase = OpenLibrary("gadgets/texteditor.gadget", 0)))
        rq("OpenLibrary(\"gadgets/texteditor.gadget\") failed!");
    if (!(LabelBase = OpenLibrary("images/label.image", 44)))
        rq("OpenLibrary(\"images/label.image\") failed!");

    if (AppPort = CreateMsgPort())
    {   /* Create the window object. */

        subcaptiontag[0].ti_Tag     = LABEL_Text;
        // subcaptiontag[0].ti_Data filled later
        subcaptiontag[1].ti_Tag     = LABEL_Justification;
        subcaptiontag[1].ti_Data    = LJ_CENTRE;
        subcaptiontag[2].ti_Tag     = TAG_END;

        texteditortag[0].ti_Tag  = GA_ID;
        // texteditortag[0].ti_Data filled later
        texteditortag[1].ti_Tag  = GA_RelVerify;
        texteditortag[1].ti_Data = TRUE;
        texteditortag[2].ti_Tag  = GA_TEXTEDITOR_ExportWrap;
        texteditortag[2].ti_Data = wrapsize;
        texteditortag[3].ti_Tag  = TAG_END;

        buttontag[0].ti_Tag      = GA_ID;
        buttontag[0].ti_Data     = GID_FINISH;
        buttontag[1].ti_Tag      = GA_RelVerify;
        buttontag[1].ti_Data     = TRUE;
        buttontag[2].ti_Tag      = GA_Text;
        buttontag[2].ti_Data     = "_Finish";
        buttontag[3].ti_Tag      = TAG_END;

        supercaptiontag[0].ti_Tag  = LAYOUT_Orientation;
        supercaptiontag[0].ti_Data = LAYOUT_ORIENT_VERT;
        supercaptiontag[1].ti_Tag  = GA_BackFill;
        supercaptiontag[1].ti_Data = NULL;
        supercaptiontag[2].ti_Tag  = LAYOUT_SpaceOuter;
        supercaptiontag[2].ti_Data = TRUE;
        supercaptiontag[3].ti_Tag  = LAYOUT_VertAlignment;
        supercaptiontag[3].ti_Data = LALIGN_CENTER;
        supercaptiontag[4].ti_Tag  = LAYOUT_HorizAlignment;
        supercaptiontag[4].ti_Data = LALIGN_CENTER;
        supercaptiontag[5].ti_Tag  = LAYOUT_BevelStyle;
        supercaptiontag[5].ti_Data = BVS_FIELD;
        supercaptiontag[6].ti_Tag  = LAYOUT_AddImage;
        // supercaptiontag[6].ti_Data filled later
        supercaptiontag[7].ti_Tag  = TAG_END;

    leftlayouttag[0].ti_Tag  = LAYOUT_Orientation;
    leftlayouttag[0].ti_Data = LAYOUT_ORIENT_VERT;
    leftlayouttag[1].ti_Tag  = LAYOUT_SpaceOuter;
    leftlayouttag[1].ti_Data = TRUE;
    leftlayouttag[2].ti_Tag  = LAYOUT_DeferLayout;
    leftlayouttag[2].ti_Data = TRUE;
    index = 3;

    if (doublecolumn)
    {   for (i = 0; i <= elements / 2; i++)
        {   subcaptiontag[0].ti_Data       = GadTitle[i];
            texteditortag[0].ti_Data       = GID_TEXTEDITOR1 + i;
            supercaptiontag[6].ti_Data     = NewObjectA(LABEL_GetClass(), NULL, subcaptiontag);
            leftlayouttag[index].ti_Tag    = LAYOUT_AddChild;
            leftlayouttag[index++].ti_Data = NewObjectA(LAYOUT_GetClass(), NULL, supercaptiontag);
            leftlayouttag[index].ti_Tag    = CHILD_WeightedHeight;
            leftlayouttag[index++].ti_Data = 0;
            leftlayouttag[index].ti_Tag    = LAYOUT_AddChild;
            leftlayouttag[index++].ti_Data = gadgets[GID_TEXTEDITOR1 + i] = NewObjectA(TEXTEDITOR_GetClass(), NULL, texteditortag);
        }
        leftlayouttag[index].ti_Tag        = TAG_END;

        rightlayouttag[0].ti_Tag  = LAYOUT_Orientation;
        rightlayouttag[0].ti_Data = LAYOUT_ORIENT_VERT;
        rightlayouttag[1].ti_Tag  = LAYOUT_SpaceOuter;
        rightlayouttag[1].ti_Data = TRUE;
        rightlayouttag[2].ti_Tag  = LAYOUT_DeferLayout;
        rightlayouttag[2].ti_Data = TRUE;
        index = 3;
        for (i = (elements / 2) + 1; i <= elements; i++)
        {   subcaptiontag[0].ti_Data        = GadTitle[i];
            texteditortag[0].ti_Data        = GID_TEXTEDITOR1 + i;
            supercaptiontag[6].ti_Data      = NewObjectA(LABEL_GetClass(), NULL, subcaptiontag);
            rightlayouttag[index].ti_Tag    = LAYOUT_AddChild;
            rightlayouttag[index++].ti_Data = NewObjectA(LAYOUT_GetClass(), NULL, supercaptiontag);
            rightlayouttag[index].ti_Tag    = CHILD_WeightedHeight;
            rightlayouttag[index++].ti_Data = 0;
            rightlayouttag[index].ti_Tag    = LAYOUT_AddChild;
            rightlayouttag[index++].ti_Data = gadgets[GID_TEXTEDITOR1 + i] = NewObjectA(TEXTEDITOR_GetClass(), NULL, texteditortag);
        }
        rightlayouttag[index].ti_Tag        = TAG_END;
    } else
    {   for (i = 0; i <= elements; i++)
        {   subcaptiontag[0].ti_Data       = GadTitle[i];
            texteditortag[0].ti_Data       = GID_TEXTEDITOR1 + i;
            supercaptiontag[6].ti_Data     = NewObjectA(LABEL_GetClass(), NULL, subcaptiontag);
            leftlayouttag[index].ti_Tag    = LAYOUT_AddChild;
            leftlayouttag[index++].ti_Data = NewObjectA(LAYOUT_GetClass(), NULL, supercaptiontag);
            leftlayouttag[index].ti_Tag    = CHILD_WeightedHeight;
            leftlayouttag[index++].ti_Data = 0;
            leftlayouttag[index].ti_Tag    = LAYOUT_AddChild;
            leftlayouttag[index++].ti_Data = gadgets[GID_TEXTEDITOR1 + i] = NewObjectA(TEXTEDITOR_GetClass(), NULL, texteditortag);
        }
        leftlayouttag[index].ti_Tag        = TAG_END;
    }

    /* The captions are as wide as required to hold the entire caption on
    one line. In many cases this means that `double-column' mode cannot be
    used, as the right column would be pushed out too far to the right,
    and partially offscreen. */

    bothlayouttag[0].ti_Tag  = LAYOUT_Orientation;
    bothlayouttag[0].ti_Data = LAYOUT_ORIENT_HORIZ;
    bothlayouttag[1].ti_Tag  = LAYOUT_SpaceOuter;
    bothlayouttag[1].ti_Data = TRUE;
    bothlayouttag[2].ti_Tag  = LAYOUT_DeferLayout;
    bothlayouttag[2].ti_Data = TRUE;

    bothlayouttag[3].ti_Tag  = LAYOUT_AddChild;
    bothlayouttag[3].ti_Data = NewObjectA(LAYOUT_GetClass(), NULL, leftlayouttag);
    bothlayouttag[4].ti_Tag  = CHILD_WeightedWidth;
    bothlayouttag[4].ti_Data = 50;
    if (doublecolumn)
    {   bothlayouttag[5].ti_Tag  = LAYOUT_AddChild;
        bothlayouttag[5].ti_Data = NewObjectA(LAYOUT_GetClass(), NULL, rightlayouttag);
        bothlayouttag[6].ti_Tag  = CHILD_WeightedWidth;
        bothlayouttag[6].ti_Data = 50;
        bothlayouttag[7].ti_Tag  = TAG_END;
    } else
        bothlayouttag[5].ti_Tag  = TAG_END;

        rootlayouttag[0].ti_Tag  = LAYOUT_Orientation;
        rootlayouttag[0].ti_Data = LAYOUT_ORIENT_VERT;
        rootlayouttag[1].ti_Tag  = LAYOUT_SpaceOuter;
        rootlayouttag[1].ti_Data = TRUE;
        rootlayouttag[2].ti_Tag  = LAYOUT_DeferLayout;
        rootlayouttag[2].ti_Data = TRUE;
        rootlayouttag[3].ti_Tag  = LAYOUT_AddChild;
        rootlayouttag[3].ti_Data = NewObjectA(LAYOUT_GetClass(), NULL, bothlayouttag);
        rootlayouttag[4].ti_Tag  = LAYOUT_AddChild;
        rootlayouttag[4].ti_Data = NewObjectA(NULL, "button.gadget", buttontag);
        rootlayouttag[5].ti_Tag  = CHILD_WeightedHeight;
        rootlayouttag[5].ti_Data = 0;
        rootlayouttag[6].ti_Tag  = TAG_END;

        if (pal)
        {   windowtag[0].ti_Tag  = WA_CustomScreen;
            windowtag[0].ti_Data = ScreenPtr;
        } else
        {   if (!(ScreenPtr = LockPubScreen(NULL)))
                rq("Can't lock default public screen!");
            windowtag[0].ti_Tag  = WA_PubScreen;
            windowtag[0].ti_Data = ScreenPtr;
        }
        windowtag[1].ti_Tag  = WA_ScreenTitle;
        windowtag[1].ti_Data = TITLEBARTEXT;
        windowtag[2].ti_Tag  = WA_Title;
        windowtag[2].ti_Data = "Report+: Text Editor";
        windowtag[3].ti_Tag  = WA_Activate;
        windowtag[3].ti_Data = TRUE;
        windowtag[4].ti_Tag  = WA_DepthGadget;
        windowtag[4].ti_Data = TRUE;
        windowtag[5].ti_Tag  = WA_DragBar;
        windowtag[5].ti_Data = TRUE;
        windowtag[6].ti_Tag  = WA_CloseGadget;
        windowtag[6].ti_Data = TRUE;
        windowtag[7].ti_Tag  = WA_SizeGadget;
        windowtag[7].ti_Data = TRUE;
        windowtag[8].ti_Tag  = WINDOW_IconifyGadget;
        windowtag[8].ti_Data = TRUE;
        windowtag[9].ti_Tag  = WINDOW_IconTitle;
        windowtag[9].ti_Data = "Report+.iconified";
        windowtag[10].ti_Tag  = WINDOW_AppPort;
        windowtag[10].ti_Data = AppPort;
        windowtag[11].ti_Tag  = WINDOW_Position;
        windowtag[11].ti_Data = WPOS_FULLSCREEN;
        windowtag[12].ti_Tag  = WINDOW_ParentGroup;
        windowtag[12].ti_Data = gadgets[GID_MAIN] = NewObjectA(LAYOUT_GetClass(), NULL, rootlayouttag);
        windowtag[13].ti_Tag  = TAG_END;

        objects[OID_MAIN] = NewObjectA(WINDOW_GetClass(), NULL, windowtag);

        if (pal)
        {   /* redirection of AmigaDOS system requesters */
            OldWindowPtr = ProcessPtr->pr_WindowPtr;
            ProcessPtr->pr_WindowPtr = (APTR) objects[OID_MAIN];
        } else
        {   UnlockPubScreen(NULL, ScreenPtr);
            ScreenPtr = NULL;
        }

        // Object creation successful?
        if (objects[OID_MAIN])
        {   // Open the window.
            if (windows[WID_MAIN] = (struct Window *) RA_OpenWindow(objects[OID_MAIN]))
            {   ULONG wait, signal, app = (1L << AppPort->mp_SigBit);
                ULONG result;
                UWORD code;

                done = FALSE;

                // Obtain the window wait signal mask.
                GetAttr(WINDOW_SigMask, objects[OID_MAIN], &signal);

                // Activate the first texteditor gadget.
                ActivateLayoutGadget(gadgets[GID_MAIN], windows[WID_MAIN], NULL, (Object) gadgets[GID_TEXTEDITOR1]);

                // Input Event Loop
                while (!done)
                {   wait = Wait( signal | SIGBREAKF_CTRL_C | app );

                    if ( wait & SIGBREAKF_CTRL_C )
                        done = TRUE;
                    else
                    {   while ( (result = RA_HandleInput(objects[OID_MAIN], &code) ) != WMHI_LASTMSG )
                        {   switch (result & WMHI_CLASSMASK)
                            {
                            case WMHI_CLOSEWINDOW:
                                // windows[WID_MAIN] = NULL;
                                done = TRUE;
                            break;
                            case WMHI_GADGETUP:
                                switch (result & WMHI_GADGETMASK)
                                {
                                case GID_FINISH:
                                    done = TRUE;
                                break;
                                default:
                                break;
                                }
                            break;
                            case WMHI_ICONIFY:
                                RA_Iconify(objects[OID_MAIN]);
                                // windows[WID_MAIN] = NULL;
                            break;
                            case WMHI_UNICONIFY:
                                windows[WID_MAIN] = (struct Window *) RA_OpenWindow(objects[OID_MAIN]);
                                if (windows[WID_MAIN])
                                    GetAttr(WINDOW_SigMask, objects[OID_MAIN], &signal);
                                else done = TRUE; // error re-opening window!
                            break;
                            default:
                            break;
            }   }   }   }   }

            for (i = 0; i <= elements; i++)
                textBuffer[i] = (STRPTR) DoGadgetMethod(gadgets[GID_TEXTEDITOR1 + i], (struct Window *) objects[WID_MAIN], NULL, GM_TEXTEDITOR_ExportText, NULL);

            /* Disposing of the window object will also close the window if it is
             * already opened, and it will dispose of the layout object attached to it.
             */
            DisposeObject(objects[OID_MAIN]);
        } else rq("Report+: Can't create ReAction objects!");
        DeleteMsgPort(AppPort);
    } else rq("Report+: Can't create window message port!");

    // Close the classes.
    CloseLibrary(LabelBase);
    LabelBase = NULL;
    CloseLibrary(TextEditorBase);
    TextEditorBase = NULL;
    CloseLibrary(ButtonBase);
    ButtonBase = NULL;
    CloseLibrary(LayoutBase);
    LayoutBase = NULL;
    CloseLibrary(WindowBase);
    WindowBase = NULL;

    return;
}
//----------------------------------------------------------------------------
AGLOBAL void listbrowse(void)
{   struct MsgPort* AppPort;
    struct Window*  windows[WID_LAST];
    struct Hook     hook;

    struct TagItem  windowtag[16],
                    rootlayouttag[7],
                    onlylayouttag[8],
                    listbrowsertag[6],
                    buttontag[4];

    /* make sure our classes opened...EXCEPT listbrowser */
    if (!(WindowBase = OpenLibrary("window.class", 44)))
        rq("OpenLibrary(\"window.class\") failed!");
    if (!(LayoutBase = OpenLibrary("gadgets/layout.gadget", 44)))
        rq("OpenLibrary(\"gadgets/layout.gadget\") failed!");
    if (!(ButtonBase = OpenLibrary("gadgets/button.gadget", 44)))
        rq("OpenLibrary(\"gadgets/button.gadget\") failed!");
    if (!(LabelBase = OpenLibrary("images/label.image", 44)))
        rq("OpenLibrary(\"images/label.image\") failed!");

    InitHook(&hook, RawKeyHook, NULL);

    if (AppPort = CreateMsgPort())
    {   /* Create the window object. */

        listbrowsertag[0].ti_Tag  = GA_ID;
        listbrowsertag[0].ti_Data = GID_LISTBROWSER;
        listbrowsertag[1].ti_Tag  = GA_ReadOnly;
        listbrowsertag[1].ti_Data = TRUE;
        listbrowsertag[2].ti_Tag  = GA_TextAttr;
        listbrowsertag[2].ti_Data = Gadget.ng_TextAttr;
        listbrowsertag[3].ti_Tag  = LISTBROWSER_ScrollRaster;
        listbrowsertag[3].ti_Data = TRUE;
        listbrowsertag[4].ti_Tag  = LISTBROWSER_Labels;
        listbrowsertag[4].ti_Data = (ULONG) &FilesList;
        listbrowsertag[5].ti_Tag  = TAG_END;

        buttontag[0].ti_Tag      = GA_ID;
        buttontag[0].ti_Data     = GID_FINISH;
        buttontag[1].ti_Tag      = GA_RelVerify;
        buttontag[1].ti_Data     = TRUE;
        buttontag[2].ti_Tag      = GA_Text;
        buttontag[2].ti_Data     = "_Finish";
        buttontag[3].ti_Tag      = TAG_END;

        onlylayouttag[0].ti_Tag  = LAYOUT_Orientation;
        onlylayouttag[0].ti_Data = LAYOUT_ORIENT_VERT;
        onlylayouttag[1].ti_Tag  = LAYOUT_SpaceOuter;
        onlylayouttag[1].ti_Data = TRUE;
        onlylayouttag[2].ti_Tag  = LAYOUT_DeferLayout;
        onlylayouttag[2].ti_Data = TRUE;
        onlylayouttag[3].ti_Tag  = LAYOUT_AddChild;
        onlylayouttag[3].ti_Data = gadgets[GID_LISTBROWSER] = NewObjectA(LISTBROWSER_GetClass(), NULL, listbrowsertag);
        onlylayouttag[4].ti_Tag  = TAG_END;

        rootlayouttag[0].ti_Tag  = LAYOUT_Orientation;
        rootlayouttag[0].ti_Data = LAYOUT_ORIENT_VERT;
        rootlayouttag[1].ti_Tag  = LAYOUT_SpaceOuter;
        rootlayouttag[1].ti_Data = TRUE;
        rootlayouttag[2].ti_Tag  = LAYOUT_DeferLayout;
        rootlayouttag[2].ti_Data = TRUE;
        rootlayouttag[3].ti_Tag  = LAYOUT_AddChild;
        rootlayouttag[3].ti_Data = NewObjectA(LAYOUT_GetClass(), NULL, onlylayouttag);
        rootlayouttag[4].ti_Tag  = LAYOUT_AddChild;
        rootlayouttag[4].ti_Data = NewObjectA(NULL, "button.gadget", buttontag);
        rootlayouttag[5].ti_Tag  = CHILD_WeightedHeight;
        rootlayouttag[5].ti_Data = 0;
        rootlayouttag[6].ti_Tag  = TAG_END;

        if (pal)
        {   windowtag[0].ti_Tag  = WA_CustomScreen;
            windowtag[0].ti_Data = ScreenPtr;
        } else
        {   if (!(ScreenPtr = LockPubScreen(NULL)))
                rq("Can't lock default public screen!");
            windowtag[0].ti_Tag  = WA_PubScreen;
            windowtag[0].ti_Data = ScreenPtr;
        }
        windowtag[1].ti_Tag  = WA_ScreenTitle;
        windowtag[1].ti_Data = TITLEBARTEXT;
        windowtag[2].ti_Tag  = WA_Title;
        windowtag[2].ti_Data = "Report+: List Browser";
        windowtag[3].ti_Tag  = WA_Activate;
        windowtag[3].ti_Data = TRUE;
        windowtag[4].ti_Tag  = WA_DepthGadget;
        windowtag[4].ti_Data = TRUE;
        windowtag[5].ti_Tag  = WA_DragBar;
        windowtag[5].ti_Data = TRUE;
        windowtag[6].ti_Tag  = WA_CloseGadget;
        windowtag[6].ti_Data = TRUE;
        windowtag[7].ti_Tag  = WA_SizeGadget;
        windowtag[7].ti_Data = TRUE;
        windowtag[8].ti_Tag  = WINDOW_IconifyGadget;
        windowtag[8].ti_Data = TRUE;
        windowtag[9].ti_Tag  = WINDOW_IconTitle;
        windowtag[9].ti_Data = "Report+.iconified";
        windowtag[10].ti_Tag  = WINDOW_AppPort;
        windowtag[10].ti_Data = AppPort;
        windowtag[11].ti_Tag  = WINDOW_Position;
        windowtag[11].ti_Data = WPOS_FULLSCREEN;
        windowtag[12].ti_Tag  = WINDOW_IDCMPHook;
        windowtag[12].ti_Data = &hook;
        windowtag[13].ti_Tag  = WINDOW_IDCMPHookBits;
        windowtag[13].ti_Data = IDCMP_RAWKEY;
        windowtag[14].ti_Tag  = WINDOW_ParentGroup;
        windowtag[14].ti_Data = gadgets[GID_MAIN] = NewObjectA(LAYOUT_GetClass(), NULL, rootlayouttag);
        windowtag[15].ti_Tag  = TAG_END;

        objects[OID_MAIN] = NewObjectA(WINDOW_GetClass(), NULL, windowtag);

        if (pal)
        {   /* redirection of AmigaDOS system requesters */
            OldWindowPtr = ProcessPtr->pr_WindowPtr;
            ProcessPtr->pr_WindowPtr = (APTR) objects[OID_MAIN];
        } else
        {   UnlockPubScreen(NULL, ScreenPtr);
            ScreenPtr = NULL;
        }

        // Object creation successful?
        if (objects[OID_MAIN])
        {   // Open the window.
            if (windows[WID_MAIN] = (struct Window *) RA_OpenWindow(objects[OID_MAIN]))
            {   ULONG wait, signal, app = (1L << AppPort->mp_SigBit);
                ULONG result;
                UWORD code;

                done = FALSE;

                // Obtain the window wait signal mask.
                GetAttr(WINDOW_SigMask, objects[OID_MAIN], &signal);

                // Input Event Loop
                while (!done)
                {   wait = Wait( signal | SIGBREAKF_CTRL_C | app );

                    if ( wait & SIGBREAKF_CTRL_C )
                        done = TRUE;
                    else
                    {   while ( (result = RA_HandleInput(objects[OID_MAIN], &code) ) != WMHI_LASTMSG )
                        {   switch (result & WMHI_CLASSMASK)
                            {
                            case WMHI_CLOSEWINDOW:
                                // windows[WID_MAIN] = NULL;
                                done = TRUE;
                            break;
                            case WMHI_GADGETUP:
                                switch (result & WMHI_GADGETMASK)
                                {
                                case GID_FINISH:
                                    done = TRUE;
                                break;
                                default:
                                break;
                                }
                            break;
                            case WMHI_ICONIFY:
                                RA_Iconify(objects[OID_MAIN]);
                                // windows[WID_MAIN] = NULL;
                            break;
                            case WMHI_UNICONIFY:
                                windows[WID_MAIN] = (struct Window *) RA_OpenWindow(objects[OID_MAIN]);
                                if (windows[WID_MAIN])
                                    GetAttr(WINDOW_SigMask, objects[OID_MAIN], &signal);
                                else done = TRUE; // error re-opening window!
                            break;
                            default:
                            break;
            }   }   }   }   }

            // consider showing byte sizes? any user demand for that?

            /* Disposing of the window object will also close the window if it is
             * already opened, and it will dispose of the layout object attached to it.
             */
            DisposeObject(objects[OID_MAIN]);
        } else rq("Report+: Can't create ReAction objects!");
        DeleteMsgPort(AppPort);
    } else rq("Report+: Can't create window message port!");

    // Close the classes, EXCEPT listbrowser
    CloseLibrary(ButtonBase);
    ButtonBase = NULL;
    CloseLibrary(LayoutBase);
    LayoutBase = NULL;
    CloseLibrary(WindowBase);
    WindowBase = NULL;

    return;
}

/* from RKRM Libraries, p. 877-878:

This function converts register-parameter hook calling convention into
standard C conventions. It rquires a C compiler that supports registerized
parameters, such as SAS/C 5.x+.

ULONG ASM HookEntry(REG(a0) struct Hook *h, REG(a2) VOID *o, REG(a1) VOID *m)
{   return
    (   (*(ULONG (*) (struct Hook *, VOID *, VOID *)) (h->h_SubEntry))
        (h, o, m)
    );
} */

void InitHook(struct Hook* hook, ULONG (*func)(), void* data)
{   // Make sure a pointer was passed
    if (hook)
    {   // Fill in the Hook fields
        hook->h_Entry    = (ULONG (*) ()) HookEntry;
        hook->h_SubEntry = func;
        hook->h_Data     = data;
}   }

ULONG RawKeyHook(struct Hook* h, void* o, void* m)
{   UWORD code = ((struct IntuiMessage *) m)->Code;

    // get access to the global data segment
    geta4();

    /* Eventually we might implement listbrowser scrolling using the
    cursor keys. */

    switch(code) {
    case SCAN_ESCAPE:
        done = TRUE;
    break;
    default:
    break;
    }     
    return(0);
}
