using System.Text;
using Strata.SqlTools.SqlBreakdown.Classes;
using Strata.SqlTools.SqlBreakdown.Expressions;
using Strata.SqlTools.SqlBreakdown.Expressions.Arithmetic;
using Strata.SqlTools.SqlBreakdown.Expressions.Conditional;
using Strata.SqlTools.SqlBreakdown.Expressions.Conditional.Comparisons;
using Strata.SqlTools.SqlBreakdown.Expressions.Conditional.Logical;
using Strata.SqlTools.SqlBreakdown.Expressions.Functions;
using Strata.SqlTools.SqlBreakdown.Expressions.Functions.Aggregate;
using Strata.SqlTools.SqlBreakdown.Expressions.Functions.Conditional;
using Strata.SqlTools.SqlBreakdown.Expressions.Literals;
using Strata.SqlTools.SqlBreakdown.Interfaces.Core;
namespace Strata.SqlTools.Markdown.Expressions;
///
/// Generates markdown documentation for Expression trees.
/// Creates human-readable documentation with expression structure, type information, and visual representations.
///
public class ExpressionGenerator : IVisitor
{
private int _indentLevel = 0;
private readonly string _indentString = " ";
///
/// Generates markdown documentation from an Expression tree.
///
/// The expression to document.
/// Optional title for the documentation.
/// A markdown formatted string documenting the expression.
public string GenerateMarkdown(Expression expression, string? title = null)
{
var sb = new StringBuilder();
if (!string.IsNullOrWhiteSpace(title))
{
sb.AppendLine($"# {title}");
sb.AppendLine();
}
sb.AppendLine("## Expression Structure");
sb.AppendLine();
sb.AppendLine("```");
_indentLevel = 0;
sb.AppendLine(expression.Accept(this));
sb.AppendLine("```");
sb.AppendLine();
sb.AppendLine("## Expression Type");
sb.AppendLine();
sb.AppendLine($"**Type:** `{expression.GetType().Name}`");
sb.AppendLine();
sb.AppendLine("## Mermaid Diagram");
sb.AppendLine();
sb.AppendLine(GenerateMermaidDiagram(expression));
sb.AppendLine();
sb.AppendLine("## Mathematical Expression");
sb.AppendLine();
sb.AppendLine(GenerateMathematicalExpression(expression));
return sb.ToString();
}
///
/// Generates a mathematical expression using LaTeX notation for GitHub markdown.
///
/// The expression to convert to mathematical notation.
/// If true, generates inline math ($...$), otherwise block math ($$...$$).
/// A string containing the LaTeX mathematical expression.
public static string GenerateMathematicalExpression(Expression expression, bool inline = false)
{
var latex = ConvertToLatex(expression);
return inline ? $"${latex}$" : $"$$\n{latex}\n$$";
}
///
/// Generates a mathematical expression from raw LaTeX in markdown format.
///
/// The LaTeX expression.
/// The format to use: "dollar" for $/$$ delimiters, "math" for ```math code fence.
/// If true and format is "dollar", generates inline math ($...$), otherwise block math ($$...$$). Ignored for "math" format.
/// A string containing the formatted mathematical expression.
public static string GenerateRawMathematicalExpression(string latex, string format = "dollar", bool inline = false)
{
return format.ToLower() switch
{
"math" => $"```math\n{latex}\n```",
_ => inline ? $"${latex}$" : $"$$\n{latex}\n$$"
};
}
private static string ConvertToLatex(Expression expression)
{
return expression switch
{
// Arithmetic expressions
ArithmeticExpression arith => ConvertArithmeticToLatex(arith),
// Comparison expressions
ComparisonOperatorExpression comp => ConvertComparisonToLatex(comp),
// Logical expressions
AndExpression and => $"({ConvertToLatex(and.ExpressionA)} \\land {ConvertToLatex(and.ExpressionB)})",
OrExpression or => $"({ConvertToLatex(or.ExpressionA)} \\lor {ConvertToLatex(or.ExpressionB)})",
NotExpression not => $"\\neg({ConvertToLatex(not.ExpressionA)})",
// Literals
NumberLiteralExpression num => num.Value.ToString() ?? "0",
StringLiteralExpression str => $"\\text{{\"{EscapeLatex(str.Value)}\"}}",
BooleanLiteralExpression b => b.Value ? "\\text{true}" : "\\text{false}",
NullLiteralExpression => "\\text{NULL}",
// Column expressions
ColumnExpression col => $"\\text{{{EscapeLatex(col.ColumnName)}}}",
// Parameter expressions
ParameterExpression param => $"@{EscapeLatex(param.ParameterName)}",
// Case expressions (before FunctionExpression since it's a subclass)
CaseExpression caseExpr => ConvertCaseToLatex(caseExpr),
// Functions
FunctionExpression func => ConvertFunctionToLatex(func),
// Between expressions
BetweenExpression between => $"{ConvertToLatex(between.Expression)} \\in [{ConvertToLatex(between.LowerBound)}, {ConvertToLatex(between.UpperBound)}]",
// IN expressions
InExpression inExpr => $"{ConvertToLatex(inExpr.SearchExpression)} \\in \\{{{string.Join(", ", inExpr.ValuesToCompare.Select(ConvertToLatex))}\\}}",
// LIKE expressions
LikeExpression like => $"{ConvertToLatex(like.Subject)} \\approx \\text{{\"{EscapeLatex(ConvertExpressionToString(like.Pattern))}\"}}",
_ => $"\\text{{{EscapeLatex(expression.GetType().Name)}}}"
};
}
private static string ConvertArithmeticToLatex(ArithmeticExpression arith)
{
var left = ConvertToLatex(arith.ExpressionA);
var right = ConvertToLatex(arith.ExpressionB);
var op = arith.ArithmeticOperator switch
{
"+" => "+",
"-" => "-",
"*" => "\\times",
"/" => "\\div",
"%" => "\\bmod",
_ => "?"
};
return $"({left} {op} {right})";
}
private static string ConvertComparisonToLatex(ComparisonOperatorExpression comp)
{
var left = ConvertToLatex(comp.ExpressionA);
var right = ConvertToLatex(comp.ExpressionB);
var op = comp.Operator switch
{
"=" => "=",
"<>" => "\\neq",
"!=" => "\\neq",
">" => ">",
">=" => "\\geq",
"<" => "<",
"<=" => "\\leq",
_ => "?"
};
return $"({left} {op} {right})";
}
private static string ConvertFunctionToLatex(FunctionExpression func)
{
var args = string.Join(", ", func.Arguments.Select(ConvertToLatex));
var funcName = EscapeLatex(func.FunctionName);
return func.FunctionName.ToUpper() switch
{
// Aggregate functions
"COUNT" => $"\\text{{COUNT}}({args})",
"SUM" => $"\\sum({args})",
"AVG" => $"\\text{{AVG}}({args})",
"MIN" => $"\\min({args})",
"MAX" => $"\\max({args})",
// Math functions
"ABS" => $"|{args}|",
"SQRT" => $"\\sqrt{{{args}}}",
"POWER" when func.Arguments.Length >= 2 =>
$"{ConvertToLatex(func.Arguments[0])}^{{{ConvertToLatex(func.Arguments[1])}}}",
"LOG" => $"\\log({args})",
"EXP" => $"e^{{{args}}}",
// Default
_ => $"\\text{{{funcName}}}({args})"
};
}
private static string ConvertCaseToLatex(CaseExpression caseExpr)
{
var sb = new StringBuilder();
sb.Append("\\begin{cases}\n");
foreach (var (condition, result) in caseExpr.ConditionResultPairs)
{
sb.Append($" {ConvertToLatex(result)} & \\text{{if }} {ConvertToLatex(condition)} \\\\\n");
}
if (caseExpr.ElseResultExpression is not null)
{
sb.Append($" {ConvertToLatex(caseExpr.ElseResultExpression)} & \\text{{otherwise}}\n");
}
sb.Append("\\end{cases}");
return sb.ToString();
}
private static string ConvertExpressionToString(Expression expression)
{
return expression switch
{
StringLiteralExpression str => str.Value,
_ => expression.ToString() ?? ""
};
}
private static string EscapeLatex(string text)
{
return text
.Replace("\\", "\\\\")
.Replace("{", "\\{")
.Replace("}", "\\}")
.Replace("_", "\\_")
.Replace("$", "\\$")
.Replace("%", "\\%")
.Replace("&", "\\&")
.Replace("#", "\\#");
}
///
/// Generates a Mermaid tree diagram from an Expression tree.
///
private string GenerateMermaidDiagram(Expression expression)
{
var sb = new StringBuilder();
sb.AppendLine("```mermaid");
sb.AppendLine("graph TD");
sb.AppendLine();
int nodeCounter = 0;
var nodeMap = new Dictionary