unit Myc.Ast.Debugger;

interface

uses
    System.SysUtils,
    System.Classes,
    System.Generics.Collections,
    Myc.Data.Scalar,
    Myc.Data.Value,
    Myc.Ast.Nodes,
    Myc.Ast.Scope,
    Myc.Ast,
    Myc.Ast.Visitor,
    Myc.Ast.Evaluator;

type
    TDebugEvaluatorVisitor = class(TEvaluatorVisitor)
    private
        FLog: TStrings;
        FIndentLevel: Integer;
        FShowScope: Boolean;

        procedure Indent;
        procedure Unindent;
        procedure AppendLine(const S: string);
        procedure ShowScope;
        function GetNodeLogInfo(const Node: IAstNode): string;
    protected
        function CreateVisitorFactory: TEvaluatorFactory; override;

        // Central Interceptor for all node types
        function Visit(const Node: IAstNode): TDataValue; override;

        // Specific overrides for enhanced logging/state observation
        function VisitVariableDeclaration(const N: IVariableDeclarationNode): TDataValue; override;
        function VisitAssignment(const N: IAssignmentNode): TDataValue; override;
        function VisitFunctionCall(const N: IFunctionCallNode): TDataValue; override;
        function VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue; override;
    public
        constructor Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0);
    end;

implementation

uses
    System.TypInfo,
    Myc.Data.Keyword;

{ TDebugEvaluatorVisitor }

constructor TDebugEvaluatorVisitor.Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer);
begin
    inherited Create(AScope);
    FLog := ALog;
    FIndentLevel := AInitialIndent;
    FShowScope := AShowScope;
    if FShowScope and (AInitialIndent = 0) then
        ShowScope;
end;

function TDebugEvaluatorVisitor.CreateVisitorFactory: TEvaluatorFactory;
begin
    var currentLog := FLog;
    var currentShowScope := FShowScope;
    var currentIndent := FIndentLevel;
    Result :=
        function(const AScope: IExecutionScope): IEvaluatorVisitor
        begin
            Result := TDebugEvaluatorVisitor.Create(AScope, currentLog, currentShowScope, currentIndent);
        end;
end;

function TDebugEvaluatorVisitor.Visit(const Node: IAstNode): TDataValue;
var
    info: string;
begin
    info := GetNodeLogInfo(Node);
    if info <> '' then
    begin
        AppendLine(info + ' {');
        Indent;
    end;

    try
        // Dispatch to inherited logic (which will call our specialized VisitXyz overrides)
        Result := inherited Visit(Node);
    finally
        if info <> '' then
        begin
            Unindent;
            var resStr :=
                if Result.IsVoid then '(void)'
                else Result.ToString;
            AppendLine('} -> ' + resStr);
        end;
    end;
end;

// --- Enhanced Observation Overrides ---

function TDebugEvaluatorVisitor.VisitVariableDeclaration(const N: IVariableDeclarationNode): TDataValue;
begin
    Result := inherited VisitVariableDeclaration(N);
    if FShowScope then
        ShowScope;
end;

function TDebugEvaluatorVisitor.VisitAssignment(const N: IAssignmentNode): TDataValue;
begin
    Result := inherited VisitAssignment(N);
    if FShowScope then
        ShowScope;
end;

function TDebugEvaluatorVisitor.VisitFunctionCall(const N: IFunctionCallNode): TDataValue;
begin
    // Log target purity if statically known
    if Assigned(N.StaticTarget) and N.IsTargetPure then
        AppendLine('[Pure Static Call]');
    Result := inherited VisitFunctionCall(N);
end;

function TDebugEvaluatorVisitor.VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue;
begin
    if Length(N.Upvalues) > 0 then
        AppendLine(Format('[Capturing %d upvalues]', [Length(N.Upvalues)]));
    Result := inherited VisitLambdaExpression(N);
end;

// --- Internal Helpers ---

function TDebugEvaluatorVisitor.GetNodeLogInfo(const Node: IAstNode): string;
begin
    case Node.Kind of
        akFunctionCall:
        begin
            var c := Node.AsFunctionCall;
            var mode :=
                if Assigned(c.StaticTarget) then 'STATIC'
                else 'DYNAMIC';
            Result := Format('Call (%s, Tail=%s)', [mode, c.IsTailCall.ToString(TUseBoolStrs.True)]);
        end;
        akIdentifier: Result := 'ID:' + Node.AsIdentifier.Name;
        akVariableDeclaration: Result := 'DEF:' + Node.AsVariableDeclaration.Target.AsIdentifier.Name;
        akAssignment: Result := 'ASSIGN:' + Node.AsAssignment.Target.AsIdentifier.Name;
        akIfExpression: Result := 'IF';
        akCondExpression: Result := 'COND';
        akRecur: Result := 'RECUR';
        akBlockExpression: Result := 'BLOCK';
        akLambdaExpression: Result := 'FN';
        akRecordLiteral: Result := 'RECORD';
        akPipe: Result := 'PIPE';
    else
        Result := ''; // Silence leaf nodes like Constants and Keywords to reduce noise
    end;
end;

procedure TDebugEvaluatorVisitor.Indent;
begin
    Inc(FIndentLevel);
end;
procedure TDebugEvaluatorVisitor.Unindent;
begin
    Dec(FIndentLevel);
end;

procedure TDebugEvaluatorVisitor.AppendLine(const S: string);
var
    pad: string;
    i: Integer;
begin
    pad := '';
    for i := 0 to FIndentLevel - 1 do
        pad := pad + ':' + ''.PadLeft(3);
    FLog.Add(pad + S);
end;

procedure TDebugEvaluatorVisitor.ShowScope;
var
    line: string;
begin
    AppendLine('  [Scope State]');
    for line in Scope.Dump.Split([sLineBreak]) do
        AppendLine('  ' + line);
end;

end.
