// helloWindow.c - GS/OS app that opens a Window Manager window and
// draws a greeting in it.  Runs under real GS/OS 6.0.2 in MAME.
//
// What this exercises:
//   - The full Window Manager StartUp chain (Memory + QD + Event +
//     Scheduler + Window).
//   - NewWindow ParamList with paramLength = sizeof (ORCA Clock.cc /
//     Reversi.cc convention).
//   - SetPort / ShowWindow / MoveTo / DrawString round-trip.
//   - Event-driven keypress wait via GetNextEvent.
//   - The W65816 backend's bank-byte relocation:
//       `gWp.wTitle = gTitle` stores a 32-bit pointer where the bank
//       byte is materialized via the new LDAi16imm_bank pseudo
//       (lowered to `lda $BE` reading PBR from a crt0-set DP slot).
//       Toolbox calls now receive correct `bank:offset` pointers for
//       any `&global` argument — no wrapper-side workarounds needed.
//
// Why fTitle is NOT set in wFrameBits despite wTitle being valid:
//   The Window Manager hangs trying to render a titled window without
//   Font Manager initialization.  A "real" titled-window demo would
//   need to drive QDStartUp's font allocation and possibly start the
//   Font Manager (FMStartUp) — that's the next milestone.

#include "iigs/toolbox.h"

#define fVis    0x0020
#define fMove   0x0080
#define fZoom   0x0100
#define fGrow   0x0400
#define fClose  0x4000
#define fTitle  0x8000


typedef struct { short v1, h1, v2, h2; } Rect;

typedef struct {
    unsigned short paramLength;
    unsigned short wFrameBits;
    void          *wTitle;
    unsigned long  wRefCon;
    Rect           wZoom;
    void          *wColor;
    short          wYOrigin, wXOrigin;
    short          wDataH,   wDataV;
    short          wMaxHeight, wMaxWidth;
    short          wScrollVer, wScrollHor;
    short          wPageVer,   wPageHor;
    unsigned long  wInfoRefCon;
    short          wInfoHeight;
    void          *wFrameDefProc;
    void          *wInfoDefProc;
    void          *wContDefProc;
    Rect           wPosition;
    void          *wPlane;
    void          *wStorage;
} NewWindowParm;


// Pascal strings: leading length byte, then characters.
static unsigned char gTitle[] = "\x09llvm816!!";
static unsigned char gMsg[]   = "\x14Hello from llvm816!";

// ParamList in BSS so the bank byte of &gWp resolves to PBR via the
// new LDAi16imm_bank reloc path.
static NewWindowParm gWp;


static unsigned short blockAddr(void *handle) {
    return (unsigned short)(unsigned long)*(void **)handle;
}


int main(void) {
    unsigned short userId = MMStartUp();

    void *dpH = NewHandle(0x900UL, userId, 0xC005, (void *)0);
    unsigned short dpBase = blockAddr(dpH);

    QDStartUp(dpBase, 0, 0xA0, userId);
    EMStartUp((unsigned short)(dpBase + 0x100), 0x14, 0, 0,
              0x14F, 0xC7, userId);
    SchStartUp();
    WindStartUp(userId);

    // Zero the parm block, then set only the fields we want non-zero.
    {
        unsigned char *p = (unsigned char *)&gWp;
        for (unsigned short i = 0; i < sizeof gWp; i++) {
            p[i] = 0;
        }
    }
    gWp.paramLength = (unsigned short)sizeof gWp;
    // fVis+fMove only — fTitle requires Font Manager startup (FMStartUp
    // with proper DP allocation) which is a TODO for the full ORCA-
    // style desktop init.  wTitle is still set to prove the new
    // R_W65816_BANK16 reloc produces the correct bank byte at runtime
    // (even though WM doesn't dereference it without fTitle).
    gWp.wFrameBits  = fVis | fMove;
    gWp.wTitle      = gTitle;
    gWp.wMaxHeight  = 200;
    gWp.wMaxWidth   = 320;
    gWp.wPosition.v1 = 40;  gWp.wPosition.h1 = 30;
    gWp.wPosition.v2 = 140; gWp.wPosition.h2 = 290;
    gWp.wPlane      = (void *)-1L;

    void *win = NewWindow(&gWp);
    if (win) {
        SetPort(win);
        ShowWindow(win);
        MoveTo(20, 30);
        DrawString(gMsg);
    }

    // Brief visible linger before checking events (so snapshot demos can
    // capture the window).  Then wait for a real keypress.
    for (volatile unsigned long s = 0; s < 400000UL; s++) { }

    short evt[8];
    while (1) {
        if (GetNextEvent(0xFFFF, evt)) {
            break;
        }
    }

    SysBeep();
    *(volatile unsigned char *)0x70 = 0x99;
    return 0;
}
