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

// ideDesigner.h -- DVX BASIC form designer
//
// Design-time data model and design surface for visual form editing.
// Controls are stored as pure data (not live widgets) and rendered
// onto a Canvas widget. The designer reads and writes .frm files.

#ifndef IDE_DESIGNER_H
#define IDE_DESIGNER_H

#include "dvxApp.h"
#include "dvxWgt.h"
#include "canvas/canvas.h"
#include "../compiler/lexer.h"
#include "../formrt/formrt.h"
#include "../formrt/frmParser.h"

#include "stb_ds_wrap.h"

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

// ============================================================
// Limits
// ============================================================
//
// Name, text, and nesting limits are the .frm limits from formrt.h and
// frmParser.h so the designer never truncates what the parser and runtime
// accept.

#define DSGN_MAX_NAME        BAS_MAX_IDENT
#define DSGN_MENU_STACK_DEPTH FRM_MAX_NESTING  // max menu nesting (editor and preview bar)
#define DSGN_MAX_TEXT        FRM_MAX_LINE_LEN
#define DSGN_MAX_PROPS       32
#define DSGN_HANDLE_SIZE      6
#define DSGN_MENU_ID_BASE    20000  // base ID for designer preview menu items
#define DSGN_MAX_NEST_DEPTH  32     // container recursion cap (save, cascade)
#define DSGN_VBOX_LAYOUT     "VBox" // default container / form layout name

// ============================================================
// Design-time property (stored as key=value strings)
// ============================================================

typedef struct {
    char name[DSGN_MAX_NAME];
    char value[DSGN_MAX_TEXT];
} DsgnPropT;

// ============================================================
// Design-time menu item (flat array with level for tree structure)
// ============================================================

typedef struct {
    char    caption[DSGN_MAX_TEXT];  // "&File", "-" for separator
    char    name[DSGN_MAX_NAME];    // "mnuFile"
    int32_t level;                   // 0 = top-level menu, 1 = item, 2+ = submenu
    bool    checked;
    bool    radioCheck;              // true = radio bullet instead of checkmark
    bool    enabled;                 // default true
    bool    visible;                 // default true; false on a top-level item = popup-only (not on menu bar)
} DsgnMenuItemT;

// ============================================================
// Design-time control
// ============================================================
//
// props[] holds every non-built-in "Key = Value" pair from the .frm,
// including widget interface properties, so a control keeps its values
// before its live widget exists and across widget rebuilds.  Interface
// properties are written back in the canonical form for their type
// (see dsgnEmitControl); props[] is only ever the raw string store.

typedef struct {
    char       name[DSGN_MAX_NAME];
    char       typeName[DSGN_MAX_NAME];
    char       parentName[DSGN_MAX_NAME]; // empty = top-level (child of form)
    int32_t    index;       // control array index (-1 = not in array)
    int32_t    left;        // design-time position hint (0 = layout-managed)
    int32_t    top;
    int32_t    width;
    int32_t    height;
    int32_t    maxWidth;    // 0 = no cap (stretch to fill)
    int32_t    maxHeight;   // 0 = no cap (stretch to fill)
    int32_t    weight;      // layout weight (0 = fixed size, >0 = share extra space)
    char       helpTopic[DSGN_MAX_NAME]; // help topic ID for F1
    bool       visible;     // design-time visibility (default true)
    bool       enabled;     // design-time enabled (default true)
    DsgnPropT  props[DSGN_MAX_PROPS];
    int32_t    propCount;
    WidgetT   *widget;      // live widget (created at design time for WYSIWYG)
} DsgnControlT;

// ============================================================
// Built-in integer control properties
// ============================================================
//
// One table drives the .frm loader, the .frm writer, the property grid
// rows, and the grid editor for every int32_t field of DsgnControlT.

typedef enum {
    DSGN_SAVE_ALWAYS,
    DSGN_SAVE_IF_NONZERO,
    DSGN_SAVE_IF_POSITIVE
} DsgnSaveRuleE;

typedef struct {
    const char    *name;     // .frm key and property-grid row name
    const char    *alias;    // alternate .frm key accepted on load (NULL = none)
    int32_t        offset;   // offsetof(DsgnControlT, field)
    DsgnSaveRuleE  saveRule;
} DsgnIntPropT;

// ============================================================
// Design-time form
// ============================================================

typedef struct {
    // Fields are ordered by alignment to minimize struct padding.
    char            name[BAS_MAX_IDENT];
    char            caption[DSGN_MAX_TEXT];
    int32_t         width;
    int32_t         height;
    int32_t         left;           // initial X position (0 = default)
    int32_t         top;            // initial Y position (0 = default)
    DsgnControlT  **controls;      // stb_ds array of heap-allocated pointers
    DsgnMenuItemT  *menuItems;     // stb_ds dynamic array (NULL if no menus)
    WidgetT        *contentBox;     // VBox parent for live widgets
    char           *code;           // BASIC code section (malloc'd, after End block)
    char            layout[DSGN_MAX_NAME]; // "VBox" or "HBox" (contentBox type)
    char            helpTopic[DSGN_MAX_NAME]; // form-level help topic
    bool            centered;       // true = center on screen, false = use left/top
    bool            autoSize;       // true = dvxFitWindow, false = use width/height
    bool            resizable;      // true = user can resize at runtime
    bool            dirty;
} DsgnFormT;

// ============================================================
// Active tool
// ============================================================
//
// Empty string = pointer (select/move) mode.
// Non-empty = basName of the widget type to place.

// ============================================================
// Grab handle IDs
// ============================================================

typedef enum {
    HANDLE_NONE = -1,
    HANDLE_E = 0,       // resize width
    HANDLE_S,           // resize height
    HANDLE_SE,          // resize both
    HANDLE_COUNT = 3
} DsgnHandleE;

// ============================================================
// Interaction mode
// ============================================================

typedef enum {
    DSGN_IDLE,
    DSGN_REORDERING,    // dragging control up/down in the VBox
    DSGN_RESIZING
} DsgnModeE;

// ============================================================
// Designer state
// ============================================================

typedef struct {
    DsgnFormT     *form;
    int32_t        selectedIdx;     // -1 = form itself selected
    char           activeTool[DSGN_MAX_NAME]; // "" = pointer, else basName to place
    DsgnModeE      mode;
    DsgnHandleE    activeHandle;
    int32_t        dragStartY;      // mouse Y at drag start (for reorder)
    int32_t        dragOrigWidth;   // widget size at resize start
    int32_t        dragOrigHeight;
    int32_t        dragStartX;      // mouse X at resize start
    WindowT       *formWin;
    AppContextT   *ctx;
    const char    *projectDir;     // project directory (for resolving relative paths)
} DsgnStateT;

// ============================================================
// API
// ============================================================

// Initialize designer state.
void dsgnInit(DsgnStateT *ds, AppContextT *ctx);

// Create live widgets for all controls in the form.
// Call after dsgnLoadFrm or dsgnNewForm, with the form window's contentBox.
void dsgnCreateWidgets(DsgnStateT *ds, WidgetT *contentBox);

// Destroy every live widget under the form's contentBox and recreate them in
// the current controls[] order (the widget child list mirrors that order).
void dsgnRebuildWidgets(DsgnStateT *ds);

// Load a .frm file into the designer.
bool dsgnLoadFrm(DsgnStateT *ds, const char *source, int32_t sourceLen);

// Save the designer form to .frm text format.
// Returns the number of bytes written (excluding null), or -1 on error or
// when the form does not fit in bufSize (the caller must not overwrite the
// original file with the partial result).
int32_t dsgnSaveFrm(const DsgnStateT *ds, char *buf, int32_t bufSize);

// Append one control's "Begin ... End" block (without its children) to buf
// at pos, indented by indent levels.  Returns the new position; shared by the
// .frm writer and the clipboard copy so both emit identical text.
int32_t dsgnEmitControl(const DsgnControlT *ctrl, char *buf, int32_t bufSize, int32_t pos, int32_t indent);

// Create a new blank form.
void dsgnNewForm(DsgnStateT *ds, const char *name);

// Draw selection handles over the painted window surface.
void dsgnPaintOverlay(DsgnStateT *ds);

// Handle mouse click on the design surface.
void dsgnOnMouse(DsgnStateT *ds, int32_t x, int32_t y, bool drag);

// Handle key press in design view.
void dsgnOnKey(DsgnStateT *ds, int32_t key);

// Get the name of the selected control (or form name if nothing selected).
const char *dsgnSelectedName(const DsgnStateT *ds);

// Return a control's property value by name, or NULL if it has no such property.
const char *dsgnControlGetPropValue(const DsgnControlT *ctrl, const char *name);

// Set (or add) a control's raw string property.  Silently ignored when the
// control already holds DSGN_MAX_PROPS distinct properties.
void dsgnSetPropValue(DsgnControlT *ctrl, const char *name, const char *value);

// Fetch the current value of a widget interface property as a string: the
// props[] copy when one exists, else the live widget.  Returns false when
// neither holds a value.
bool dsgnIfacePropValue(const DsgnControlT *ctrl, const WgtPropDescT *p, char *out, int32_t outSize);

// Push a control's size fields (width/height/max/weight) to its live widget.
void dsgnSyncWidgetGeom(DsgnControlT *ctrl);

// Built-in integer property table: entry by index (NULL past the end) and
// lookup by .frm key (name or alias).
const DsgnIntPropT *dsgnIntPropAt(int32_t idx);
const DsgnIntPropT *dsgnFindIntProp(const char *name);

// Text value of a runtime form property (basFormRtFormProps) as the designer
// stores it; false when the designer has no field for it (runtime-only).
bool dsgnFormPropValue(const DsgnFormT *form, const char *name, char *out, int32_t outSize);

// Get the default event name for a control type.
const char *dsgnDefaultEvent(const char *typeName);

// Auto-generate a unique control name for the given type.
void dsgnAutoName(const DsgnStateT *ds, const char *typeName, char *buf, int32_t bufSize);

// Check if a control type is a container (can hold children).
bool dsgnIsContainer(const char *typeName);

// True if name (case-insensitive) is already the form's name, a control's
// name, or (when checkMenus) a menu item's name.  Entries equal to
// exceptName are ignored so a control array or the object being renamed
// does not collide with itself.
bool dsgnNameInUse(const DsgnFormT *form, const char *name, const char *exceptName, bool checkMenus);

// Initialize a blank menu item (enabled, visible, level 0).
void dsgnMenuItemInit(DsgnMenuItemT *mi);

// Look up a widget type's interface property descriptor by name, or NULL if
// the type has no such interface property.  Shared IDE-internal helper.
const WgtPropDescT *findIfaceProp(const char *typeName, const char *propName);

// True if the widget type registers an interface property of this name.  The
// generic Visible/Enabled designer rows defer to such a property.
bool dsgnIfaceHasProp(const char *typeName, const char *propName);

// Clamped formatted append into a fixed buffer.  Returns pos unchanged once
// pos has reached bufSize; otherwise performs a size-clamped vsnprintf and
// returns the new position, capped at bufSize-1 on truncation.
int32_t emitClamped(char *buf, int32_t bufSize, int32_t pos, const char *fmt, ...);

// Free designer resources.
void dsgnFree(DsgnStateT *ds);

// Create a live design-time widget for a given VB type name.
WidgetT *dsgnCreateDesignWidget(const char *vbTypeName, WidgetT *parent);

// Build a display-only menu bar on a window from the form's menuItems.
// Used in the form designer to preview the menu layout.
void dsgnBuildPreviewMenuBar(WindowT *win, const DsgnFormT *form);

// ============================================================
// Code rename support (implemented in ideMain.c)
// ============================================================
//
// Rename all references to a form or control in project .bas files.
// Replaces OldName. -> NewName. and OldName_ -> NewName_ (case-insensitive,
// word-boundary aware). Also handles FormName.ControlName. patterns.

void ideRenameInCode(const char *oldName, const char *newName);

#endif // IDE_DESIGNER_H
