// 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.

// dvxWgtP.h -- Plugin API for DVX widget DXE modules
//
// Included by widget .c files to access core infrastructure.
// Provides: widget allocation, tree ops, class table, shared
// interaction state, scrollbar helpers, layout helpers, and
// common utility functions.
//
// Does NOT contain any per-widget prototypes.  Each widget's
// internal functions (paint, onMouse, onKey, calcMinSize, etc.)
// are private to its own .c file and referenced only through
// the WidgetClassT vtable.
#ifndef DVX_WGT_P_H
#define DVX_WGT_P_H

#include "dvxWgt.h"
#include "dvxApp.h"
#include "dvxDraw.h"
#include "dvxWm.h"
#include "dvxVideo.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "dvxMem.h"

// ============================================================
// Widget class table
// ============================================================

// stb_ds dynamic array.  Each widget DXE appends its class(es)
// via wgtRegisterClass() during wgtRegister().
extern const WidgetClassT **widgetClassTable;

// ============================================================
// Validation macros
// ============================================================

#define VALIDATE_WIDGET(w, wtype, retval) \
    do { if (!(w) || (w)->type != (wtype)) { return (retval); } } while (0)

#define VALIDATE_WIDGET_VOID(w, wtype) \
    do { if (!(w) || (w)->type != (wtype)) { return; } } while (0)

// Cursor shape IDs (CURSOR_*) come from dvxCur.h via dvxApp.h.

// ============================================================
// Keyboard modifier flags
// ============================================================

#define KEY_MOD_SHIFT  0x03
#define KEY_MOD_CTRL   0x04
#define KEY_MOD_ALT    0x08

// ============================================================
// Core layout constants
// ============================================================

#define DEFAULT_SPACING  4
#define DEFAULT_PADDING  4
#define SB_MIN_THUMB     14
#define WGT_SB_W         14

// ============================================================
// Inline helpers
// ============================================================

static inline int32_t clampInt(int32_t val, int32_t lo, int32_t hi) {
    if (val < lo) { return lo; }
    if (val > hi) { return hi; }
    return val;
}


static inline void drawTextEmbossed(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, const char *text, const ColorSchemeT *colors) {
    drawText(d, ops, font, x + 1, y + 1, text, colors->windowHighlight, 0, false);
    drawText(d, ops, font, x, y, text, colors->windowShadow, 0, false);
}


static inline void drawTextAccelEmbossed(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, const char *text, const ColorSchemeT *colors) {
    drawTextAccel(d, ops, font, x + 1, y + 1, text, colors->windowHighlight, 0, false);
    drawTextAccel(d, ops, font, x, y, text, colors->windowShadow, 0, false);
}

// ============================================================
// Shared interaction state (defined in widgetCore.c)
// ============================================================

extern bool     sCursorBlinkOn;
extern clock_t  sDblClickTicks;
extern bool     sDebugLayout;
extern WidgetT *sClosedPopup;
extern WidgetT *sFocusedWidget;
extern WidgetT *sKeyPressedBtn;
extern WidgetT *sOpenPopup;
extern WidgetT *sDragWidget;     // widget being dragged (any drag type)
extern WidgetT **sPollWidgets;    // stb_ds dynamic array
// Incremented every time a widget is destroyed.  Dispatch code snapshots
// it before invoking a user callback; a changed value afterwards means
// the callback destroyed widgets, so cached WidgetT pointers (the hit
// widget, the focused widget) may be dangling and must not be touched.
extern uint32_t sWidgetGen;
extern void   (*sCursorBlinkFn)(void);

// ============================================================
// Core widget functions (widgetCore.c)
// ============================================================

// Tree manipulation
void widgetAddChild(WidgetT *parent, WidgetT *child);
void widgetRemoveChild(WidgetT *parent, WidgetT *child);
void widgetDestroyChildren(WidgetT *w);

// Clears every global interaction pointer that references w and removes w
// from the poll list.  Call for every destroyed node.
void widgetClearReferences(WidgetT *w);

// Clears every global interaction pointer whose widget lives on win.
// Called when a window's destruction is deferred (destroyPending), so no
// drag/popup/key-press dispatch reaches its still-allocated widget tree.
void widgetDetachWindowReferences(WindowT *win);

// Widget-destroy notification.  Subscribers registered here are fired by
// widgetClearReferences for every destroyed widget so a module holding a
// static WidgetT pointer can null it before the widget is freed.  Register
// is idempotent; unregister is a no-op if the callback is absent.
void widgetRegisterDestroyFn(void (*fn)(WidgetT *w));
void widgetUnregisterDestroyFn(void (*fn)(WidgetT *w));

// Allocation
WidgetT *widgetAlloc(WidgetT *parent, int32_t type);

// Undo a successful widgetAlloc when a subsequent data-struct
// allocation fails.  Detaches from parent, removes from the poll
// list, frees the widget struct.  Call only before w->data has
// been set; once data is attached, use wgtDestroy instead.
void     widgetAllocRollback(WidgetT *w);

// Allocate a widget of the given type PLUS a data struct of dataSize
// bytes.  The data struct must begin with a `const char *text` field
// (WCLASS_HAS_TEXT semantics); this field is set to strdup(text) and
// the widget's accelKey is parsed from text.  Other fields in the data
// struct remain zeroed.  Returns NULL on allocation failure.
WidgetT *widgetAllocWithText(WidgetT *parent, int32_t type, size_t dataSize, const char *text);

// Focus management.  widgetTransferFocus / widgetFireFocusChange are
// declared in dvxWgt.h (the app layer's Tab/accelerator handlers use them).
WidgetT *widgetFindNextFocusable(WidgetT *root, WidgetT *after);
WidgetT *widgetFindPrevFocusable(WidgetT *root, WidgetT *before);
WidgetT *widgetFindByAccel(WidgetT *root, char key);

// Utility queries
int32_t widgetCountVisibleChildren(const WidgetT *w);
int32_t widgetFrameBorderWidth(const WidgetT *w);
bool widgetIsFocusable(int32_t type);
bool widgetIsHorizContainer(int32_t type);
bool widgetIsShown(const WidgetT *w);
int32_t multiClickDetect(int32_t vx, int32_t vy);

// Clipboard
void clipboardCopy(const char *text, int32_t len);
const char *clipboardGet(int32_t *outLen);

// Hit testing
WidgetT *widgetHitTest(WidgetT *w, int32_t x, int32_t y);

// Scrollbar helpers.  widgetScrollbarThumb yields a zero-size thumb when
// trackLen or totalSize is <= 0 and clamps scrollPos, so callers need no
// guards of their own.
void widgetScrollbarThumb(int32_t trackLen, int32_t totalSize, int32_t visibleSize, int32_t scrollPos, int32_t *thumbPos, int32_t *thumbSize);
int32_t widgetScrollbarThumbDragScroll(int32_t trackLen, int32_t total, int32_t visible, int32_t relMouse, int32_t maxScroll);

// ============================================================
// Scrollbar drawing and hit testing (widgetScrollbar.c)
// ============================================================

typedef enum {
    ScrollHitNoneE,
    ScrollHitArrowDecE,
    ScrollHitArrowIncE,
    ScrollHitPageDecE,
    ScrollHitPageIncE,
    ScrollHitThumbE
} ScrollHitE;

void widgetDrawScrollbarH(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t sbX, int32_t sbY, int32_t sbW, int32_t totalSize, int32_t visibleSize, int32_t scrollPos);
void widgetDrawScrollbarHEx(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t sbX, int32_t sbY, int32_t sbW, int32_t totalSize, int32_t visibleSize, int32_t scrollPos, int32_t barW);
void widgetDrawScrollbarV(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t sbX, int32_t sbY, int32_t sbH, int32_t totalSize, int32_t visibleSize, int32_t scrollPos);
void widgetDrawScrollbarVEx(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t sbX, int32_t sbY, int32_t sbH, int32_t totalSize, int32_t visibleSize, int32_t scrollPos, int32_t barW);
ScrollHitE widgetScrollbarHitTest(int32_t sbLen, int32_t relPos, int32_t totalSize, int32_t visibleSize, int32_t scrollPos);

// ============================================================
// Layout functions (widgetLayout.c)
// ============================================================

// Resolves a box container's padding/gap (tagged sizes with defaults) and
// its class-supplied extraTop/borderW overrides.  font may be NULL when
// only borderW is wanted (character-unit sizes then resolve to 0).
void widgetBoxMetrics(const WidgetT *w, const BitmapFontT *font, int32_t *pad, int32_t *gap, int32_t *extraTop, int32_t *borderW);
void widgetCalcMinSizeBox(WidgetT *w, const BitmapFontT *font);
void widgetCalcMinSizeTree(WidgetT *w, const BitmapFontT *font);
void widgetLayoutBox(WidgetT *w, const BitmapFontT *font);
void widgetLayoutChildren(WidgetT *w, const BitmapFontT *font);

// ============================================================
// Event dispatch (widgetEvent.c)
// ============================================================

void widgetManageScrollbars(WindowT *win, AppContextT *ctx);
// widgetOnBlur / widgetOnFocus are the default window focus handlers
// installed by wgtInitWindow; declared in dvxWgt.h so apps that override
// win->onBlur/onFocus can chain them.
void widgetOnKey(WindowT *win, int32_t key, int32_t mod);
void widgetOnKeyUp(WindowT *win, int32_t scancode, int32_t mod);
void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons);
void widgetOnPaint(WindowT *win, RectT *dirtyArea);
void widgetOnResize(WindowT *win, int32_t newW, int32_t newH);
void widgetOnScroll(WindowT *win, ScrollbarOrientE orient, int32_t value);

// ============================================================
// Paint helpers (widgetOps.c)
// ============================================================

void widgetPaintOne(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors);
void widgetPaintOverlays(WidgetT *root, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors);

// ============================================================
// Pressable helpers (widgetOps.c)
// ============================================================
//
// Shared drag/press state machine for WCLASS_PRESS_RELEASE widgets
// (Button, ImageButton).  The widget's w->pressed flag tracks the
// visual state; these helpers handle mouse/key activation and fire
// the widget's onClick callback on release-within-bounds.

void widgetPressableOnAccelActivate(WidgetT *w, WidgetT *root);
void widgetPressableOnDragEnd(WidgetT *w, WidgetT *root, int32_t x, int32_t y);
void widgetPressableOnDragUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y);
void widgetPressableOnKey(WidgetT *w, int32_t key, int32_t mod);
void widgetPressableOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy);

// ============================================================
// Text helpers (widgetOps.c)
// ============================================================
//
// Generic text get/set for widgets whose DataT struct has
// `const char *text` as the first field (Button, Checkbox, Label,
// Radio). The widget class must set WCLASS_HAS_TEXT so destroy
// frees the text automatically. Widgets register these directly
// as their WGT_METHOD_GET_TEXT / WGT_METHOD_SET_TEXT handlers.

const char *widgetTextGet(const WidgetT *w);
void        widgetTextSet(WidgetT *w, const char *text);

// Shared helper libraries:
//   texthelp/textHelp.h  -- clipboard, text editing, word boundaries
//   listhelp/listHelp.h  -- dropdown arrow, popup list, keyboard nav
// Widget DXEs that use these include the headers directly.

#endif // DVX_WGT_P_H
