(*
---
## Aura Module API Documentation

This unit defines the core interfaces and records for the Aura system, designed for robust algorithmic trading strategy development,
backtesting, and optimization. It encapsulates key concepts for defining strategies, executing backtests, performing walk-forward
optimizations, and conducting statistical robustness analysis via Monte Carlo simulations.

---

### Core Interfaces and Records

#### `IAuraObject`
Base interface for all Aura objects, providing a common `Name` property for identification.
    * `Name`: A writeable string representing the object's name.

#### `TAuraArray<T: IAuraObject>`
A record providing a mutable, observable array-like collection for `IAuraObject` instances. It wraps a `TWriteable<TArray<T>>` and offers basic array manipulation methods.
    * `Items`: A writeable array of `IAuraObject` instances.
    * `Insert(Idx: Integer; const Item: T)`: Inserts an item at a specified index.
    * `Delete(Idx: Integer)`: Deletes an item at a specified index.
    * `IndexOf(const Item: T)`: Returns the index of a given item.

#### `IAuraLiveObject`
Extends `IAuraObject` for entities that are dynamically produced and actively executing operations, providing logging capabilities and a running status.
    * `Log`: A `TStrings` object for logging events and status messages.
    * `IsRunning`: Indicates whether the object is currently active.

#### `IAuraNode`
Extends `IAuraObject` for objects that can be part of a hierarchical structure and can be serialized.
    * `Caption`: A human-readable representation of the object's name.
    * `Serialize(const Write: TJsonWriter)`: Serializes the object's state to a JSON writer.

#### `IAuraChilds<T: IAuraNode>`
A generic interface for `IAuraNode`s that can have child nodes, enabling hierarchical organization within the Aura system.
    * `Items`: A `TAuraArray` containing the child nodes.

#### `TAuraParameterDef`
Defines the metadata for a single strategy parameter, including its name, type, and default value.
    * `Name`: The name of the parameter.
    * `ParamType`: The data type of the parameter (e.g., integer, float, string, UTC time).
    * `DefaultValue`: The default value for the parameter, stored as a `TAuraParameterValue`.

#### `TAuraParameterRecordDef`
Represents a collection of `TAuraParameterDef` records, defining all parameters for a specific strategy. This is an alias for `TArray<TAuraParameterDef>`.

#### `IAuraBot`
An interface representing an instance that executes a trading strategy with given parameters over a specified time period.
    * `PnL`: A mutable data series representing the Profit and Loss (PnL) curve of the bot's execution.

#### `IAuraStrategy`
Defines a trading strategy, providing methods to create `IAuraBot` instances and specifying its parameter structure.
    * `CreateBot(const Parameters: TArray<TAuraParameterValue>; StartTime, EndTime: TDateTime)`: Creates and returns an `IAuraBot` instance configured with the given parameters and time range.
    * `ParameterRecordDef`: Defines the set of parameters expected by this strategy.

#### `TAuraTradePerformance`
A record encapsulating key performance metrics from a backtest or optimization run.
    * `PnL`: Total Profit and Loss.
    * `SharpeRatio`: Risk-adjusted return.
    * `MaxDrawdown`: Maximum peak-to-trough decline.
    * `ProfitFactor`: Ratio of gross profits to gross losses.
    * `CalmarRatio`: Annualized return divided by the maximum drawdown.
    * `WinRate`: Percentage of winning trades.

#### `TAuraMonteCarloResult`
Stores the results of a Monte Carlo simulation, providing a probabilistic distribution of `TAuraTradePerformance` outcomes.
    * `Percentile`: An array of `TAuraTradePerformance` records, sorted by PnL and distributed into 10% percentiles, allowing for statistical risk assessment.

#### `IAuraTradeResult`
Represents the comprehensive outcome of a trade simulation (e.g., from a backtest or optimization), including performance metrics, trade details, and the ability to perform Monte Carlo analysis.
    * `Performance`: The `TAuraTradePerformance` metrics for this result.
    * `Trades`: A `TDataSeries<Double>` representing the PnLs of individual trades, serving as the basis for Monte Carlo simulation.
    * `CalcMonteCarloSimulation(Steps: Integer)`: Asynchronously performs a Monte Carlo simulation on the `Trades` data, returning a `TFuture` that resolves to a `TAuraMonteCarloResult`.

#### `TAuraTimeRange`
A record defining a specific time window with a start and end date/time.
    * `StartTime`: The start of the time range.
    * `EndTime`: The end of the time range.

#### `IAuraBacktest`
An interface for executing a single backtest of a trading strategy.
    * `BacktestResult`: A `TFuture` that resolves to the `IAuraTradeResult` of this backtest.
    * `Bot`: The `IAuraBot` instance executing the backtest.
    * `Parameters`: The specific parameters used for this backtest.
    * `Strategy`: The `IAuraStrategy` being backtested.
    * `TimeRange`: The historical time window over which the backtest is conducted.

#### `IAuraParameterOptimization`
Represents a single optimization slot (window) within a Walk-Forward Optimization process. It manages the execution of multiple in-sample backtests to find optimal parameters, followed by an out-of-sample test.
    * `InSampleTests`: A mutable array of `IAuraBacktest` instances representing the backtests run during the in-sample optimization phase. New tests are added, and less optimal ones may be removed.
    * `OutOfSampleTest`: A mutable `IAuraBacktest` representing the test run with the best-fit parameters from the in-sample optimization on the subsequent out-of-sample data.

#### `IAuraWalkForwardOptimizer`
Orchestrates the entire Walk-Forward Optimization (WFO) process, coordinating multiple `IAuraParameterOptimization` slots sequentially to test strategy adaptability across various market phases.
    * `OptimizationResult`: A `TFuture` that resolves to the aggregated `IAuraTradeResult` representing the combined performance of all out-of-sample tests.
    * `Slot`: An array of `IAuraParameterOptimization` instances, each representing a distinct optimization window.

#### `IAuraParameterNode`
Represents a single configurable parameter for optimization algorithms, allowing its value to be set and observed.
    * `Value`: A mutable `TAuraParameterValue` representing the parameter's current setting.

#### `IAuraParameterList`
An alias for `IAuraChilds<IAuraParameterNode>`, providing a structured way to manage collections of `IAuraParameterNode`s.

#### `IAuraStudy`
Base interface for all types of strategy analysis studies (e.g., single backtests, parameter optimizations, WFOs).
    * `EventLog`: A `TStrings` object for logging events specific to the study.
    * `Strategy`: The `IAuraStrategy` that is the subject of the study.

#### `TAuraParameterRange`
Defines the allowed range for a strategy parameter during optimization.
    * `MinValue`: The minimum allowed value for the parameter, as a `TAuraParameterValue`.
    * `MaxValue`: The maximum allowed value for the parameter, as a `TAuraParameterValue`.

#### `IAuraBacktestStudy`
A specialized `IAuraStudy` for setting up and initiating a single backtest.
    * `TimeRange`: The `TAuraTimeRange` for the backtest.
    * `Start`: Initiates and returns an `IAuraBacktest` instance.

#### `IAuraParameterOptimizationStudy`
A specialized `IAuraStudy` for configuring and initiating a parameter optimization process (e.g., using genetic algorithms) over a specific time range.
    * `Parameters`: An `IAuraChilds` collection of `IAuraParameterNode`s defining the configuration parameters for the optimization algorithm itself (e.g., max generations, population size).
    * `TimeRange`: The `TAuraTimeRange` for the optimization.
    * `Start`: Initiates and returns an `IAuraParameterOptimization` instance.

#### `IAuraWFOStudy`
A specialized `IAuraStudy` for configuring and initiating a Walk-Forward Optimization process.
    * `ParameterRange[Idx: Integer]`: A writeable `TAuraParameterRange` defining the search space for each strategy parameter during the optimization phases.
    * `NumSlots`: A writeable integer indicating the number of time windows (slots) for the WFO.
    * `Start`: Initiates and returns an `IAuraWalkForwardOptimizer` instance.

#### `IAuraTradingMode`
An enumeration defining different operational modes for a trading environment.
    * `tmTesting`: Mode for historical backtesting.
    * `tmSim`: Mode for simulated live trading (paper trading).
    * `tmLive`: Mode for actual live trading.

#### `IAuraWorkspace`
Represents the scope of a complete testing and trading environment, containing various studies and managing the overall trading mode.
    * `WalkForwardAnalysis`: An `IAuraChilds` collection of `IAuraWFOStudy` instances.
    * `Backtests`: An `IAuraChilds` collection of `IAuraBacktestStudy` instances.
    * `ParameterOptimizations`: An `IAuraChilds` collection of `IAuraParameterOptimizationStudy` instances.
    * `TradingMode`: A writeable `IAuraTradingMode` indicating the current operational mode.

#### `IAuraModule`
An interface for creating instances of `IAuraStrategy`. Used for managing available strategy types.
    * `CreateStrategy`: Creates and returns a new `IAuraStrategy` instance.

#### `IAuraApplication`
The application-level singleton, serving as the root for serialization and providing access to all defined strategies and workspaces.
    * `Strategies`: An `IAuraChilds` collection of `IAuraModule` instances.
    * `Workspaces`: An `IAuraChilds` collection of `IAuraWorkspace` instances.

---
*)

unit Myc.Aura.Module;

interface

uses
    System.JSON.Writers,
    System.Classes,
    System.Generics.Collections,
    Myc.Signals,
    Myc.Futures,
    Myc.Mutable,
    Myc.Aura.Parameter;

type
    // Base class of all Aura objects
    IAuraObject = interface
        function GetName: TWriteable<String>;
        property Name: TWriteable<String> read GetName;
    end;

    TAuraArray<T: IAuraObject> = record
    private
        FArray: TWriteable<TArray<T>>;
        function GetItems: TWriteable<TArray<T>>;
    public
        class operator Initialize(out Dest: TAuraArray<T>);
        procedure Insert(Idx: Integer; const Item: T);
        procedure Delete(Idx: Integer);
        function IndexOf(const Item: T): Integer;
        property Items: TWriteable<TArray<T>> read GetItems;
    end;

    // Base class for objects that are dynamically produced and are executing
    IAuraLiveObject = interface(IAuraObject)
        function GetLog: TStrings;
        function IsRunning: Boolean;
        property Log: TStrings read GetLog;
    end;

    IAuraNode = interface(IAuraObject)
        function GetCaption: string;
        // Serialize the whole project
        procedure Serialize(const Write: TJsonWriter);
        // Pretty print of Name
        property Caption: string read GetCaption;
    end;

    IAuraChilds<T> = interface(IAuraNode)
        function GetItems: TWriteable<TArray<T>>;
        procedure Insert(Idx: Integer; const Item: T);
        procedure Delete(Idx: Integer);
        function IndexOf(const Item: T): Integer;
        // Nodes to add as childs in hierarchical representation
        property Items: TWriteable<TArray<T>> read GetItems;
    end;

    TAuraChilds<T> = record
    private
        FChilds: IAuraChilds<T>;
        function GetCaption: string;
    public
        constructor Create(const AChilds: IAuraChilds<T>);
        class operator Implicit(const A: IAuraChilds<T>): TAuraChilds<T>; overload;
        class operator Implicit(const A: TAuraChilds<T>): IAuraChilds<T>; overload;
        procedure Serialize(const Write: TJsonWriter);
        procedure Insert(Idx: Integer; const Item: T);
        procedure Delete(Idx: Integer);
        function IndexOf(const Item: T): Integer;
        property Caption: string read GetCaption;
    end;

    TAuraParameterDef = record
        Name: String;
        ParamType: TAuraParameterType;
        DefaultValue: TAuraParameterValue;
    end;

    TAuraParameterRecordDef = TArray<TAuraParameterDef>;

    // A bot executes a strategy
    IAuraBot = interface(IAuraLiveObject)
    end;

    // A strategy creates a bot that executes that strategy with given parameters and within a given time slot.
    IAuraStrategy = interface(IAuraObject)
        function CreateBot(const Parameters: TArray<TAuraParameterValue>; StartTime, EndTime: TDateTime): IAuraBot;
        function GetParameterRecordDef: TAuraParameterRecordDef;
        // Defines the parameters of this strategy
        property ParameterRecordDef: TAuraParameterRecordDef read GetParameterRecordDef;
    end;

    // The performance of a backtest or walk forward optimization
    TAuraTradePerformance = record
        PnL: Double;
        SharpeRatio, MaxDrawdown, ProfitFactor, CalmarRatio, WinRate: Double;
    end;

    TAuraMonteCarloResult = record
        // Performance sorted by PnL and distributed to percentiles of 10% each
        Percentile: array[0..10] of TAuraTradePerformance;
    end;

    IAuraTradeResult = interface
        function GetPerformance: TAuraTradePerformance;
        // Perform Monte Carlo Simulation and generate performance distribution
        function CalcMonteCarloSimulation(Steps: Integer): TFuture<TAuraMonteCarloResult>;
        // The performance of this equity curve
        property Performance: TAuraTradePerformance read GetPerformance;
    end;

    TAuraTimeRange = record
        StartTime: TDateTime;
        EndTime: TDateTime;
    end;

    // Backtesting
    IAuraBacktest = interface(IAuraLiveObject)
        function GetParameters: TArray<TAuraParameterValue>;
        function GetStrategy: IAuraStrategy;
        function GetBot: IAuraBot;
        function GetBacktestResult: TFuture<IAuraTradeResult>;
        function GetTimeRange: TAuraTimeRange;
        // The bot executing the backtest (generated by Strategy.CreateBot with the given parameters).
        property Bot: IAuraBot read GetBot;
        // The backtest result
        property BacktestResult: TFuture<IAuraTradeResult> read GetBacktestResult;
        // The strategy parameters for this backtest
        property Parameters: TArray<TAuraParameterValue> read GetParameters;
        // The strategy to backtest
        property Strategy: IAuraStrategy read GetStrategy;
        // The tested time window
        property TimeRange: TAuraTimeRange read GetTimeRange;
    end;

    // Generates parameter sets using genetic algorithms (or other techniques).
    // Executes backtests and finds the best run by evaluating the backtest results.
    IAuraParameterOptimization = interface(IAuraLiveObject)
        function GetInSampleTests: TMutable<TArray<IAuraBacktest>>;
        function GetOutOfSampleTest: TMutable<IAuraBacktest>;
        // The running in-sample-tests. New tests will be added until the optimization stops.
        property InSampleTests: TMutable<TArray<IAuraBacktest>> read GetInSampleTests;
        // After in sample testing is finished, the out-of-sample-test with the best parameter set is executed.
        property OutOfSampleTest: TMutable<IAuraBacktest> read GetOutOfSampleTest;
    end;

    // Walk-Forward-Optimizer
    IAuraWalkForwardOptimizer = interface(IAuraLiveObject)
        function GetOptimizationResult: TFuture<IAuraTradeResult>;
        function GetSlot: TArray<IAuraParameterOptimization>;
        // When optimization is ready, this will contain the result for the combined WFO slots
        property OptimizationResult: TFuture<IAuraTradeResult> read GetOptimizationResult;
        // The concurrently running optimization slots
        property Slot: TArray<IAuraParameterOptimization> read GetSlot;
    end;

    // A Parameter Value
    IAuraParameterNode = interface(IAuraNode)
        function GetValue: TMutable<TAuraParameterValue>;
        property Value: TMutable<TAuraParameterValue> read GetValue;
    end;
    IAuraParameterList = IAuraChilds<IAuraParameterNode>;

    // A study on a strategy
    IAuraStudy = interface(IAuraNode)
        function GetEventLog: TStrings;
        function GetStrategy: IAuraStrategy;
        property EventLog: TStrings read GetEventLog;
        // The Strategy to optimize
        property Strategy: IAuraStrategy read GetStrategy;
    end;

    TAuraParameterRange = record
        MinValue, MaxValue: TAuraParameterValue;
    end;

    // Study on a single backtest
    IAuraBacktestStudy = interface(IAuraStudy)
        function GetTimeRange: TAuraTimeRange;
        function Start: IAuraBacktest;
        property TimeRange: TAuraTimeRange read GetTimeRange;
    end;

    // Study parameter optimization
    IAuraParameterOptimizationStudy = interface(IAuraStudy)
        function GetParameters: IAuraChilds<IAuraParameterNode>;
        function GetTimeRange: TAuraTimeRange;
        function Start: IAuraParameterOptimization;
        // Depending on the implementation this may be MaxGenerations, PopulationSize, Mutations, etc
        property Parameters: IAuraChilds<IAuraParameterNode> read GetParameters;
        property TimeRange: TAuraTimeRange read GetTimeRange;
    end;

    // Walk forward optimization
    IAuraWFOStudy = interface(IAuraStudy)
        function GetParameterRange(Idx: Integer): TWriteable<TAuraParameterRange>;
        function GetNumSlots: TWriteable<Integer>;
        // Starts WalkForwardOptimizer with the given number of NumSlots.
        function Start: IAuraWalkForwardOptimizer;
        // A list of parameter values to test, one for each parameter in the Strategy's parameter definition.
        // This defines the parameter space for optimization algorithms.
        property ParameterRange[Idx: Integer]: TWriteable<TAuraParameterRange> read GetParameterRange;
        // Number of time slots (windows) to test.
        property NumSlots: TWriteable<Integer> read GetNumSlots;
    end;

    TAuraTradingMode = (tmTesting, tmSim, tmLive);

    // Repesents the scope of a workspace
    IAuraWorkspace = interface(IAuraNode)
        ['{8485F7E7-F097-4CB8-8186-A6C9AE0048AF}']
        //        function GetWalkForwardAnalysis: IAuraChilds<IAuraWFOStudy>;
        //        function GetBacktests: IAuraChilds<IAuraBacktestStudy>;
        //        function GetParameterOptimizations: IAuraChilds<IAuraParameterOptimizationStudy>;
        //        function GetWalkForwardAnalysis: IAuraChilds<IAuraWFOStudy>;
        //        function GetBacktests: IAuraChilds<IAuraBacktestStudy>;
        //        function GetParameterOptimizations: IAuraChilds<IAuraParameterOptimizationStudy>;
        //        property WalkForwardAnalysis: IAuraChilds<IAuraWFOStudy> read GetWalkForwardAnalysis;
        //        property Backtests: IAuraChilds<IAuraBacktestStudy> read GetBacktests;
        //        property ParameterOptimizations: IAuraChilds<IAuraParameterOptimizationStudy> read GetParameterOptimizations;
        //        property WalkForwardAnalysis: IAuraChilds<IAuraWFOStudy> read GetWalkForwardAnalysis;
        //        property Backtests: IAuraChilds<IAuraBacktestStudy> read GetBacktests;
        //        property ParameterOptimizations: IAuraChilds<IAuraParameterOptimizationStudy> read GetParameterOptimizations;
        function GetTradingMode: TWriteable<TAuraTradingMode>;
        function GetStudies: TAuraArray<IAuraStudy>;
        property TradingMode: TWriteable<TAuraTradingMode> read GetTradingMode;
        property Studies: TAuraArray<IAuraStudy> read GetStudies;
    end;

    IAuraModule = interface
        procedure SetupWorkspace(const Workspace: IAuraWorkspace);
    end;

    // Application singleton, the root for serialization
    IAuraApplication = interface(IAuraNode)
        function GetModuleNames: TMutable<TArray<String>>;
        function GetModules(const Name: String): IAuraModule;
        function GetWorkspaces: IAuraChilds<IAuraWorkspace>;
        procedure RegisterModule(const Name: String; const Module: IAuraModule);
        property ModuleNames: TMutable<TArray<String>> read GetModuleNames;
        property Modules[const Name: String]: IAuraModule read GetModules;
        property Workspaces: IAuraChilds<IAuraWorkspace> read GetWorkspaces;
    end;

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // Generic implementation for a collection of child nodes.
    TMycAuraObject = class(TInterfacedObject, IAuraObject)
    private
        FName: TWriteable<String>;
        function GetName: TWriteable<String>;
        procedure Serialize(const Write: TJsonWriter); virtual;
    public
        constructor Create(const AName: string);
    end;

    // Generic implementation for a collection of child nodes.
    TMycAuraNode = class(TMycAuraObject, IAuraNode)
    private
    protected
        function GetCaption: string; virtual;
        procedure Serialize(const Write: TJsonWriter); override;
    end;

    // Generic implementation for a collection of child nodes.
    TMycAuraChilds<T: IAuraNode> = class(TInterfacedObject, IAuraChilds<T>)
    private
        FName: TWriteable<String>;
        FItems: TWriteable<TArray<T>>;
        function GetCaption: string;
        function GetItems: TWriteable<TArray<T>>;
        function GetName: TWriteable<String>;
        procedure Serialize(const Write: TJsonWriter);
    public
        constructor Create(const AName: string);
        procedure Delete(Idx: Integer);
        function IndexOf(const Item: T): Integer;
        procedure Insert(Idx: Integer; const Item: T);
    end;

    TMycAuraWorkspace = class(TMycAuraNode, IAuraWorkspace)
    private
        FTradingMode: TWriteable<TAuraTradingMode>;
        FStudies: TAuraArray<IAuraStudy>;
        function GetStudies: TAuraArray<IAuraStudy>;
        function GetTradingMode: TWriteable<TAuraTradingMode>;
    protected
        function GetCaption: string; override;
    public
        constructor Create(const AName: string; TradingMode: TAuraTradingMode);
    end;

    TMycAuraApplication = class(TInterfacedObject, IAuraApplication)
    private
        FName: TWriteable<String>;
        FModules: TDictionary<String, IAuraModule>;
        FModulesChanged: TEvent;
        FModuleNames: TMutable<TArray<String>>;
        FWorkspaces: TAuraChilds<IAuraWorkspace>;
        function GetCaption: string;
        function GetName: TWriteable<String>;
        function GetModuleNames: TMutable<TArray<String>>;
        function GetModules(const Name: String): IAuraModule;
        function GetWorkspaces: IAuraChilds<IAuraWorkspace>;
        procedure Serialize(const Write: TJsonWriter);
    public
        constructor Create;
        procedure RegisterModule(const Name: String; const Module: IAuraModule);
    end;

implementation

{ TMycAuraObject }

constructor TMycAuraObject.Create(const AName: string);
begin
    inherited Create;
    FName := TWriteable<String>.CreateWriteable;
    FName.Value := AName;
end;

function TMycAuraObject.GetName: TWriteable<String>;
begin
    Result := FName;
end;

procedure TMycAuraObject.Serialize(const Write: TJsonWriter);
begin
    Write.WriteStartObject;
    Write.WritePropertyName('Name');
    Write.WriteValue(FName.Value);
    Write.WriteEndObject;
end;

{ TMycAuraNode }

function TMycAuraNode.GetCaption: string;
begin
    Result := FName.Value;
end;

procedure TMycAuraNode.Serialize(const Write: TJsonWriter);
begin
    Write.WriteStartObject;
    inherited;
    Write.WriteEndObject;
end;

{ TAuraArray<T> }

class operator TAuraArray<T>.Initialize(out Dest: TAuraArray<T>);
begin
    Dest.FArray := TWriteable<TArray<T>>.CreateWriteable;
end;

procedure TAuraArray<T>.Delete(Idx: Integer);
var
    oldArray: TArray<T>;
    newArray: TArray<T>;
    oldCount: Integer;
begin
    oldArray := FArray.Value;
    oldCount := Length(oldArray);

    if (Idx < 0) or (Idx >= oldCount) then
        exit;

    SetLength(newArray, oldCount - 1);

    // Copy elements before the index
    if Idx > 0 then
        TArray.Copy<T>(oldArray, newArray, 0, 0, Idx);

    // Copy elements after the index
    if Idx < oldCount - 1 then
        TArray.Copy<T>(oldArray, newArray, Idx + 1, Idx, oldCount - Idx - 1);

    FArray.Value := newArray;
end;

procedure TAuraArray<T>.Insert(Idx: Integer; const Item: T);
var
    oldArray: TArray<T>;
    newArray: TArray<T>;
    oldCount: Integer;
begin
    oldArray := FArray.Value;
    if not Assigned(oldArray) then
        SetLength(oldArray, 0);
    oldCount := Length(oldArray);

    if Idx < 0 then
        Idx := 0;
    if Idx > oldCount then
        Idx := oldCount;

    SetLength(newArray, oldCount + 1);

    // Copy elements before the index
    if Idx > 0 then
        TArray.Copy<T>(oldArray, newArray, 0, 0, Idx);

    newArray[Idx] := Item;

    // Copy elements after the index
    if Idx < oldCount then
        TArray.Copy<T>(oldArray, newArray, Idx, Idx + 1, oldCount - Idx);

    FArray.Value := newArray;
end;

function TAuraArray<T>.GetItems: TWriteable<TArray<T>>;
begin
    Result := FArray;
end;

function TAuraArray<T>.IndexOf(const Item: T): Integer;
begin
    Result := TArray.IndexOf<T>(FArray.Value, Item);
end;

{ TMycAuraChilds<T> }

constructor TMycAuraChilds<T>.Create(const AName: string);
begin
    inherited Create;
    FName := TWriteable<String>.CreateWriteable(AName);
    FItems := TWriteable<TArray<T>>.CreateWriteable;
end;

procedure TMycAuraChilds<T>.Delete(Idx: Integer);
var
    oldArray: TArray<T>;
    newArray: TArray<T>;
    oldCount: Integer;
begin
    oldArray := FItems.Value;
    oldCount := Length(oldArray);

    if (Idx < 0) or (Idx >= oldCount) then
        exit;

    SetLength(newArray, oldCount - 1);

    // Copy elements before the index
    if Idx > 0 then
        TArray.Copy<T>(oldArray, newArray, 0, 0, Idx);

    // Copy elements after the index
    if Idx < oldCount - 1 then
        TArray.Copy<T>(oldArray, newArray, Idx + 1, Idx, oldCount - Idx - 1);

    FItems.Value := newArray;
end;

function TMycAuraChilds<T>.GetCaption: string;
begin
    Result := FName.Value;
end;

function TMycAuraChilds<T>.GetItems: TWriteable<TArray<T>>;
begin
    Result := FItems;
end;

function TMycAuraChilds<T>.GetName: TWriteable<String>;
begin
    Result := FName;
end;

function TMycAuraChilds<T>.IndexOf(const Item: T): Integer;
begin
    Result := TArray.IndexOf<T>(FItems.Value, Item);
end;

procedure TMycAuraChilds<T>.Insert(Idx: Integer; const Item: T);
var
    oldArray: TArray<T>;
    newArray: TArray<T>;
    oldCount: Integer;
begin
    oldArray := FItems.Value;
    if not Assigned(oldArray) then
        SetLength(oldArray, 0);
    oldCount := Length(oldArray);

    if Idx < 0 then
        Idx := 0;
    if Idx > oldCount then
        Idx := oldCount;

    SetLength(newArray, oldCount + 1);

    // Copy elements before the index
    if Idx > 0 then
        TArray.Copy<T>(oldArray, newArray, 0, 0, Idx);

    newArray[Idx] := Item;

    // Copy elements after the index
    if Idx < oldCount then
        TArray.Copy<T>(oldArray, newArray, Idx, Idx + 1, oldCount - Idx);

    FItems.Value := newArray;
end;

procedure TMycAuraChilds<T>.Serialize(const Write: TJsonWriter);
var
    Item: T;
    arr: TArray<T>;
begin
    Write.WriteStartObject;
    inherited;
    Write.WritePropertyName('Items');
    Write.WriteStartArray;
    arr := FItems.Value;
    for Item in arr do
        Item.Serialize(Write);
    Write.WriteEndArray;
    Write.WriteEndObject;
end;

{ TMycAuraApplication }

constructor TMycAuraApplication.Create;
begin
    inherited Create;
    FName := TWriteable<String>.CreateWriteable('Application');
    FModules := TDictionary<String, IAuraModule>.Create;
    FModulesChanged := TEvent.CreateEvent;
    FModuleNames :=
        TMutable<TArray<String>>.Construct(FModulesChanged.Signal, function: TArray<String> begin Result := FModules.Keys.ToArray; end);
    FWorkspaces := TMycAuraChilds<IAuraWorkspace>.Create('Workspaces');
end;

function TMycAuraApplication.GetCaption: string;
begin
    Result := FName.Value;
end;

function TMycAuraApplication.GetName: TWriteable<String>;
begin
    Result := FName;
end;

function TMycAuraApplication.GetModuleNames: TMutable<TArray<String>>;
begin
    Result := FModuleNames;
end;

function TMycAuraApplication.GetModules(const Name: String): IAuraModule;
begin
    if not FModules.TryGetValue(Name, Result) then
        Result := nil;
end;

function TMycAuraApplication.GetWorkspaces: IAuraChilds<IAuraWorkspace>;
begin
    Result := FWorkspaces;
end;

procedure TMycAuraApplication.RegisterModule(const Name: String; const Module: IAuraModule);
begin
    FModules.Add(Name, Module);
    FModulesChanged.Notify;
end;

procedure TMycAuraApplication.Serialize(const Write: TJsonWriter);
begin
    Write.WriteStartObject;
    Write.WritePropertyName('Name');
    Write.WriteValue(FName.Value);
    Write.WritePropertyName('Workspaces');
    FWorkspaces.Serialize(Write);
    Write.WriteEndObject;
end;

function CreateAuraApplication: IAuraApplication;
begin
    Result := TMycAuraApplication.Create;
end;

constructor TMycAuraWorkspace.Create(const AName: string; TradingMode: TAuraTradingMode);
begin
    inherited Create(AName);
    FTradingMode := TWriteable<TAuraTradingMode>.CreateWriteable(TradingMode);
end;

function TMycAuraWorkspace.GetCaption: string;
begin
    var mode := '';
    case FTradingMode.Value of
        tmTesting: mode := ' (Test)';
        tmSim: mode := ' (Sim)';
        tmLive: mode := ' (Live)';
    end;
    Result := inherited GetCaption + mode;
end;

function TMycAuraWorkspace.GetStudies: TAuraArray<IAuraStudy>;
begin
    Result := FStudies;
end;

function TMycAuraWorkspace.GetTradingMode: TWriteable<TAuraTradingMode>;
begin
    Result := FTradingMode;
end;

constructor TAuraChilds<T>.Create(const AChilds: IAuraChilds<T>);
begin
    FChilds := AChilds;
end;

procedure TAuraChilds<T>.Delete(Idx: Integer);
begin
    FChilds.Delete(Idx);
end;

function TAuraChilds<T>.GetCaption: string;
begin
    Result := FChilds.Caption;
end;

function TAuraChilds<T>.IndexOf(const Item: T): Integer;
begin
    Result := FChilds.IndexOf(Item);
end;

procedure TAuraChilds<T>.Insert(Idx: Integer; const Item: T);
begin
    FChilds.Insert(Idx, Item);
end;

procedure TAuraChilds<T>.Serialize(const Write: TJsonWriter);
begin
    FChilds.Serialize(Write);
end;

class operator TAuraChilds<T>.Implicit(const A: IAuraChilds<T>): TAuraChilds<T>;
begin
    Result.Create(A);
end;

class operator TAuraChilds<T>.Implicit(const A: TAuraChilds<T>): IAuraChilds<T>;
begin
    Result := A.FChilds;
end;

end.
