// The MIT License (MIT)
//
// Copyright (C) 2026 Scott Duensing
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

// dvxWgt.h  -- Widget system for DVX GUI
//
// A retained-mode widget toolkit layered on top of the DVX window manager.
// Widgets form a tree (parent-child via firstChild/lastChild/nextSibling
// pointers) rooted at a per-window VBox container. Layout is automatic:
// the engine measures minimum sizes bottom-up, then allocates space top-down
// using a flexbox-like algorithm with weights for extra-space distribution.
//
// Design decisions and rationale:
//
// - Generic WidgetT with void *data: core knows nothing about individual
//   widget types. Each widget DXE allocates and manages its own private
//   data struct via w->data. The wclass vtable provides polymorphic
//   dispatch for paint, layout, events, and all type-specific operations.
//   No widget-specific enums, structs, or union members in this header.
//
// - Dynamic type IDs: wgtRegisterClass() returns a runtime ID at load
//   time. No compile-time enum. Adding a new widget requires zero core
//   changes -- just drop a .wgt DXE in the widgets directory.
//
// - Tagged size values (wgtPixels/wgtChars/wgtPercent): encoding the unit
//   in the high bits of a single int32_t avoids extra struct fields for
//   unit type and lets size hints be passed as plain integers. The 30-bit
//   value range (up to ~1 billion) is more than sufficient for pixel counts.
//
// - Tree linkage uses firstChild/lastChild/nextSibling (no prevSibling):
//   this halves the pointer overhead per widget and insertion/removal is
//   still O(n) in the worst case, which is acceptable given typical tree
//   depths of 5-10 nodes.
#ifndef DVX_WGT_H
#define DVX_WGT_H

#include "dvxTypes.h"

#include <stdlib.h>
#include <time.h>

// Forward declarations
struct AppContextT;
struct WidgetClassT;

// ============================================================
// Size specifications
// ============================================================
//
// Tagged size values encode both a unit type and a numeric value in a
// single int32_t. The top 2 bits select the unit (pixels, character widths,
// or percentage of parent), and the low 30 bits hold the numeric value.
// A raw 0 means "auto" (use the widget's natural/minimum size).
//
// This encoding avoids a separate enum field for the unit type, keeping
// size hints as simple scalar assignments: w->minW = wgtChars(40);
// The wgtResolveSize() function in the layout engine decodes these tagged
// values back into pixel counts using the font metrics and parent dimensions.

#define WGT_SIZE_TYPE_MASK  0xC0000000
#define WGT_SIZE_VAL_MASK   0x3FFFFFFF
#define WGT_SIZE_PIXELS     0x00000000
#define WGT_SIZE_CHARS      0x40000000
#define WGT_SIZE_PERCENT    0x80000000

// Standard "fill remaining space" weight for VBox/HBox children.
// A widget with WGT_WEIGHT_FILL gets one share of extra space; use
// 2*, 3* etc. to give siblings proportionally more space.
#define WGT_WEIGHT_FILL     100

#define wgtPixels(v)  ((int32_t)(WGT_SIZE_PIXELS  | ((uint32_t)(v) & WGT_SIZE_VAL_MASK)))
#define wgtChars(v)   ((int32_t)(WGT_SIZE_CHARS   | ((uint32_t)(v) & WGT_SIZE_VAL_MASK)))
#define wgtPercent(v) ((int32_t)(WGT_SIZE_PERCENT  | ((uint32_t)(v) & WGT_SIZE_VAL_MASK)))

// Widget type IDs are assigned dynamically by wgtRegisterClass() at
// runtime.  There is no compile-time enum.  Each widget DXE stores
// its assigned ID(s) in file-scope globals.

// ============================================================
// ListView column types (used by ListView API in widgetListView.h)
// ============================================================

typedef enum {
    ListViewAlignLeftE,
    ListViewAlignCenterE,
    ListViewAlignRightE
} ListViewAlignE;

typedef enum {
    ListViewSortNoneE,
    ListViewSortAscE,
    ListViewSortDescE
} ListViewSortE;

typedef struct {
    const char      *title;
    int32_t          width;     // tagged size (wgtPixels/wgtChars/wgtPercent, 0 = auto)
    ListViewAlignE   align;
} ListViewColT;

// ============================================================
// Alignment enum
// ============================================================
//
// Controls main-axis alignment of children within a container.
// HBox: AlignStartE=left, AlignCenterE=center, AlignEndE=right
// VBox: AlignStartE=top,  AlignCenterE=center, AlignEndE=bottom

typedef enum {
    AlignStartE,
    AlignCenterE,
    AlignEndE
} WidgetAlignE;

// ============================================================
// Widget-specific enums and types used by the public API
// ============================================================
//
// These types are referenced by application code through the wgtApi
// function table. Widget-private types (data structs) are defined
// in their respective widget .c files.

// ============================================================
// Widget class dispatch table
// ============================================================
//
// Each widget type has a WidgetClassT that defines its behavior.
// Built-in classes are static const; dynamically registered classes
// (from DXE plugins) are stored in the mutable region of
// widgetClassTable[].
//
// The handlers[] array provides ABI-stable dispatch: method IDs are
// fixed constants that never change. Adding new methods appends new
// IDs without shifting existing ones, so widget DXEs compiled against
// an older DVX version continue to work unmodified. The version field
// is checked at wgtRegisterClass() time to catch DXEs compiled against
// an incompatible layout.
//
// Flags encode static properties checked by the framework:
//   FOCUSABLE         -- can receive keyboard focus (Tab navigation)
//   HORIZ_CONTAINER   -- lays out children horizontally (vs. vertical)
//   PAINTS_CHILDREN   -- widget handles child rendering itself
//   NO_HIT_RECURSE    -- hit testing stops here, no child recursion

// Flags encode static properties checked by the framework.
// Widgets set these in their WidgetClassT definition.
#define WCLASS_FOCUSABLE           0x00000001
#define WCLASS_HORIZ_CONTAINER     0x00000004
#define WCLASS_PAINTS_CHILDREN     0x00000008
#define WCLASS_NO_HIT_RECURSE      0x00000010
#define WCLASS_FOCUS_FORWARD       0x00000020  // accel hit forwards focus to next focusable
#define WCLASS_HAS_POPUP           0x00000040  // has dropdown popup overlay
#define WCLASS_SCROLLABLE          0x00000080  // accepts mouse wheel events
#define WCLASS_SCROLL_CONTAINER    0x00000100  // scroll container (ScrollPane)
#define WCLASS_NEEDS_POLL          0x00000200  // needs periodic polling (AnsiTerm comms)
#define WCLASS_SWALLOWS_TAB        0x00000400  // Tab key goes to widget, not focus nav
#define WCLASS_RELAYOUT_ON_SCROLL  0x00000800  // full relayout on scrollbar drag
#define WCLASS_PRESS_RELEASE       0x00001000  // click = press+release (Button, ImageButton)
#define WCLASS_ACCEL_WHEN_HIDDEN   0x00002000  // accel matching works even when invisible
#define WCLASS_HAS_TEXT            0x00004000  // first field of w->data is (const char *), owned by widget (strdup/free)

// Method IDs -- stable ABI, never reorder, never reuse.
// New methods are appended at the end with the next sequential ID.
#define WGT_METHOD_PAINT               0  // void (w, d, ops, font, colors)
#define WGT_METHOD_PAINT_OVERLAY       1  // void (w, d, ops, font, colors)
#define WGT_METHOD_CALC_MIN_SIZE       2  // void (w, font)
#define WGT_METHOD_LAYOUT              3  // void (w, font)
#define WGT_METHOD_GET_LAYOUT_METRICS  4  // void (w, font, pad, gap, extraTop, borderW)
#define WGT_METHOD_ON_MOUSE            5  // void (w, root, vx, vy)
#define WGT_METHOD_ON_KEY              6  // void (w, key, mod)
#define WGT_METHOD_ON_ACCEL_ACTIVATE   7  // void (w, root)
#define WGT_METHOD_DESTROY             8  // void (w)
#define WGT_METHOD_ON_CHILD_CHANGED    9  // void (parent, child)
#define WGT_METHOD_GET_TEXT           10  // const char *(w)
#define WGT_METHOD_SET_TEXT           11  // void (w, text)
#define WGT_METHOD_CLEAR_SELECTION    12  // bool (w)
#define WGT_METHOD_CLOSE_POPUP        13  // void (w)
#define WGT_METHOD_GET_POPUP_RECT     14  // void (w, font, contentH, popX, popY, popW, popH)
#define WGT_METHOD_ON_DRAG_UPDATE     15  // void (w, root, x, y)
#define WGT_METHOD_ON_DRAG_END        16  // void (w, root, x, y)
#define WGT_METHOD_GET_CURSOR_SHAPE   17  // int32_t (w, vx, vy)
#define WGT_METHOD_POLL               18  // void (w, win)
#define WGT_METHOD_QUICK_REPAINT      19  // int32_t (w, outY, outH)
#define WGT_METHOD_SCROLL_CHILD_INTO_VIEW 20  // void (parent, child)
#define WGT_METHOD_ON_BLUR            21  // void (w) -- widget lost keyboard focus
#define WGT_METHOD_COUNT              22
#define WGT_METHOD_MAX                32  // room for future methods

#define WGT_CLASS_VERSION              1  // bump on breaking ABI change

typedef struct WidgetClassT {
    uint32_t version;
    uint32_t flags;
    void    *handlers[WGT_METHOD_MAX];
} WidgetClassT;

// ============================================================
// Widget structure
// ============================================================

#define MAX_WIDGET_NAME 32

typedef struct WidgetT {
    int32_t                    type;  // assigned by wgtRegisterClass()
    // wclass points to the vtable for this widget type. Looked up once at
    // creation from widgetClassTable[type]. This avoids a switch on type
    // in every paint/layout/event dispatch  -- the cost is one pointer per
    // widget, which is negligible.
    const struct WidgetClassT *wclass;
    char                       name[MAX_WIDGET_NAME];

    // Tree linkage
    struct WidgetT  *parent;
    struct WidgetT  *firstChild;
    struct WidgetT  *lastChild;
    struct WidgetT  *nextSibling;
    WindowT         *window;

    // Computed geometry (relative to window content area)
    int32_t          x;
    int32_t          y;
    int32_t          w;
    int32_t          h;

    // Computed minimum size  -- set bottom-up by calcMinSize during layout.
    // These represent the smallest possible size for this widget (including
    // its children if it's a container). The layout engine uses these as
    // the starting point for space allocation.
    int32_t          calcMinW;
    int32_t          calcMinH;

    // Size hints (tagged: wgtPixels/wgtChars/wgtPercent, 0 = auto).
    // These are set by the application and influence the layout engine:
    // minW/minH override calcMinW/H if larger, maxW/maxH clamp the final
    // size, and prefW/prefH request a specific size (layout may override).
    int32_t          minW;
    int32_t          minH;
    int32_t          maxW;       // 0 = no limit
    int32_t          maxH;
    int32_t          prefW;      // preferred size, 0 = auto
    int32_t          prefH;
    // weight controls how extra space beyond minimum is distributed among
    // siblings in a VBox/HBox. weight=0 means fixed size (no stretching),
    // weight=100 is the default for flexible widgets. A widget with
    // weight=200 gets twice as much extra space as one with weight=100.
    int32_t          weight;     // extra-space distribution (0 = fixed, 100 = normal)

    // Container properties
    WidgetAlignE     align;      // main-axis alignment for children
    int32_t          spacing;    // tagged size for spacing between children (0 = default)
    int32_t          padding;    // tagged size for internal padding (0 = default)

    // Colors (0 = use color scheme defaults)
    uint32_t         fgColor;
    uint32_t         bgColor;

    // State
    bool             visible;
    bool             enabled;
    bool             readOnly;
    bool             swallowTab;  // Tab key goes to widget, not focus nav
    bool             paintDirty;  // needs repaint (set by wgtInvalidatePaint)
    bool             childDirty;  // a descendant needs repaint (for WCLASS_PAINTS_CHILDREN)
    bool             pressed;     // WCLASS_PRESS_RELEASE: set while button is pressed
    char             accelKey;     // lowercase accelerator character, 0 if none

    // Content offset: mouse event coordinates are adjusted by this amount
    // so callbacks receive content-relative coords (e.g. canvas subtracts
    // its border so (0,0) = first drawable pixel, matching VB3 behavior).
    int32_t          contentOffX;
    int32_t          contentOffY;

    // User data and callbacks.  These fire for ALL widget types from the
    // central event dispatcher, not from individual widget handlers.
    // Type-specific handlers (e.g. button press animation, listbox
    // selection) run first, then these universal callbacks fire.
    void            *userData;
    void            *data;        // widget-private data (allocated by widget DXE)
    const char      *tooltip;     // tooltip text (NULL = none, caller owns string)
    MenuT           *contextMenu; // right-click context menu (NULL = none, caller owns)
    void           (*onClick)(struct WidgetT *w);
    void           (*onDblClick)(struct WidgetT *w);
    void           (*onChange)(struct WidgetT *w);
    void           (*onFocus)(struct WidgetT *w);
    void           (*onBlur)(struct WidgetT *w);
    void           (*onKeyPress)(struct WidgetT *w, int32_t keyAscii);
    void           (*onKeyDown)(struct WidgetT *w, int32_t keyCode, int32_t shift);
    void           (*onKeyUp)(struct WidgetT *w, int32_t keyCode, int32_t shift);
    void           (*onMouseDown)(struct WidgetT *w, int32_t button, int32_t x, int32_t y);
    void           (*onMouseUp)(struct WidgetT *w, int32_t button, int32_t x, int32_t y);
    void           (*onMouseMove)(struct WidgetT *w, int32_t button, int32_t x, int32_t y);
    void           (*onScroll)(struct WidgetT *w, int32_t delta);
    bool           (*onValidate)(struct WidgetT *w);  // return false to cancel write

} WidgetT;

// ============================================================
// Typed dispatch helpers
// ============================================================
//
// Each wclsFoo() inline function extracts a handler by stable method ID,
// casts it to the correct function pointer type, and calls it with a
// NULL check. This gives callers type-safe dispatch with the same
// codegen as a direct struct field call.

static inline bool wclsHas(const WidgetT *w, int32_t methodId) {
    return w->wclass && w->wclass->handlers[methodId] != NULL;
}


static inline void wclsPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
    typedef void (*FnT)(WidgetT *, DisplayT *, const BlitOpsT *, const BitmapFontT *, const ColorSchemeT *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_PAINT] : NULL;
    if (fn) { fn(w, d, ops, font, colors); }
}


static inline void wclsPaintOverlay(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
    typedef void (*FnT)(WidgetT *, DisplayT *, const BlitOpsT *, const BitmapFontT *, const ColorSchemeT *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_PAINT_OVERLAY] : NULL;
    if (fn) { fn(w, d, ops, font, colors); }
}


static inline void wclsCalcMinSize(WidgetT *w, const BitmapFontT *font) {
    typedef void (*FnT)(WidgetT *, const BitmapFontT *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_CALC_MIN_SIZE] : NULL;
    if (fn) { fn(w, font); }
}


static inline void wclsLayout(WidgetT *w, const BitmapFontT *font) {
    typedef void (*FnT)(WidgetT *, const BitmapFontT *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_LAYOUT] : NULL;
    if (fn) { fn(w, font); }
}


static inline void wclsGetLayoutMetrics(const WidgetT *w, const BitmapFontT *font, int32_t *pad, int32_t *gap, int32_t *extraTop, int32_t *borderW) {
    typedef void (*FnT)(const WidgetT *, const BitmapFontT *, int32_t *, int32_t *, int32_t *, int32_t *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_GET_LAYOUT_METRICS] : NULL;
    if (fn) { fn(w, font, pad, gap, extraTop, borderW); }
}


static inline void wclsOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
    typedef void (*FnT)(WidgetT *, WidgetT *, int32_t, int32_t);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_ON_MOUSE] : NULL;
    if (fn) { fn(w, root, vx, vy); }
}


static inline void wclsOnKey(WidgetT *w, int32_t key, int32_t mod) {
    typedef void (*FnT)(WidgetT *, int32_t, int32_t);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_ON_KEY] : NULL;
    if (fn) { fn(w, key, mod); }
}


// Dispatched on the widget LOSING keyboard focus, before the app-level
// onBlur callback.  Editing widgets (e.g. the spinner) commit/clamp their
// in-progress text here.  Must be called from every focus-loss transition
// (mouse focus change, Tab/accel navigation, window blur, wgtSetFocused).
static inline void wclsOnBlur(WidgetT *w) {
    typedef void (*FnT)(WidgetT *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_ON_BLUR] : NULL;
    if (fn) { fn(w); }
}


static inline void wclsOnAccelActivate(WidgetT *w, WidgetT *root) {
    typedef void (*FnT)(WidgetT *, WidgetT *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_ON_ACCEL_ACTIVATE] : NULL;
    if (fn) { fn(w, root); }
}


// Default destroy behavior: if the widget's class does not supply an
// explicit WGT_METHOD_DESTROY handler, free w->data automatically (and
// the text-at-offset-0 first, if WCLASS_HAS_TEXT is set). This removes
// the need for one-line free(w->data) destroy handlers in every widget.
// Widget classes that need to free multiple allocations or unregister
// from global lists must still provide an explicit handler.
static inline void wclsDestroy(WidgetT *w) {
    typedef void (*FnT)(WidgetT *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_DESTROY] : NULL;
    if (fn) {
        fn(w);
        return;
    }
    if (w->data) {
        if (w->wclass && (w->wclass->flags & WCLASS_HAS_TEXT)) {
            free(*(char **)w->data);
        }
        free(w->data);
        w->data = NULL;
    }
}


static inline void wclsOnChildChanged(WidgetT *parent, WidgetT *child) {
    typedef void (*FnT)(WidgetT *, WidgetT *);
    FnT fn = parent->wclass ? (FnT)parent->wclass->handlers[WGT_METHOD_ON_CHILD_CHANGED] : NULL;
    if (fn) { fn(parent, child); }
}


static inline const char *wclsGetText(const WidgetT *w) {
    typedef const char *(*FnT)(const WidgetT *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_GET_TEXT] : NULL;
    return fn ? fn(w) : "";
}


static inline void wclsSetText(WidgetT *w, const char *text) {
    typedef void (*FnT)(WidgetT *, const char *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_SET_TEXT] : NULL;
    if (fn) { fn(w, text); }
}


static inline bool wclsClearSelection(WidgetT *w) {
    typedef bool (*FnT)(WidgetT *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_CLEAR_SELECTION] : NULL;
    return fn ? fn(w) : false;
}


static inline void wclsClosePopup(WidgetT *w) {
    typedef void (*FnT)(WidgetT *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_CLOSE_POPUP] : NULL;
    if (fn) { fn(w); }
}


static inline void wclsGetPopupRect(const WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH) {
    typedef void (*FnT)(const WidgetT *, const BitmapFontT *, int32_t, int32_t *, int32_t *, int32_t *, int32_t *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_GET_POPUP_RECT] : NULL;
    if (fn) { fn(w, font, contentH, popX, popY, popW, popH); }
}


static inline void wclsOnDragUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
    typedef void (*FnT)(WidgetT *, WidgetT *, int32_t, int32_t);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_ON_DRAG_UPDATE] : NULL;
    if (fn) { fn(w, root, x, y); }
}


static inline void wclsOnDragEnd(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
    typedef void (*FnT)(WidgetT *, WidgetT *, int32_t, int32_t);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_ON_DRAG_END] : NULL;
    if (fn) { fn(w, root, x, y); }
}


static inline int32_t wclsGetCursorShape(const WidgetT *w, int32_t vx, int32_t vy) {
    typedef int32_t (*FnT)(const WidgetT *, int32_t, int32_t);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_GET_CURSOR_SHAPE] : NULL;
    return fn ? fn(w, vx, vy) : 0;
}


static inline void wclsPoll(WidgetT *w, WindowT *win) {
    typedef void (*FnT)(WidgetT *, WindowT *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_POLL] : NULL;
    if (fn) { fn(w, win); }
}


static inline int32_t wclsQuickRepaint(WidgetT *w, int32_t *outY, int32_t *outH) {
    typedef int32_t (*FnT)(WidgetT *, int32_t *, int32_t *);
    FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_QUICK_REPAINT] : NULL;
    return fn ? fn(w, outY, outH) : 0;
}


static inline void wclsScrollChildIntoView(WidgetT *parent, const WidgetT *child) {
    typedef void (*FnT)(WidgetT *, const WidgetT *);
    FnT fn = parent->wclass ? (FnT)parent->wclass->handlers[WGT_METHOD_SCROLL_CHILD_INTO_VIEW] : NULL;
    if (fn) { fn(parent, child); }
}


// ============================================================
// Window integration
// ============================================================

// Initialize the widget system for a window. Creates a root VBox container
// that fills the window's content area, and installs callback handlers
// (onPaint, onMouse, onKey, onResize) that dispatch events to the widget
// tree. After this call, the window is fully managed by the widget system
//  -- the application builds its UI by adding child widgets to the returned
// root container. The window's userData is set to the AppContextT pointer.
WidgetT *wgtInitWindow(struct AppContextT *ctx, WindowT *win);

// Default window focus handlers installed by wgtInitWindow into
// win->onBlur / win->onFocus.  widgetOnBlur clears the widget focus and
// fires the focused widget's onBlur (LostFocus + data write-back);
// widgetOnFocus marks the window's minimized-icon thumbnail dirty.  Apps
// that replace win->onBlur/onFocus must call these to keep the widget
// focus bridge working.
void widgetOnBlur(WindowT *win);
void widgetOnFocus(WindowT *win);

// ============================================================
// Operations
// ============================================================

// Walk from any widget up the tree to the root, then retrieve the
// AppContextT stored in the window's userData. This lets any widget
// access the full application context without passing it through every
// function call.
struct AppContextT *wgtGetContext(const WidgetT *w);

// Mark a widget as needing both re-layout (measure + position) and
// repaint. Propagates upward to ancestors since a child's size change
// can affect parent layout. Use this after structural changes (adding/
// removing children, changing text that affects size).
void wgtInvalidate(WidgetT *w);

// Mark a widget as needing repaint only, without re-layout. Use this
// for visual-only changes that don't affect geometry (e.g. checkbox
// toggle, selection highlight change, cursor blink).
void wgtInvalidatePaint(WidgetT *w);

// Set/get widget text (label, button, textInput, etc.)
void wgtSetText(WidgetT *w, const char *text);
const char *wgtGetText(const WidgetT *w);

// Enable/disable a widget
void wgtSetEnabled(WidgetT *w, bool enabled);

// Set read-only mode (allows scrolling/selection but blocks editing)
void wgtSetReadOnly(WidgetT *w, bool readOnly);

// Set/get keyboard focus.  wgtSetFocused refuses disabled or hidden widgets.
void wgtSetFocused(WidgetT *w);
WidgetT *wgtGetFocused(void);

// Move keyboard focus to w (NULL clears it): clears the previous widget's
// selection, repaints both, and fires the blur/focus callbacks.  Returns
// false if a callback destroyed widgets -- do not touch w or the previous
// widget afterwards.  This is the single focus-transition path; use it
// instead of assigning the focused widget directly.
bool widgetTransferFocus(WidgetT *w);

// Fire the blur/focus callbacks for a transition that has already been
// recorded (prev lost focus, next gained it) without touching selection or
// repaint state.  Same return contract as widgetTransferFocus.
bool widgetFireFocusChange(WidgetT *prev, WidgetT *next);

// Show/hide a widget
void wgtSetVisible(WidgetT *w, bool visible);

// Set a widget's name (for lookup via wgtFind)
void wgtSetName(WidgetT *w, const char *name);

// Find a widget by name (searches the subtree rooted at root)
WidgetT *wgtFind(WidgetT *root, const char *name);

// Destroy a widget and all its children (removes from parent)
void wgtDestroy(WidgetT *w);

// Destroy all children of w (w itself stays). Each child is fully
// destroyed (fires onChildChanged, clears global refs), so use this
// when rebuilding a container's contents to avoid leaking old subtrees.
void wgtDestroyChildren(WidgetT *w);

// Default window resize handler installed by wgtInitWindow. Re-evaluates
// scrollbars and relayouts the widget tree. Call this from custom onResize
// handlers to chain to the widget system before firing application events.
void widgetOnResize(WindowT *win, int32_t newW, int32_t newH);

// ============================================================
// Tooltip
// ============================================================

// Set tooltip text for a widget (NULL to remove).
// Caller owns the string  -- it must outlive the widget.
void wgtSetTooltip(WidgetT *w, const char *text);

// ============================================================
// Debug
// ============================================================

// Draw borders around layout containers in ugly colors
void wgtSetDebugLayout(struct AppContextT *ctx, bool enabled);

// ============================================================
// Dynamic widget registration
// ============================================================

// Register a new widget class at runtime. Returns the assigned type ID
// Appends wclass to widgetClassTable and returns the assigned type ID.
// The WidgetClassT must remain valid for the lifetime of the process
// (typically a static const in the registering DXE).
int32_t wgtRegisterClass(const WidgetClassT *wclass);

// ============================================================
// Layout (called internally; available for manual trigger)
// ============================================================

// Decode a tagged size value (WGT_SIZE_PIXELS/CHARS/PERCENT) into a
// concrete pixel count. For CHARS, multiplies by charWidth; for PERCENT,
// computes the fraction of parentSize. Returns 0 for a raw 0 input
// (meaning "auto").
int32_t wgtResolveSize(int32_t taggedSize, int32_t parentSize, int32_t charWidth);

// Execute the full two-pass layout algorithm on the widget tree:
//   Pass 1 (bottom-up): calcMinSize on every widget to compute minimum sizes.
//   Pass 2 (top-down): allocate space within availW/availH, distributing
//   extra space according to weights and respecting min/max constraints.
// Normally called automatically by the paint handler; exposed here for
// cases where layout must be forced before the next paint (e.g. dvxFitWindow).
void wgtLayout(WidgetT *root, int32_t availW, int32_t availH, const BitmapFontT *font);

// Paint the entire widget tree by depth-first traversal. Each widget's
// clip rect is set to its bounds before calling its paint function.
// Overlays (dropdown popups, tooltips) are painted in a second pass
// after the main tree so they render on top of everything.
void wgtPaint(WidgetT *root, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, bool fullRepaint);

// ============================================================
// Widget API registry
// ============================================================
//
// Each widget DXE registers a small API struct under a name
// during wgtRegister().  Callers retrieve it via wgtGetApi()
// and cast to the widget-specific API type.  Per-widget headers
// (e.g. widgetButton.h) provide typed accessors and convenience
// macros so callers don't need manual casts.
//
// This replaces the monolithic WidgetApiT -- adding a new widget
// requires zero changes to dvxWidget.h.

void wgtRegisterApi(const char *name, const void *api);
const void *wgtGetApi(const char *name);

// ============================================================
// Widget interface descriptors
// ============================================================
//
// Each widget DXE can register an interface descriptor that
// describes its BASIC-facing properties, methods, and events.
// The form runtime and IDE use these for generic dispatch and
// property panel enumeration.

// Property data types
#define WGT_IFACE_STRING  0
#define WGT_IFACE_INT     1
#define WGT_IFACE_BOOL    2
#define WGT_IFACE_FLOAT   3
#define WGT_IFACE_ENUM    4  // int32_t with named values

// Method calling conventions (how the form runtime marshals args)
#define WGT_SIG_VOID       0   // void fn(WidgetT *)
#define WGT_SIG_INT        1   // void fn(WidgetT *, int32_t)
#define WGT_SIG_BOOL       2   // void fn(WidgetT *, bool)
#define WGT_SIG_STR        3   // void fn(WidgetT *, const char *)
#define WGT_SIG_INT_INT    4   // void fn(WidgetT *, int32_t, int32_t)
#define WGT_SIG_INT_BOOL   5   // void fn(WidgetT *, int32_t, bool)
#define WGT_SIG_RET_INT    6   // int32_t fn(const WidgetT *)
#define WGT_SIG_RET_BOOL   7   // bool fn(const WidgetT *)
#define WGT_SIG_RET_BOOL_INT 8  // bool fn(const WidgetT *, int32_t)
#define WGT_SIG_INT3         9  // void fn(WidgetT *, int32_t, int32_t, int32_t)
#define WGT_SIG_INT4         10 // void fn(WidgetT *, int32_t, int32_t, int32_t, int32_t)
#define WGT_SIG_RET_INT_INT_INT   11 // int32_t fn(const WidgetT *, int32_t, int32_t)
#define WGT_SIG_INT_INT_STR  12 // void fn(WidgetT *, int32_t, int32_t, const char *)
#define WGT_SIG_INT5         13 // void fn(WidgetT *, int32_t, int32_t, int32_t, int32_t, int32_t)
#define WGT_SIG_RET_STR_INT 14 // const char *fn(const WidgetT *, int32_t)
#define WGT_SIG_RET_STR_INT_INT  15 // const char *fn(const WidgetT *, int32_t, int32_t)
#define WGT_SIG_STR_BOOL_BOOL   16 // bool fn(WidgetT *, const char *, bool, bool)
#define WGT_SIG_STR_STR_BOOL    17 // int32_t fn(WidgetT *, const char *, const char *, bool)
#define WGT_SIG_RET_STR         18 // const char *fn(const WidgetT *)
#define WGT_SIG_STR_INT         19 // void fn(WidgetT *, const char *, int32_t)
#define WGT_SIG_INT_STR         20 // void fn(WidgetT *, int32_t, const char *)
#define WGT_SIG_STR_STR         21 // void fn(WidgetT *, const char *, const char *)
#define WGT_SIG_STR_BOOL        22 // void fn(WidgetT *, const char *, bool)

// Property descriptor
typedef struct {
    const char  *name;      // BASIC property name (e.g. "Caption", "Value")
    uint8_t      type;      // WGT_IFACE_*
    void        *getFn;     // getter function pointer (NULL if write-only)
    void        *setFn;     // setter function pointer (NULL if read-only)
    const char **enumNames; // WGT_IFACE_ENUM only: NULL-terminated array of value names
} WgtPropDescT;

// Method descriptor
typedef struct {
    const char *name;      // BASIC method name (e.g. "Clear", "SetFocus")
    uint8_t     sig;       // WGT_SIG_*
    void       *fn;        // function pointer
} WgtMethodDescT;

// Event descriptor
typedef struct {
    const char *name;      // event name (e.g. "Click", "Change")
} WgtEventDescT;

// Common events implicitly available on all widgets.
// The form runtime wires these callbacks on every control.
// Widget descriptors only need to list EXTRA events beyond these.
//   Click, DblClick, Change, GotFocus, LostFocus

// Create function signature types for design-time / runtime instantiation.
// The create function is always the first slot in the widget API struct.
typedef enum {
    WGT_CREATE_PARENT = 0,         // fn(parent)
    WGT_CREATE_PARENT_TEXT,        // fn(parent, const char *text)
    WGT_CREATE_PARENT_INT,         // fn(parent, int32_t)
    WGT_CREATE_PARENT_INT_INT,     // fn(parent, int32_t, int32_t)
    WGT_CREATE_PARENT_INT_INT_INT, // fn(parent, int32_t, int32_t, int32_t)
    WGT_CREATE_PARENT_INT_BOOL,    // fn(parent, int32_t, bool)
    WGT_CREATE_PARENT_BOOL,        // fn(parent, bool)
    WGT_CREATE_PARENT_DATA,        // fn(parent, data, w, h, pitch) -- not auto-creatable
} WgtCreateSigE;

#define WGT_MAX_CREATE_ARGS 3

// Widget interface descriptor (registered by each .wgt)
typedef struct {
    // Fields are ordered by alignment to minimize struct padding.
    const char             *basName;         // VB-style name (e.g. "CommandButton"), or NULL
    const WgtPropDescT    *props;            // type-specific properties
    int32_t                 propCount;
    const WgtMethodDescT  *methods;          // type-specific methods
    int32_t                 methodCount;
    const WgtEventDescT   *events;           // extra events beyond common set
    int32_t                 eventCount;
    int32_t                 createArgs[WGT_MAX_CREATE_ARGS]; // default numeric args
    const char             *defaultEvent;     // default event name (e.g. "Click")
    const char             *namePrefix;       // auto-name prefix (NULL = use basName)
    uint8_t                 createSig;        // WgtCreateSigE: how to call create fn
    bool                    isContainer;      // can hold child widgets
} WgtIfaceT;

// Register/retrieve interface descriptors by widget type name.
void wgtRegisterIface(const char *name, const WgtIfaceT *iface);
const WgtIfaceT *wgtGetIface(const char *name);

// Case-insensitive lookup of a property descriptor on iface by name.
// Single source of truth for the runtime and IDE property-override rule.
// Returns NULL if iface is NULL or no property matches.
const WgtPropDescT *wgtIfaceFindProp(const WgtIfaceT *iface, const char *propName);

// Find a widget type name by its VB-style name (e.g. "CommandButton" -> "button").
// Returns NULL if no widget has that basName. Case-insensitive.
const char *wgtFindByBasName(const char *basName);

// Enumerate all registered widget interfaces.
int32_t wgtIfaceCount(void);
const WgtIfaceT *wgtIfaceAt(int32_t idx, const char **outName);

// Get/set the .wgt file path for a registered widget (set by loader).
const char *wgtIfaceGetPath(const char *name);
void wgtIfaceSetPath(const char *name, const char *path);

// Get the 1-based index of this widget within its .wgt file.
// Used to construct suffixed resource names (e.g. "name-2", "icon16-2").
int32_t wgtIfaceGetPathIndex(const char *name);

#endif // DVX_WGT_H
