// Copyright (c) 2024 LinZeming
// Released under the MIT License
//
// AST — Abstract Syntax Tree node definitions for the template engine.
//
// The AST is produced by the parser and consumed by the renderer.  It
// represents the complete structure of a compiled template.

// ---------------------------------------------------------------------------
// Runtime value type
// ---------------------------------------------------------------------------

///|
/// Runtime value — used for template context data and literal values.
pub(all) enum Value {
  Null
  Bool(Bool)
  Int(Int64)
  Float(Double)
  Str(String)
  Array(Array[Value])
  Object(Map[String, Value])
} derive(Debug)

///|
/// Convert a `Value` to its string representation (for output).
pub fn Value::to_string(self : Value) -> String {
  match self {
    Null => "null"
    Bool(b) => if b { "true" } else { "false" }
    Int(i) => i.to_string()
    Float(f) => f.to_string()
    Str(s) => s
    Array(arr) => {
      let mut result = "["
      let len = arr.length()
      for i = 0; i < len; i = i + 1 {
        result = result + arr[i].to_string()
        if i + 1 < len {
          result = result + ", "
        }
      }
      result + "]"
    }
    Object(_) => "[object]"
  }
}

///|
/// Test whether a value is "truthy" (for use in `if` conditions).
pub fn Value::is_truthy(self : Value) -> Bool {
  match self {
    Null => false
    Bool(b) => b
    Int(i) => i != 0L
    Float(f) => f != 0.0
    Str(s) => s != ""
    Array(arr) => arr.length() > 0
    Object(m) => m.length() > 0
  }
}

///|
/// Return a human-readable type name for this value.
pub fn Value::type_name(self : Value) -> String {
  match self {
    Null => "null"
    Bool(_) => "bool"
    Int(_) => "int"
    Float(_) => "float"
    Str(_) => "string"
    Array(_) => "array"
    Object(_) => "object"
  }
}

// ---------------------------------------------------------------------------
// Expression nodes
// ---------------------------------------------------------------------------

///|
/// Binary operator kinds.
pub(all) enum BinOpKind {
  Add
  Sub
  Mul
  Div
  Mod
  Eq
  Neq
  Lt
  Gt
  Le
  Ge
  And
  Or
} derive(Debug)

///|
/// Unary operator kinds.
pub(all) enum UnaryOpKind {
  Not
  Neg
} derive(Debug)

///|
/// Expression node — evaluates to a `Value` at render time.
pub(all) enum Expr {
  Literal(Value)                       // literal value (42, "hello", true, …)
  Variable(String)                     // variable reference (user.name)
  Member(Expr, String)                 // member access  a.b.c
  Index(Expr, Expr)                    // index access    a[b]
  Call(String, Array[Expr])            // function / filter call
  BinOp(Expr, BinOpKind, Expr)         // binary operation a + b, a and b
  UnaryOp(UnaryOpKind, Expr)           // unary operation  not x, -x
  Filter(Expr, String, Array[Expr])    // filter chain:  value | filter(args)
} derive(Debug)

// ---------------------------------------------------------------------------
// Template statement nodes
// ---------------------------------------------------------------------------

///|
/// A single elif (or else) branch inside an `{% if %}` block.
///
/// `condition = None` represents the final `{% else %}` clause.
pub(all) struct ElifBranch {
  condition : Option[Expr]
  body : Array[Node]
} derive(Debug)

///|
/// Template node — a top-level statement inside a template.
pub(all) enum Node {
  Text(String)                                       // plain text output
  Output(Expr)                                       // {{ expr }} interpolation
  If(Expr, Array[Node], Array[ElifBranch], Array[Node]) // {% if cond %}…{% elif %}…{% else %}…{% endif %}
  For(String, Expr, Array[Node])                     // {% for var in iterable %}…{% endfor %}
  Block(String, Array[Node])                         // {% block name %}…{% endblock %}
  Extends(String)                                    // {% extends "parent.html" %}
  Include(String)                                    // {% include "partial.html" %}
  Macro(String, Array[String], Array[Node])          // {% macro name(args) %}…{% endmacro %}
  FilterBlock(String, Array[Expr], Array[Node])      // {% filter name(args) %}…{% endfilter %}
} derive(Debug)

// Template struct is defined in template.mbt (parse + render entry point).