unit Myc.Fmx.AstEditor.Handlers.Control;

interface

uses
    System.SysUtils,
    System.UITypes,
    System.Generics.Collections,
    Myc.Ast,
    Myc.Ast.Nodes,
    Myc.Fmx.AstEditor.Render,
    Myc.Fmx.AstEditor.Node;

type
    // --- Logic & Control Flow ---

    TBlockExpressionNodeHandler = class(TBaseNodeHandler<IBlockExpressionNode>)
    private
        FExpressionsNode: TAstViewNode;
    public
        procedure BuildUI(OwnerNode: TAstViewNode); override;
        function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
    end;

    TIfExpressionNodeHandler = class(TBaseNodeHandler<IIfExpressionNode>)
    private
        FConditionNode: TAstViewNode;
        FThenNode: TAstViewNode;
        FElseNode: TAstViewNode;
    public
        procedure BuildUI(OwnerNode: TAstViewNode); override;
        function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
    end;

    // Handles the 'cond' expression (multi-branch if/else)
    TCondExpressionNodeHandler = class(TBaseNodeHandler<ICondExpressionNode>)
    private
        type
            TBranchView = record
                Condition: TAstViewNode;
                Branch: TAstViewNode;
            end;
    private
        FBranchNodes: TList<TBranchView>;
        FElseNode: TAstViewNode;
    public
        constructor Create(const ANode: ICondExpressionNode);
        destructor Destroy; override;
        procedure BuildUI(OwnerNode: TAstViewNode); override;
        function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
    end;

    // --- Functions & Calls ---

    TLambdaExpressionNodeHandler = class(TBaseNodeHandler<ILambdaExpressionNode>)
    private
        FParamsNode: TAstViewNode;
        FBodyNode: TAstViewNode;
    public
        procedure BuildUI(OwnerNode: TAstViewNode); override;
        function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
    end;

    TFunctionCallNodeHandler = class(TBaseNodeHandler<IFunctionCallNode>)
    private
        FCalleeNode: TAstViewNode;
        FArgumentsNode: TAstViewNode;
    public
        procedure BuildUI(OwnerNode: TAstViewNode); override;
        function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
    end;

    // Renders function calls as infix operators (e.g. "A + B" instead of "call + A B")
    TOperatorCallHandler = class(TBaseNodeHandler<IFunctionCallNode>)
    private
        FArgumentNodes: TList<TAstViewNode>;
        FCalleeName: string;
    public
        constructor Create(const ANode: IFunctionCallNode);
        destructor Destroy; override;
        procedure BuildUI(OwnerNode: TAstViewNode); override;
        function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
    end;

    TMacroExpansionNodeHandler = class(TBaseNodeHandler<IMacroExpansionNode>)
    private
        FExpandedBodyNode: TAstViewNode;
    public
        procedure BuildUI(OwnerNode: TAstViewNode); override;
        function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
    end;

    TRecurNodeHandler = class(TBaseNodeHandler<IRecurNode>)
    private
        FArgumentsNode: TAstViewNode;
    public
        procedure BuildUI(OwnerNode: TAstViewNode); override;
        function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
    end;

    TMacroDefinitionNodeHandler = class(TBaseNodeHandler<IMacroDefinitionNode>)
    private
        FParamsNode: TAstViewNode;
        FBodyNode: TAstViewNode;
    public
        procedure BuildUI(OwnerNode: TAstViewNode); override;
        function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override;
    end;

implementation

{ TBlockExpressionNodeHandler }

procedure TBlockExpressionNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
    visu: IAstVisualizer;
    titleLabel: TTextNode;
begin
    visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);

    OwnerNode.Frameless := False;
    OwnerNode.Orientation := loVertical;
    OwnerNode.Alignment := laFlush;

    titleLabel := OwnerNode.AddLabel(OwnerNode, 'do');
    titleLabel.FontSettings.Font.Style := [TFontStyle.fsBold];

    FExpressionsNode := visu.CallAccept(FNode.Expressions);
    FExpressionsNode.Frameless := True;
end;

function TBlockExpressionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
    Result := TAst.Block(FNode.Identity, FExpressionsNode.CreateAst.AsExpressionList, FNode.StaticType);
end;

{ TIfExpressionNodeHandler }

procedure TIfExpressionNodeHandler.BuildUI(OwnerNode: TAstViewNode);
begin
    OwnerNode.Frameless := False;
    OwnerNode.Orientation := loVertical;
    OwnerNode.Alignment := laFlush;

    FConditionNode := OwnerNode.AddExpr(OwnerNode, 'if', FNode.Condition);
    FThenNode := OwnerNode.AddExpr(OwnerNode, 'then', FNode.ThenBranch);
    if Assigned(FNode.ElseBranch) then
        FElseNode := OwnerNode.AddExpr(OwnerNode, 'else', FNode.ElseBranch);
end;

function TIfExpressionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
    elseAst: IAstNode;
begin
    if Assigned(FElseNode) then
        elseAst := FElseNode.CreateAst
    else
        elseAst := nil;

    Result := TAst.IfExpr(FNode.Identity, FConditionNode.CreateAst, FThenNode.CreateAst, elseAst, FNode.StaticType);
end;

{ TCondExpressionNodeHandler }

constructor TCondExpressionNodeHandler.Create(const ANode: ICondExpressionNode);
begin
    inherited Create(ANode);
    FBranchNodes := TList<TBranchView>.Create;
end;

destructor TCondExpressionNodeHandler.Destroy;
begin
    FBranchNodes.Free;
    inherited;
end;

procedure TCondExpressionNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
    visu: IAstVisualizer;
    pair: TCondPair;
    i: Integer;
    row: TAutoFitLayout;
    bv: TBranchView;
begin
    visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);

    OwnerNode.Frameless := False;
    OwnerNode.Orientation := loVertical;
    OwnerNode.Alignment := laFlush;

    var title := OwnerNode.AddLabel(OwnerNode, '?');
    title.FontSettings.Font.Style := [TFontStyle.fsBold];

    for i := 0 to High(FNode.Pairs) do
    begin
        pair := FNode.Pairs[i];
        row := OwnerNode.AddContainer(OwnerNode, loHorizontal, laCenter);
        row.Margins := TMarginRect.Create(0, 2, 0, 2);

        bv.Condition := visu.CallAccept(pair.Condition);
        row.AddChild(bv.Condition);

        OwnerNode.AddLabel(row, '->');
        bv.Branch := visu.CallAccept(pair.Branch);
        row.AddChild(bv.Branch);

        FBranchNodes.Add(bv);
    end;

    // Else Branch
    var elseRow := OwnerNode.AddContainer(OwnerNode, loHorizontal, laCenter);
    elseRow.Margins := TMarginRect.Create(0, 4, 0, 0); // Extra spacing
    OwnerNode.AddLabel(elseRow, 'else ->');
    FElseNode := visu.CallAccept(FNode.ElseBranch);
    // FIX: Use AddChild instead of assigning Parent directly
    elseRow.AddChild(FElseNode);
end;

function TCondExpressionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
    newPairs: TArray<TCondPair>;
    i: Integer;
begin
    SetLength(newPairs, FBranchNodes.Count);
    for i := 0 to FBranchNodes.Count - 1 do
    begin
        newPairs[i] := TCondPair.Create(FBranchNodes[i].Condition.CreateAst, FBranchNodes[i].Branch.CreateAst);
    end;

    Result := TAst.CondExpr(FNode.Identity, newPairs, FElseNode.CreateAst, FNode.StaticType);
end;

{ TLambdaExpressionNodeHandler }

procedure TLambdaExpressionNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
    visu: IAstVisualizer;
    titleContainer: TAutoFitLayout;
    lbl: TTextNode;
begin
    visu := OwnerNode.Visualizer.Clone(OwnerNode, 0);

    OwnerNode.Frameless := False;
    OwnerNode.Orientation := loVertical;
    OwnerNode.Alignment := laFlush;
    OwnerNode.BackgroundColor := $090000ff;

    titleContainer := OwnerNode.AddContainer(OwnerNode, loHorizontal, laCenter);
    lbl := OwnerNode.AddLabel(titleContainer, 'fn');
    lbl.FontSettings.Font.Style := [TFontStyle.fsBold];

    // Parameters: Delegate to List Handler
    var paramVisu := visu.Clone(titleContainer, 0);
    FParamsNode := paramVisu.CallAccept(FNode.Parameters);

    // Body
    FBodyNode := visu.CallAccept(FNode.Body);
    FBodyNode.Frameless := False;
end;

function TLambdaExpressionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
    Result :=
        TAst.LambdaExpr(
            FNode.Identity,
            FParamsNode.CreateAst.AsParameterList,
            FBodyNode.CreateAst,
            FNode.Layout,
            FNode.Descriptor,
            FNode.Upvalues,
            FNode.HasNestedLambdas,
            FNode.IsPure,
            FNode.StaticType
        );
end;

{ TFunctionCallNodeHandler }

procedure TFunctionCallNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
    visu: IAstVisualizer;
    callLabel: TTextNode;
begin
    visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);

    OwnerNode.Frameless := True;
    OwnerNode.Orientation := loHorizontal;

    callLabel := OwnerNode.AddLabel(OwnerNode, 'call ');
    callLabel.FontSettings.Font.Style := [TFontStyle.fsBold];

    FCalleeNode := visu.CallAccept(FNode.Callee);

    FArgumentsNode := visu.CallAccept(FNode.Arguments);
end;

function TFunctionCallNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
    Result :=
        TAst.FunctionCall(
            FNode.Identity,
            FCalleeNode.CreateAst,
            FArgumentsNode.CreateAst.AsArgumentList,
            FNode.StaticType,
            FNode.IsTailCall,
            FNode.StaticTarget,
            FNode.IsTargetPure
        );
end;

{ TOperatorCallHandler }

constructor TOperatorCallHandler.Create(const ANode: IFunctionCallNode);
begin
    inherited Create(ANode);
    FArgumentNodes := TList<TAstViewNode>.Create;

    if ANode.Callee.Kind = akIdentifier then
        FCalleeName := ANode.Callee.AsIdentifier.Name
    else if ANode.Callee.Kind = akKeyword then
        FCalleeName := ANode.Callee.AsKeyword.Value.Name
    else
        FCalleeName := '?';
end;

destructor TOperatorCallHandler.Destroy;
begin
    FArgumentNodes.Free;
    inherited;
end;

procedure TOperatorCallHandler.BuildUI(OwnerNode: TAstViewNode);
var
    visu: IAstVisualizer;
    i: Integer;
    argView: TAstViewNode;
    opLabel: TTextNode;
begin
    visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);

    OwnerNode.Frameless := True;
    OwnerNode.Orientation := loHorizontal;
    OwnerNode.Alignment := laCenter;

    if FNode.Arguments.Count = 1 then
    begin
        opLabel := OwnerNode.AddLabel(OwnerNode, FCalleeName);
        opLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
        opLabel.Color := TAlphaColors.Darkorange;
        opLabel.Margins := TMarginRect.Create(2, 0, 2, 0);

        argView := visu.CallAccept(FNode.Arguments[0]);
        FArgumentNodes.Add(argView);
    end
    else
    begin
        for i := 0 to FNode.Arguments.Count - 1 do
        begin
            if i > 0 then
            begin
                opLabel := OwnerNode.AddLabel(OwnerNode, ' ' + FCalleeName + ' ');
                opLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
                opLabel.Color := TAlphaColors.Darkorange;
                // Compact spacing for operators
                opLabel.Margins := TMarginRect.Create(0, 0, 0, 0);
            end;

            argView := visu.CallAccept(FNode.Arguments[i]);
            FArgumentNodes.Add(argView);
        end;
    end;

    if FNode.Arguments.Count = 0 then
    begin
        opLabel := OwnerNode.AddLabel(OwnerNode, FCalleeName);
        opLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
    end;
end;

function TOperatorCallHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
var
    newArgs: TArray<IAstNode>;
    i: Integer;
begin
    SetLength(newArgs, FArgumentNodes.Count);
    for i := 0 to FArgumentNodes.Count - 1 do
        newArgs[i] := FArgumentNodes[i].CreateAst;

    Result :=
        TAst.FunctionCall(
            FNode.Identity,
            FNode.Callee,
            TArgumentList.Create(newArgs, FNode.Arguments.Identity),
            FNode.StaticType,
            FNode.IsTailCall,
            FNode.StaticTarget,
            FNode.IsTargetPure
        );
end;

{ TMacroExpansionNodeHandler }

procedure TMacroExpansionNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
    visu: IAstVisualizer;
    titleLabel: TTextNode;
begin
    visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);

    OwnerNode.Frameless := False;
    OwnerNode.Orientation := loVertical;
    titleLabel := OwnerNode.AddLabel(OwnerNode, 'Macro Expansion');
    titleLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
    FExpandedBodyNode := visu.CallAccept(FNode.ExpandedBody);
end;

function TMacroExpansionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
    Result := TAst.MacroExpansionNode(FNode.Identity, FNode.CallNode, FExpandedBodyNode.CreateAst.AsTypedNode);
end;

{ TRecurNodeHandler }

procedure TRecurNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
    visu: IAstVisualizer;
    titleLabel: TTextNode;
begin
    visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);

    OwnerNode.Frameless := True;
    OwnerNode.Orientation := loHorizontal;

    titleLabel := OwnerNode.AddLabel(OwnerNode, 'recur');
    titleLabel.FontSettings.Font.Style := [TFontStyle.fsBold];

    FArgumentsNode := visu.CallAccept(FNode.Arguments);
end;

function TRecurNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
    Result := TAst.Recur(FNode.Identity, FArgumentsNode.CreateAst.AsArgumentList, FNode.StaticType);
end;

{ TMacroDefinitionNodeHandler }

procedure TMacroDefinitionNodeHandler.BuildUI(OwnerNode: TAstViewNode);
var
    visu: IAstVisualizer;
    titleLabel: TTextNode;
    titleContainer: TAutoFitLayout;
begin
    visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);

    OwnerNode.Frameless := False;
    OwnerNode.Orientation := loVertical;

    titleContainer := OwnerNode.AddContainer(OwnerNode, loHorizontal, laCenter);
    titleLabel := OwnerNode.AddLabel(titleContainer, 'Macro Def: ' + FNode.Name.Name);
    titleLabel.FontSettings.Font.Style := [TFontStyle.fsBold];

    // Parameters List
    var paramVisu := visu.Clone(titleContainer, visu.ExprDepth);
    FParamsNode := paramVisu.CallAccept(FNode.Parameters);

    FBodyNode := visu.CallAccept(FNode.Body);
end;

function TMacroDefinitionNodeHandler.ReconstructAst(OwnerNode: TAstViewNode): IAstNode;
begin
    Result := TAst.MacroDef(FNode.Identity, FNode.Name, FParamsNode.CreateAst.AsParameterList, FBodyNode.CreateAst);
end;

end.
