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

// dvx_app.h  -- Layer 5: Application API for DVX GUI
//
// The topmost layer and the public-facing API for applications. Aggregates
// all lower layers into a single AppContextT and provides a clean interface
// for window creation, event dispatch, and utilities. Applications interact
// exclusively through dvx*() functions and window callbacks  -- they never
// need to call the lower WM, compositor, or draw layers directly.
//
// The event loop (dvxRun/dvxUpdate) follows a cooperative model: poll mouse
// and keyboard, dispatch events to the focused window, run the compositor
// for dirty regions, then yield. There's no preemptive scheduling  -- the
// application must return from callbacks promptly.
#ifndef DVX_APP_H
#define DVX_APP_H

#include "dvxCur.h"
#include "dvxTypes.h"
#include "dvxPlat.h"
#include "dvxVideo.h"
#include "dvxDraw.h"
#include "dvxComp.h"
#include "dvxWm.h"

#include <time.h>

// ============================================================
// Application context
// ============================================================
//
// Single monolithic context that owns all GUI state. Allocated on the
// caller's stack (or statically)  -- no internal heap allocation for the
// context itself. This "god struct" approach keeps the API simple (one
// pointer to pass everywhere) and avoids the overhead of a handle-based
// system with opaque lookups. The tradeoff is a large struct, but on DOS
// memory layout is flat and cache pressure isn't a concern at this level.

typedef struct AppContextT {
    DisplayT      display;
    WindowStackT  stack;
    DirtyListT    dirty;
    BlitOpsT      blitOps;
    BitmapFontT   font;
    ColorSchemeT  colors;
    PopupStateT   popup;
    SysMenuStateT sysMenu;
    KbMoveResizeT kbMoveResize;
    CursorT       cursors[CURSOR_COUNT]; // indexed by CURSOR_xxx
    int32_t       cursorId;       // active cursor shape
    bool          running;
    int32_t       mouseX;
    int32_t       mouseY;
    int32_t       mouseButtons;
    int32_t       mouseWheel;      // wheel delta this frame (positive=down, negative=up)
    bool          hasMouseWheel;   // true if CuteMouse Wheel API detected
    int32_t       keyModifiers;    // current BIOS shift state (KEY_MOD_xxx)
    // Previous-frame mouse state for edge detection (button press/release
    // transitions and delta-based cursor movement).
    int32_t       prevMouseX;
    int32_t       prevMouseY;
    int32_t       prevMouseButtons;
    // Set true when a menu/popup click consumed a mouse press; the
    // matching release is then swallowed so widgets underneath the
    // (now-closed) menu don't receive a stray click.
    bool          suppressNextMouseUp;
    // Double-click detection for minimized window icons: timestamps and
    // window IDs track whether two clicks land on the same icon within
    // the system double-click interval.
    clock_t       lastIconClickTime;
    int32_t       lastIconClickId;    // window ID of last-clicked minimized icon (-1 = none)
    clock_t       lastCloseClickTime;
    int32_t       lastCloseClickId;   // window ID of last-clicked close gadget (-1 = none)
    clock_t       lastTitleClickTime;
    int32_t       lastTitleClickId;   // window ID of last-clicked title bar (-1 = none)
    // Minimized icon thumbnails are refreshed one per frame (round-robin)
    // rather than all at once, to amortize the cost of scaling the content
    // buffer down to icon size.
    int32_t       iconRefreshIdx;     // next minimized icon to refresh (staggered)
    int32_t       frameCount;         // frame counter for periodic tasks
    // The idle callback allows the host application (e.g. the DVX shell)
    // to do background work (poll serial ports, service DXE apps) during
    // frames where no GUI events occurred, instead of just yielding the CPU.
    void        (*idleCallback)(void *ctx); // called instead of yield when non-NULL
    void         *idleCtx;
    WindowT     *modalWindow;   // if non-NULL, only this window receives input
    // Deferred window destruction.  Destroying a window from inside a
    // user callback (onClick, timer tick, BASIC event handler) would
    // free widget memory the dispatch code still touches after the
    // callback returns.  While dispatchDepth > 0, dvxDestroyWindow
    // hides the window and flags it destroyPending; the flags are
    // flushed in dvxUpdate once the dispatch stack has fully unwound.
    // pendingDestroyCount is only a fast-path hint that a flush scan is
    // needed -- the per-window destroyPending flag is the single source
    // of truth for "queued".
    int32_t      dispatchDepth;
    int32_t      pendingDestroyCount;
    void        (*onCtrlEsc)(void *ctx); // system-wide Ctrl+Esc handler (e.g. task manager)
    void         *ctrlEscCtx;
    void        (*onF1)(void *ctx);    // system-wide F1 handler (context-sensitive help)
    void         *f1Ctx;
    void        (*onTitleChange)(void *ctx); // called when any window title changes
    void         *titleChangeCtx;
    int32_t      currentAppId;   // set by shell before calling app code (0 = shell)
    bool         busy;           // when true, show hourglass and block input
    // Tooltip state  -- tooltip appears after the mouse hovers over a widget
    // with a tooltip string for a brief delay. Pre-computing W/H avoids
    // re-measuring on every paint frame.
    clock_t      tooltipHoverStart;    // when mouse stopped moving
    const char  *tooltipText;          // text to show (NULL = hidden)
    int32_t      tooltipX;             // screen position
    int32_t      tooltipY;
    int32_t      tooltipW;             // size (pre-computed)
    int32_t      tooltipH;
    // Fixed-point reciprocal (16.16) of font.charHeight, pre-computed so
    // that dividing by charHeight (needed for pixel-to-row conversion in
    // terminal/text widgets) becomes a multiply+shift instead of an
    // integer divide, which is very slow on 486 (40+ cycles per DIV).
    uint32_t     charHeightRecip;      // fixed-point 16.16 reciprocal of font.charHeight
    // Mouse configuration (loaded from preferences)
    int32_t      wheelDirection;      // 1 = normal, -1 = reversed
    int32_t      wheelStep;           // lines per wheel notch (1-10, default 3)
    clock_t      dblClickTicks;       // double-click speed in clock() ticks
    int32_t      accelThreshold;      // last applied pointer-accel threshold
    int32_t      mickeyRatio;         // last applied mickeys-to-pixels ratio
    // Color scheme source RGB values (unpacked, for theme save/get)
    uint8_t      colorRgb[ColorCountE][3];
    // Available video modes (enumerated once at init)
    VideoModeInfoT *videoModes;     // stb_ds dynamic array
    // Wallpaper  -- pre-rendered to screen dimensions in native pixel format.
    // NULL means no wallpaper (solid desktop color).  wallpaperPath is
    // kept so the image can be reloaded after a video mode or mode change.
    uint8_t        *wallpaperBuf;        // pixel data (screen width * height * bpp/8)
    int32_t         wallpaperPitch;      // bytes per row
    char            wallpaperPath[DVX_MAX_PATH];  // source image path (empty = none)
    WallpaperModeE  wallpaperMode;       // stretch, tile, or center
} AppContextT;

// Initialize the entire GUI stack: video mode, input devices, font,
// color scheme, cursor shapes, and internal state. This is the single
// entry point for starting a DVX application. Returns 0 on success.
int32_t dvxInit(AppContextT *ctx, int32_t requestedW, int32_t requestedH, int32_t preferredBpp);

// Switch to a new video mode live.  Reallocates the backbuffer, all
// window content buffers, repacks colors, rescales wallpaper, and
// repositions windows that would be off-screen.  Returns 0 on success
// or -1 on failure (old mode is restored on failure).
int32_t dvxChangeVideoMode(AppContextT *ctx, int32_t requestedW, int32_t requestedH, int32_t preferredBpp);

// Configure mouse behaviour.  wheelDir: 1 = normal, -1 = reversed.
// dblClickMs: double-click speed in milliseconds (e.g. 500).
// accelThreshold: double-speed threshold in mickeys/sec (0 = don't change).
// mickeyRatio: mickeys per 8 pixels (0 = don't change, 8 = default speed).
// wheelStep: lines per wheel notch (1-10, default 3).
void dvxSetMouseConfig(AppContextT *ctx, int32_t wheelDir, int32_t dblClickMs, int32_t accelThreshold, int32_t mickeyRatio, int32_t wheelStep);

// ============================================================
// Color scheme
// ============================================================

// Set a single color by ID.  Repacks to native pixel format and
// invalidates the entire screen.
void dvxSetColor(AppContextT *ctx, ColorIdE id, uint8_t r, uint8_t g, uint8_t b);

// Get a color's RGB values by ID.
void dvxGetColor(const AppContextT *ctx, ColorIdE id, uint8_t *r, uint8_t *g, uint8_t *b);

// Apply all colors from ctx->colorRgb[] at once (repack + full repaint).
void dvxApplyColorScheme(AppContextT *ctx);

// Reset all colors to the built-in defaults and repaint.
void dvxResetColorScheme(AppContextT *ctx);

// Load a theme file (INI format with [colors] section) and apply it.
bool dvxLoadTheme(AppContextT *ctx, const char *filename);

// Save the current color scheme to a theme file.
bool dvxSaveTheme(const AppContextT *ctx, const char *filename);

// Return the INI key name for a color ID (e.g. "desktop", "windowFace").
const char *dvxColorName(ColorIdE id);

// Return a human-readable display label (e.g. "Desktop", "Cursor Color").
const char *dvxColorLabel(ColorIdE id);

// ============================================================
// Wallpaper
// ============================================================

// Load and apply a wallpaper image.  Uses the current wallpaperMode
// (stretch/tile/center).  Pass NULL to clear the wallpaper.
bool dvxSetWallpaper(AppContextT *ctx, const char *path);

// Change the wallpaper display mode and re-render the buffer.
// Has no effect if no wallpaper is loaded.
void dvxSetWallpaperMode(AppContextT *ctx, WallpaperModeE mode);

// Return the list of available video modes (enumerated at init).
// count receives the number of entries.
const VideoModeInfoT *dvxGetVideoModes(const AppContextT *ctx, int32_t *count);

// Tear down the GUI stack in reverse order: destroy all windows, restore
// text mode, release input devices. Safe to call after a failed dvxInit().
void dvxShutdown(AppContextT *ctx);

// Enter the main event loop. Polls input, dispatches events, composites
// dirty regions, and yields on each iteration. Returns when ctx->running
// becomes false (typically from dvxQuit() or a close callback).
void dvxRun(AppContextT *ctx);

// Process exactly one frame of the event loop. Provided for applications
// that need to integrate the GUI into their own main loop (e.g. the DVX
// shell polling serial ports between frames). Returns false when the GUI
// wants to exit.
bool dvxUpdate(AppContextT *ctx);

// Create a window at an explicit screen position. The window is raised
// to the top, focused, and its entire region is dirtied for the next
// composite pass.
WindowT *dvxCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable);

// Convenience wrapper that centers the window on screen.
WindowT *dvxCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable);

// Destroy a window, free all its resources, and dirty its former region.
// While a user callback is on the stack (dispatchDepth > 0) the teardown
// is deferred: the window is hidden and made inert, and its memory is
// freed once the dispatch stack unwinds.
void dvxDestroyWindow(AppContextT *ctx, WindowT *win);

// Destroy a window SYNCHRONOUSLY, even mid-dispatch, clearing any pending
// deferred-destroy state first.  For teardown that must complete before
// code or data becomes invalid (e.g. the shell destroying an app's windows
// before dlclose unmaps its DXE).  Safe mid-dispatch for windows the
// current dispatch is not executing inside: wgtDestroy bumps sWidgetGen
// and every dispatch site re-checks it before touching cached widgets.
void dvxDestroyWindowNow(AppContextT *ctx, WindowT *win);

// Raise a window to the top of the z-order and give it focus.
void dvxRaiseWindow(AppContextT *ctx, WindowT *win);

// Display a context menu at a screen position.  `menu` must be a
// standalone MenuT allocated with wmCreateMenu() and populated with
// wmAddMenuItem() / wmAddMenuSeparator() / wmAddSubMenu().  `win`
// supplies the onMenu dispatch target; menu-item clicks fire
// win->onMenu(win, id) exactly like menu-bar items.  Position is
// clamped to screen edges.
void dvxShowContextMenu(AppContextT *ctx, WindowT *win, MenuT *menu, int32_t screenX, int32_t screenY);

// Resize a window to exactly fit its widget tree's computed minimum size
// (plus chrome). Used for dialog boxes and other fixed-layout windows
// where the window should shrink-wrap its content.
void dvxFitWindow(AppContextT *ctx, WindowT *win);
void dvxFitWindowW(AppContextT *ctx, WindowT *win);
void dvxFitWindowH(AppContextT *ctx, WindowT *win);
void dvxResizeWindow(AppContextT *ctx, WindowT *win, int32_t newW, int32_t newH);

// Mark a sub-region of a window's content area as needing repaint. The
// coordinates are relative to the content area, not the screen. The
// onPaint callback will be called during the next composite pass with
// the dirty area.
void dvxInvalidateRect(AppContextT *ctx, WindowT *win, int32_t x, int32_t y, int32_t w, int32_t h);

// Hide the on-screen tooltip if it borrows `text` (NULL = hide any).
// ctx->tooltipText borrows the widget's tooltip string while displayed,
// so any code that frees or replaces such a string must call this first.
void dvxInvalidateTooltip(AppContextT *ctx, const char *text);

// Mark the entire window content area as dirty.
void dvxInvalidateWindow(AppContextT *ctx, WindowT *win);

// Hide a window without destroying it (marks exposed region dirty).
void dvxHideWindow(AppContextT *ctx, WindowT *win);

// Show a previously hidden window (marks region dirty for repaint).
void dvxShowWindow(AppContextT *ctx, WindowT *win);

// Minimize a window (show as icon at bottom of screen)
void dvxMinimizeWindow(AppContextT *ctx, WindowT *win);

// Maximize a window (expand to fill screen or maxW/maxH)
void dvxMaximizeWindow(AppContextT *ctx, WindowT *win);

// Request exit from main loop
void dvxQuit(AppContextT *ctx);

// Set/clear busy state. While busy, shows hourglass cursor and blocks input.
void dvxSetBusy(AppContextT *ctx, bool busy);

// Save the entire screen (backbuffer contents) to a PNG file. Converts
// from native pixel format to RGB for the PNG encoder. Returns 0 on
// success, -1 on failure.
int32_t dvxScreenshot(AppContextT *ctx, const char *path);

// Set window title
void dvxSetTitle(AppContextT *ctx, WindowT *win, const char *title);

// Get the default font
const BitmapFontT *dvxGetFont(const AppContextT *ctx);

// Get the color scheme
const ColorSchemeT *dvxGetColors(const AppContextT *ctx);

// Get the display
DisplayT *dvxGetDisplay(AppContextT *ctx);

// Get blit ops
const BlitOpsT *dvxGetBlitOps(const AppContextT *ctx);

// Load an icon for a window from an image file
int32_t dvxSetWindowIcon(AppContextT *ctx, WindowT *win, const char *path);

// Save a window's content to a PNG file (returns 0 on success, -1 on failure)
int32_t dvxWindowScreenshot(AppContextT *ctx, WindowT *win, const char *path);

// Allocate a new accelerator table. Attach it to a window via
// win->accelTable. The event loop checks the focused window's accel
// table on every keystroke and fires onMenu(cmdId) on match.
AccelTableT *dvxCreateAccelTable(void);

// Free an accelerator table and its entries.
void dvxFreeAccelTable(AccelTableT *table);

// Register a keyboard shortcut. key is an ASCII char or KEY_Fxx constant.
// modifiers is a bitmask of ACCEL_CTRL/ACCEL_SHIFT/ACCEL_ALT. cmdId is
// passed to the window's onMenu callback, matching the convention used
// for menu item IDs so the same handler works for both menus and hotkeys.
void dvxAddAccel(AccelTableT *table, int32_t key, int32_t modifiers, int32_t cmdId);

// Window arrangement functions  -- operate on all visible, non-minimized
// windows. These mirror the classic Windows 3.x "Window" menu commands.

// Cascade: offset each window diagonally by the title bar height.
void dvxCascadeWindows(AppContextT *ctx);

// Grid tile: arrange windows in an NxM grid filling the screen.
void dvxTileWindows(AppContextT *ctx);

// Horizontal tile: side by side, equal width, full height.
void dvxTileWindowsH(AppContextT *ctx);

// Vertical tile: stacked, full width, equal height.
void dvxTileWindowsV(AppContextT *ctx);

// Load an image file (BMP, PNG, JPEG, GIF) and convert to the display's
// native pixel format. Returns the pixel buffer on success (caller must
// free with dvxFreeImage), or NULL on failure. Output params receive the
// image dimensions and row pitch in bytes.
uint8_t *dvxLoadImage(const AppContextT *ctx, const char *path, int32_t *outW, int32_t *outH, int32_t *outPitch);

// Load an image from a memory buffer (e.g. resource data). Same output
// format as dvxLoadImage. Caller must free the result with dvxFreeImage.
uint8_t *dvxLoadImageFromMemory(const AppContextT *ctx, const uint8_t *data, int32_t dataLen, int32_t *outW, int32_t *outH, int32_t *outPitch);

// Load an image with alpha transparency support. Pixels with alpha < 128
// are replaced with the key color (packed magenta). If any transparent
// pixels were found, *outKeyColor receives the packed key color and
// *outHasAlpha is set to true. Caller must free with dvxFreeImage.
uint8_t *dvxLoadImageAlpha(const AppContextT *ctx, const uint8_t *data, int32_t dataLen, int32_t *outW, int32_t *outH, int32_t *outPitch, bool *outHasAlpha, uint32_t *outKeyColor);

// Free a pixel buffer returned by dvxLoadImage.
void dvxFreeImage(uint8_t *data);

// Query image dimensions without decoding the full file.
// Returns true on success, false if the file can't be read.
bool dvxImageInfo(const char *path, int32_t *outW, int32_t *outH);

// Save native-format pixel data to a PNG file. The pixel data must be
// in the display's native format (as returned by dvxLoadImage or
// captured from a content buffer). Returns 0 on success, -1 on failure.
int32_t dvxSaveImage(const AppContextT *ctx, const uint8_t *data, int32_t w, int32_t h, int32_t pitch, const char *path);

// Copy text to the process-wide clipboard buffer. The clipboard is a
// simple static buffer (not inter-process)  -- adequate for copy/paste
// within the DVX environment on single-tasking DOS.
void dvxClipboardCopy(const char *text, int32_t len);

// Retrieve the current clipboard contents. Returns a pointer to the
// internal static buffer (valid until the next dvxClipboardCopy), or
// NULL if the clipboard is empty.
const char *dvxClipboardGet(int32_t *outLen);

// ============================================================
// Resource loading helpers
// ============================================================
//
// Convenience functions for loading typed resources from DXE files.
// These combine dvxResOpen/Read/Close with type-specific processing
// so callers don't need to manage the resource handle or temp buffers.

// Load an icon/image resource and decode it to native pixel format.
// Returns the decoded pixel data (caller must free with dvxFreeImage),
// or NULL if not found. Sets outW/outH/outPitch on success.
uint8_t *dvxResLoadIcon(AppContextT *ctx, const char *dxePath, const char *resName, int32_t *outW, int32_t *outH, int32_t *outPitch);

// Load a text resource into a caller-provided buffer.
// Returns true on success. The text is null-terminated and truncated
// to fit bufSize. Returns false if the resource is not found.
bool dvxResLoadText(const char *dxePath, const char *resName, char *buf, int32_t bufSize);

// Load a raw binary resource. Returns a malloc'd buffer that the
// caller must free, or NULL if not found. Sets *outSize to the
// data size in bytes.
void *dvxResLoadData(const char *dxePath, const char *resName, uint32_t *outSize);

// ============================================================
// Text hash for dirty tracking
// ============================================================
//
// djb2-xor hash for cheap dirty detection. Compare the hash at save
// time with the current hash to detect changes without keeping a
// shadow copy of the text. Not cryptographic.

uint32_t dvxTextHash(const char *text);

#endif // DVX_APP_H
