/**
 * ========================================================================
 * ARCHITECTURE: FormPropertyManager
 * ========================================================================
 *
 * FormPropertyManager is the core property management layer for DataForm.
 * It handles all get/set operations for form data across three possible
 * backing stores, abstracting the complexity of property access from the
 * UI rendering layer.
 *
 * KEY CONCEPTS:
 *
 * 1. THREE BACKING STORES:
 *    - dataPropertyObject: IDataPropertyObject - uses getProperty()/ensureProperty() pattern
 *    - getsetPropertyObject: IGetSetPropertyObject - uses getProperty()/setProperty() pattern
 *    - directObject: plain JavaScript object - direct property access
 *
 *    Only one is typically used at a time, but the manager checks all three.
 *
 * 2. SCALAR VS OBJECT REPRESENTATION:
 *    Some form fields can be represented as either a scalar value OR an object.
 *    For example, a damage field might be:
 *      - Scalar: 5
 *      - Object: { amount: 5, type: "fire" }
 *
 *    The manager handles "upscaling" (scalar → object) and "downscaling"
 *    (object → scalar when only default values remain).
 *
 * 3. TYPE COERCION:
 *    Input from form controls is always strings. The manager converts to
 *    the appropriate type based on the field's dataType (int, float, boolean, etc.)
 *
 * ========================================================================
 * UPSCALE / DOWNSCALE STRATEGY
 * ========================================================================
 *
 * The "upscale on edit, downscale on persist" pattern is a core design
 * principle throughout this form system. It allows the UI to handle a
 * canonical (most complex) representation while persisting the simplest
 * valid format.
 *
 * THE PATTERN:
 *
 *   ┌────────────────┐     upscale()      ┌────────────────┐
 *   │  File/Stored   │  ───────────────▶  │  Edit Form     │
 *   │  (simplest)    │                    │  (canonical)   │
 *   └────────────────┘                    └────────────────┘
 *          ▲                                      │
 *          │            downscale()               │
 *          └──────────────────────────────────────┘
 *
 * WHY THIS MATTERS:
 *
 *   Minecraft JSON often supports multiple representations of the same data.
 *   For example, the `repair_items` field in `minecraft:repairable`:
 *
 *     Simple:   "minecraft:iron_ingot"
 *     Array:    ["minecraft:iron_ingot", "minecraft:gold_ingot"]
 *     Object:   { "items": ["minecraft:iron_ingot"], "repair_amount": 50 }
 *
 *   Without upscaling, the editor would need to handle all three forms.
 *   With upscaling, we always edit the object form and simplify on save.
 *
 * TWO LAYERS OF UPSCALING:
 *
 *   1. DATA LAYER (FormPropertyManager):
 *      - upscaleDirectObject(): Converts scalars to objects
 *        Example: 5 → { __scalar: 5 } or { amount: 5 }
 *      - downscaleDirectObject(): Collapses back to scalar if only defaults
 *        Example: { amount: 5 } → 5 (if amount is the only non-default)
 *
 *   2. UI LAYER (DataFormUtilities.selectFieldForValue):
 *      - Selects the appropriate field alternate based on actual value type
 *      - Ensures object values use object editors, arrays use array editors
 *      - Works with field "alternates" that define multiple type representations
 *
 * FIELD ALTERNATES:
 *
 *   A field definition can have multiple type representations via `alternates`.
 *   Each alternate is a complete field definition with a different dataType.
 *
 *   Example from repair_items:
 *     Primary:   { id: "repair_items", dataType: stringArray }
 *     Alternate: { dataType: objectArray, subForm: repairItemsSubForm }
 *
 *   selectFieldForValue() examines the actual data and picks the right variant.
 *
 * IMPLEMENTATION LOCATIONS:
 *
 *   - FormPropertyManager.upscaleDirectObject() - Scalar → Object
 *   - FormPropertyManager.downscaleDirectObject() - Object → Scalar
 *   - FormPropertyManager.setPropertyValue() - Uses upscale → edit → downscale
 *   - DataFormUtilities.selectFieldForValue() - Picks field variant for value type
 *   - DataForm.tsx render - Uses selectFieldForValue() for rendering
 *
 * EXTENDING THE PATTERN:
 *
 *   When adding new field types or component editors:
 *   1. Define all possible representations as field alternates
 *   2. Ensure selectFieldForValue() handles the new types
 *   3. The downscale logic automatically simplifies on persist
 *   4. Test with both simple and complex input data
 *
 * ========================================================================
 *
 * USAGE:
 *    const manager = new FormPropertyManager(definition, {
 *      dataPropertyObject: myDataPropertyObject,
 *      getsetPropertyObject: myGetSetPropertyObject,
 *      directObject: myDirectObject,
 *    });
 *
 *    // Get a property value
 *    const damage = manager.getProperty("damage", 0);
 *
 *    // Set a property value (returns updated directObject if applicable)
 *    const updatedObj = manager.setPropertyValue("damage", 10, currentDirectObject);
 *
 * RELATED FILES:
 *    - src/dataformux/DataForm.tsx - Main UI component that uses this manager
 *    - src/dataform/IField.ts - Field definitions including FieldDataType
 *    - src/dataform/IFormDefinition.ts - Form structure definitions
 *    - src/dataform/DataFormUtilities.ts - Static utilities for form operations
 *
 * ========================================================================
 */
import IFormDefinition from "./IFormDefinition";
import IField from "./IField";
import IProperty from "./IProperty";
/**
 * Interface for objects that provide property access via getProperty/ensureProperty pattern.
 * Used by block-style data objects.
 */
export interface IDataPropertyObject {
    getProperty(name: string): IProperty | undefined;
    ensureProperty(name: string): IProperty;
    onPropertyChanged: {
        subscribe(callback: () => void): void;
        unsubscribe(callback: () => void): void;
    };
}
/**
 * Interface for objects that provide property access via getProperty/setProperty pattern.
 * Used by simpler data objects.
 */
export interface IGetSetPropertyObject {
    getProperty(name: string): any;
    setProperty(name: string, value: any): void;
    setBaseValue?(value: any): void;
}
/**
 * Configuration options for FormPropertyManager.
 */
export interface IFormPropertyManagerOptions {
    /** Property object using getProperty/ensureProperty pattern */
    dataPropertyObject?: IDataPropertyObject;
    /** Property object using getProperty/setProperty pattern */
    getsetPropertyObject?: IGetSetPropertyObject;
    /** Plain JavaScript object for direct property access */
    directObject?: any;
}
/**
 * Result of a property update operation.
 */
export interface IPropertyUpdateResult {
    /** The updated direct object (after upscaling/downscaling) */
    updatedDirectObject: any;
    /** The property that was changed */
    property: IProperty;
    /** The new value */
    newValue: any;
}
/**
 * FormPropertyManager handles all property get/set operations for DataForm.
 *
 * It abstracts the complexity of:
 * - Multiple backing stores (dataPropertyObject, getsetPropertyObject, directObject)
 * - Scalar to object conversion (upscaling) and back (downscaling)
 * - Type coercion from string inputs to typed values
 * - Default value handling
 */
export default class FormPropertyManager {
    private _definition;
    private _dataPropertyObject;
    private _getsetPropertyObject;
    constructor(definition: IFormDefinition, options?: IFormPropertyManagerOptions);
    /**
     * Gets the form definition.
     */
    get definition(): IFormDefinition;
    /**
     * Updates the form definition.
     */
    set definition(value: IFormDefinition);
    /**
     * Updates the backing store objects.
     */
    updateBackingStores(options: IFormPropertyManagerOptions): void;
    /**
     * Gets a field definition by its ID.
     *
     * @param id - The field ID to look up
     * @returns The field definition, or undefined if not found
     */
    getFieldById(id: string): IField | undefined;
    /**
     * Gets a property value from the backing stores.
     *
     * Checks all three backing stores in order:
     * 1. dataPropertyObject
     * 2. getsetPropertyObject
     * 3. directObject
     *
     * @param name - The property name
     * @param defaultValue - Default value if property not found
     * @param directObject - The current direct object (may be updated state)
     * @returns The property value, or defaultValue if not found
     */
    getProperty(name: string, defaultValue?: any, directObject?: any): any;
    /**
     * Gets a property value as an integer.
     *
     * @param name - The property name
     * @param defaultValue - Default value if property not found or not convertible
     * @param directObject - The current direct object
     * @returns The property value as an integer
     */
    getPropertyAsInt(name: string, defaultValue?: number, directObject?: any): number | undefined;
    /**
     * Gets a property value as a boolean.
     *
     * @param name - The property name
     * @param defaultValue - Default value if property not found
     * @param directObject - The current direct object
     * @returns The property value as a boolean
     */
    getPropertyAsBoolean(name: string, defaultValue?: boolean, directObject?: any): boolean;
    /**
     * Converts a string value to the appropriate typed value based on field data type.
     *
     * @param field - The field definition
     * @param value - The value to convert (typically a string from form input)
     * @returns The typed value
     */
    getTypedData(field: IField, value: any): any;
    /**
     * Converts a scalar value to an object representation.
     *
     * This is needed when a field can be represented as either:
     * - A scalar: 5
     * - An object: { amount: 5, type: "fire" }
     *
     * @param directObject - The value to upscale (may be scalar or object)
     * @returns An object representation
     */
    upscaleDirectObject(directObject: {
        [propName: string]: any;
    } | string | number | boolean): {
        [name: string]: any;
    };
    /**
     * Checks if the object has any values besides scalar and default values.
     *
     * Used to determine if an object can be collapsed back to a scalar.
     *
     * @param directObject - The object to check
     * @returns True if the object has unique values
     */
    directObjectHasUniqueValuesBesidesScalar(directObject: {
        [propName: string]: any;
    }): boolean;
    /**
     * Converts an object representation back to a scalar if possible.
     *
     * This collapses objects that only contain:
     * - Empty values
     * - Default values
     * - The scalar field value
     *
     * @param directObject - The object to potentially downscale
     * @returns Either the original object or a scalar value
     */
    downscaleDirectObject(directObject: {
        [propName: string]: any;
    }): any;
    /**
     * Sets a property value across all applicable backing stores.
     *
     * @param id - The property ID to set
     * @param val - The new value
     * @param currentDirectObject - The current direct object state
     * @returns The update result with the new direct object state
     */
    setPropertyValue(id: string, val: any, currentDirectObject?: any): IPropertyUpdateResult;
    /**
     * Processes an input update from a form control.
     *
     * This is the main entry point for handling user input. It:
     * 1. Finds the field definition
     * 2. Converts the string input to the appropriate type
     * 3. Updates all backing stores
     *
     * @param id - The field ID being updated
     * @param data - The string value from the form control
     * @param currentDirectObject - The current direct object state
     * @returns The update result, or undefined if the update failed
     */
    processInputUpdate(id: string, data: string, currentDirectObject?: any): IPropertyUpdateResult | undefined;
    /**
     * Toggles a boolean property value.
     *
     * @param id - The property ID to toggle
     * @param defaultValue - The default value to use if property is not set
     * @param currentDirectObject - The current direct object state
     * @returns The update result
     */
    toggleBooleanProperty(id: string, defaultValue: boolean, currentDirectObject?: any): IPropertyUpdateResult;
}
