unit Myc.Fmx.AstEditor.Handlers;

interface

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

type
    // --- Abstract List Base ---
    // Implements IReorderable to support drag & drop reordering within the virtual tree
    TNodeListHandler<T: IAstNode> = class(TBaseNodeHandler<INodeList<T>>, IReorderable)
    protected
        FChildNodes: TList<TAstViewNode>;
        function GetOrientation: TLayoutOrientation; virtual; abstract;
        function GetAlignment: TLayoutAlignment; virtual;
    public
        constructor Create(const ANode: INodeList<T>);
        destructor Destroy; override;
        procedure BuildUI(OwnerNode: TAstViewNode); override;
        function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override; abstract;

        // IReorderable Implementation
        function CanDrag(Child: TVisualNode): Boolean;
        procedure MoveChild(SourceIndex, TargetIndex: Integer);
    end;

implementation

{ TNodeListHandler<T> }

constructor TNodeListHandler<T>.Create(const ANode: INodeList<T>);
begin
    inherited Create(ANode);
    FChildNodes := TList<TAstViewNode>.Create;
end;

destructor TNodeListHandler<T>.Destroy;
begin
    FChildNodes.Free;
    inherited;
end;

function TNodeListHandler<T>.GetAlignment: TLayoutAlignment;
begin
    Result := TLayoutAlignment.laCenter;
end;

procedure TNodeListHandler<T>.BuildUI(OwnerNode: TAstViewNode);
var
    visu: IAstVisualizer;
    i: Integer;
    childView: TAstViewNode;
    openTok, closeTok, sep: string;
    listId: IListIdentity;
begin
    if (FNode.Identity <> nil) and (FNode.Identity.Kind = ikList) then
    begin
        listId := FNode.Identity.AsList;
        openTok := listId.OpenToken;
        closeTok := listId.CloseToken;
        sep := listId.Separator;
    end
    else
    begin
        openTok := '';
        closeTok := '';
        sep := ' ';
    end;

    visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);

    // No BeginUpdate/EndUpdate needed for TVisualNode, just property setting
    OwnerNode.Frameless := True;
    OwnerNode.Orientation := GetOrientation;
    OwnerNode.Alignment := GetAlignment;

    if openTok <> '' then
        OwnerNode.AddLabel(OwnerNode, openTok);

    for i := 0 to FNode.Count - 1 do
    begin
        childView := visu.CallAccept(FNode[i]);
        FChildNodes.Add(childView);

        if (i < FNode.Count - 1) and (sep <> '') and (sep <> ' ') then
            OwnerNode.AddLabel(OwnerNode, sep);
    end;

    if closeTok <> '' then
        OwnerNode.AddLabel(OwnerNode, closeTok);

    if FNode.Count = 0 then
    begin
        if (openTok = '') and (closeTok = '') then
        begin
            var lbl := OwnerNode.AddLabel(OwnerNode, 'ø');
            lbl.Opacity := 0.5;
        end;
    end;
end;

// IReorderable Implementation

function TNodeListHandler<T>.CanDrag(Child: TVisualNode): Boolean;
begin
    // Only allow dragging if the child is one of our managed ViewNodes
    if Child is TAstViewNode then
        Result := FChildNodes.Contains(TAstViewNode(Child))
    else
        Result := False;
end;

procedure TNodeListHandler<T>.MoveChild(SourceIndex, TargetIndex: Integer);
var
    item: TAstViewNode;
begin
    if (SourceIndex < 0) or (SourceIndex >= FChildNodes.Count) then
        Exit;
    if (TargetIndex < 0) or (TargetIndex > FChildNodes.Count) then
        Exit;

    // Adjust target index if moving downwards (since removing source shifts indices)
    if SourceIndex < TargetIndex then
        Dec(TargetIndex);

    if SourceIndex = TargetIndex then
        Exit;

    item := FChildNodes[SourceIndex];
    FChildNodes.Delete(SourceIndex);
    FChildNodes.Insert(TargetIndex, item);

    // The visual order update happens via the controller's compilation cycle
end;

end.
