// 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_wm.h  -- Layer 4: Window manager for DVX GUI
//
// Manages the window lifecycle, Z-order stack, chrome drawing, hit testing,
// and interactive operations (drag, resize, scroll). This layer bridges the
// low-level drawing/compositing layers below with the high-level application
// API above.
//
// Design philosophy: the WM owns window geometry and chrome, but the
// window's content is owned by the application (via callbacks or the widget
// system). This separation means the WM can move, resize, and repaint
// window frames without involving the application at all  -- only content
// changes trigger application callbacks.
//
// All WM operations that change visible screen state accept a DirtyListT*
// so they can mark affected regions for compositor repaint. This push-based
// dirty tracking avoids the need for a full-screen diff each frame.
#ifndef DVX_WM_H
#define DVX_WM_H

#include "dvxTypes.h"

// Zero the window stack. Must be called before any other wm function.
void wmInit(WindowStackT *stack);

// Allocate a new WindowT, initialize its geometry and content buffer, and
// push it to the top of the Z-order stack. The returned window has default
// callbacks (all NULL)  -- the caller should set onPaint/onKey/etc. before
// the next event loop iteration.
WindowT *wmCreateWindow(WindowStackT *stack, DisplayT *d, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable);

// Free the window's content buffer and all attached resources (menu bar,
// scrollbars, widget tree), then remove it from the stack. Does not dirty
// the vacated region  -- the caller is responsible for marking it for
// repaint before calling (the window geometry is read here for cleanup).
void wmDestroyWindow(WindowStackT *stack, WindowT *win);

// Move window at stack index idx to the top of the Z-order. Dirties both
// the old and new top positions so overlapping windows get repainted.
void wmRaiseWindow(WindowStackT *stack, DirtyListT *dl, int32_t idx);

// Transfer keyboard focus to the window at stack index idx. Unfocuses the
// previously focused window and dirties both title bars (since active/
// inactive title colors differ).
void wmSetFocus(WindowStackT *stack, DirtyListT *dl, int32_t idx);

// Recompute contentX/Y/W/H from the window's outer frame dimensions,
// accounting for chrome borders, title bar, menu bar, and scrollbars.
// Must be called after any change to frame size or chrome configuration
// (e.g. adding a menu bar or scrollbar).
void wmUpdateContentRect(WindowT *win);

// Reallocate the per-window content backbuffer to match the current
// contentW/H. Returns 0 on success, -1 if allocation fails. The old
// buffer contents are lost  -- the caller should trigger a full repaint
// via onPaint after a successful realloc.
int32_t wmReallocContentBuf(WindowT *win, const DisplayT *d);

// Allocate and attach a menu bar to a window. Adjusts the content area
// to make room for the menu bar (CHROME_MENU_HEIGHT pixels). Only one
// menu bar per window is supported.
MenuBarT *wmAddMenuBar(WindowT *win);

// Free the menu bar and reclaim the content area. The content buffer is
// reallocated to the grown content rect; pass d as NULL only when the
// window is being destroyed and the buffer is already freed.
void wmDestroyMenuBar(WindowT *win, const DisplayT *d);

// Get the minimum window size (accounts for chrome, gadgets, and menu bar).
void wmMinWindowSize(const WindowT *win, int32_t *minW, int32_t *minH);

// Free both window scrollbars (if any) and clear win->vScroll/hScroll.
// Does not recompute the content rect.
void wmRemoveScrollbars(WindowT *win);

// Append a dropdown menu to the menu bar. Returns the MenuT to populate
// with items. The label supports & accelerator markers (e.g. "&File").
MenuT *wmAddMenu(MenuBarT *bar, const char *label);

// Append a clickable item to a menu. The id is passed to the window's
// onMenu callback when the item is selected.
void wmAddMenuItem(MenuT *menu, const char *label, int32_t id);

// Add a checkbox-style item. Check state is toggled on click and
// rendered with a checkmark glyph.
void wmAddMenuCheckItem(MenuT *menu, const char *label, int32_t id, bool checked);

// Add a radio-style item. Radio groups are defined implicitly by
// consecutive radio items in the same menu  -- selecting one unchecks
// the others in the group.
void wmAddMenuRadioItem(MenuT *menu, const char *label, int32_t id, bool checked);

// Insert a horizontal separator line. Separators are not interactive.
void wmAddMenuSeparator(MenuT *menu);

// Remove a menu item by command ID. Returns true if found and removed.
bool wmRemoveMenuItem(MenuT *menu, int32_t id);

// Remove all items from a menu (preserves the menu itself on the menu bar).
void wmClearMenuItems(MenuT *menu);

// Recursively search a single menu (and all of its submenus, to any
// depth) for an item with the given command ID. Returns the item, or
// NULL if not found. Unlike the menu-bar lookups below, this takes a
// standalone MenuT so it works for popup/context menus not attached to
// any menu bar.
MenuItemT *wmMenuFindItemInMenu(MenuT *menu, int32_t id);

// Query or set the checked state of a menu item by command ID.
// Searches all menus in the menu bar. For radio items, setting
// checked=true also unchecks other radio items in the same group.
bool wmMenuItemIsChecked(MenuBarT *bar, int32_t id);
void wmMenuItemSetChecked(MenuBarT *bar, int32_t id, bool checked);

// Set the checked state of an item within a standalone menu (a popup or
// context menu with no menu bar). Mirrors wmMenuItemSetChecked's radio
// behavior: setting a radio item checked unchecks its group siblings.
// Searches submenus recursively.
void wmMenuItemSetCheckedInMenu(MenuT *menu, int32_t id, bool checked);

// Apply a checked state to the item at index idx within menu, honoring
// implicit radio-group semantics (checking a radio item unchecks its
// contiguous group siblings). This is the single shared primitive behind
// wmMenuItemSetChecked, wmMenuItemSetCheckedInMenu, and the app's menu
// click handler, so the group-scan rule lives in exactly one place.
void menuItemApplyChecked(MenuT *menu, int32_t idx, bool checked);

// Enable or disable a menu item by command ID.
void wmMenuItemSetEnabled(MenuBarT *bar, int32_t id, bool enabled);

// Create a cascading submenu attached to the parent menu. Returns the
// child MenuT to populate, or NULL on allocation failure.
// The child MenuT is heap-allocated and freed when the parent window
// is destroyed.
MenuT *wmAddSubMenu(MenuT *parentMenu, const char *label);

// Attach a vertical scrollbar to the right edge of the window's content
// area. Shrinks contentW by SCROLLBAR_WIDTH pixels to make room. The
// scrollbar's value range and page size determine thumb proportions.
ScrollbarT *wmAddVScrollbar(WindowT *win, int32_t min, int32_t max, int32_t pageSize);

// Attach a horizontal scrollbar to the bottom edge. Shrinks contentH
// by SCROLLBAR_WIDTH pixels.
ScrollbarT *wmAddHScrollbar(WindowT *win, int32_t min, int32_t max, int32_t pageSize);

// Draw the window frame (outer bevel, title bar with text, close/minimize/
// maximize gadgets, and menu bar if present). clipTo restricts drawing to
// only the intersection with the given dirty rectangle, avoiding redundant
// pixel writes outside the damaged region.
void wmDrawChrome(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, WindowT *win, const RectT *clipTo);

// Blit the window's content backbuffer into the display backbuffer,
// clipped to the dirty rect. This is a pure copy (no drawing)  -- the
// content was already rendered by the application into contentBuf.
void wmDrawContent(DisplayT *d, const BlitOpsT *ops, WindowT *win, const RectT *clipTo);

// Draw scrollbars (track, arrows, proportional thumb) for a window.
// Drawn after content so scrollbars overlay the content area edge.
void wmDrawScrollbars(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, WindowT *win, const RectT *clipTo);

// Draw icons for all minimized windows along the bottom of the screen.
// Each icon shows a scaled-down preview of the window's content (or a
// default icon) with a beveled border, similar to a classic desktop icon bar.
void wmDrawMinimizedIcons(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, const WindowStackT *stack, const RectT *clipTo);

// Determine which window and which part of that window is under the given
// screen coordinates. Iterates the stack front-to-back (highest Z first)
// so the topmost window wins. Returns the stack index, or -1 if no window
// was hit (i.e. the desktop). hitPart identifies the chrome region:
//   HIT_CONTENT, HIT_TITLE, HIT_CLOSE, HIT_RESIZE,
//   HIT_MENU, HIT_VSCROLL, HIT_HSCROLL, HIT_MINIMIZE, HIT_MAXIMIZE
int32_t wmHitTest(const WindowStackT *stack, int32_t mx, int32_t my, int32_t *hitPart);

// For a point within a window's border zone, determine which edge(s)
// are being targeted for resize. Returns a bitmask of RESIZE_xxx flags.
// Corner hits combine two edges (e.g. RESIZE_LEFT | RESIZE_TOP).
int32_t wmResizeEdgeHit(const WindowT *win, int32_t mx, int32_t my);

// Update window position during an active drag. Called on each mouse move
// event while dragWindow is active. Dirties both the old and new window
// positions. The drag offset (mouse position relative to window origin at
// drag start) is applied so the window tracks the mouse smoothly.
void wmDragMove(WindowStackT *stack, DirtyListT *dl, int32_t mouseX, int32_t mouseY, int32_t screenW, int32_t screenH);

// Update window dimensions during an active resize. Enforces the dynamic
// wmMinWindowSize minimum, optional maxW/maxH constraints, and keeps the
// window reachable on-screen. Reallocates the content buffer if the
// content area size changed, then calls onResize to notify the application.
// mouseX/mouseY are in/out: on return they hold the clamped position so the
// caller can warp the hardware cursor to keep it stuck to the window edge.
void wmResizeMove(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, int32_t *mouseX, int32_t *mouseY);

// Begin a window drag operation. Records the mouse offset from the window
// origin so the window doesn't jump to the cursor position.
void wmDragBegin(WindowStackT *stack, int32_t idx, int32_t mouseX, int32_t mouseY);

// End the current drag operation. Clears dragWindow state.
void wmDragEnd(WindowStackT *stack);

// Begin a window resize operation. Records which edge(s) are being dragged
// and the initial mouse position for delta computation.
void wmResizeBegin(WindowStackT *stack, int32_t idx, int32_t edge, int32_t mouseX, int32_t mouseY);

// Apply a completed geometry change: recompute content rect, reallocate the
// content buffer, fire onResize, and request a PAINT_FULL repaint.
void wmResizeCommit(WindowT *win, const DisplayT *d);

// End the current resize operation. Clears resizeWindow state.
void wmResizeEnd(WindowStackT *stack);

// Set window title
void wmSetTitle(WindowT *win, DirtyListT *dl, const char *title);

// Handle an initial click on a scrollbar. Determines what was hit (up/down
// arrows, page trough area, or thumb) and either adjusts the value
// immediately (arrows, trough) or begins a thumb drag operation.
void wmScrollbarClick(WindowStackT *stack, DirtyListT *dl, int32_t idx, ScrollbarOrientE orient, int32_t mx, int32_t my);

// Update the scroll value during an active thumb drag. Maps the mouse
// position along the track to a scroll value proportional to the range.
void wmScrollbarDrag(WindowStackT *stack, DirtyListT *dl, int32_t mx, int32_t my);

// End an active scrollbar thumb drag.
void wmScrollbarEnd(WindowStackT *stack);

// Maximize a window (saves current geometry, expands to screen/max bounds)
void wmMaximize(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT *win);

// Minimize a window (hides window, shows icon at bottom of screen)
void wmMinimize(WindowStackT *stack, DirtyListT *dl, WindowT *win);

// Hit-test minimized icons at bottom of screen
// Returns stack index of the minimized window, or -1
int32_t wmMinimizedIconHit(const WindowStackT *stack, const DisplayT *d, int32_t mx, int32_t my);

// Compute screen position of a minimized icon by ordinal index.
// Icons wrap into rows from bottom to top when the screen is full.
void wmMinimizedIconPos(const DisplayT *d, int32_t index, int32_t *x, int32_t *y);

// Compute the screen rect covering all minimized icon rows.
// Used to dirty the icon area when windows are minimized or restored.
void wmMinimizedIconRect(const WindowStackT *stack, const DisplayT *d, int32_t *y, int32_t *h);

// Restore a maximized window to its pre-maximize geometry
void wmRestore(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT *win);

// Restore a minimized window
void wmRestoreMinimized(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT *win);

// Load an icon image for a window (converts to display pixel format)
int32_t wmSetIcon(WindowT *win, const char *path, const DisplayT *d);

// Allocate a heap-resident MenuT for use as a context menu (right-click).
// Unlike menu bar menus (which are embedded in MenuBarT), context menus
// are standalone allocations because they may be shared across windows or
// attached/detached dynamically. Free with wmFreeMenu().
MenuT *wmCreateMenu(void);

// Free a standalone menu allocated with wmCreateMenu(). Also frees any
// heap-allocated submenu children recursively.
void wmFreeMenu(MenuT *menu);

// Draw a standalone vertical scrollbar at the given screen coordinates.
// Used by popup lists (dropdown, combobox) that need a scrollbar without
// a full ScrollbarT struct. scrollPos/visibleItems/totalItems define the
// scroll state; the function computes thumb position/size internally.
void wmDrawVScrollbarAt(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t x, int32_t y, int32_t h, int32_t scrollPos, int32_t visibleItems, int32_t totalItems);

#endif // DVX_WM_H
