<?php
/**
 * Generated by Haxe 4.1.5
 */

namespace haxe;

use \php\_Boot\HxAnon;
use \php\Boot;
use \php\_Boot\HxString;
use \haxe\ds\List_hx;
use \haxe\_Template\TemplateExpr;
use \haxe\iterators\ArrayIterator;

/**
 * `Template` provides a basic templating mechanism to replace values in a source
 * String, and to have some basic logic.
 * A complete documentation of the supported syntax is available at:
 * <https://haxe.org/manual/std-template.html>
 */
class Template {
	/**
	 * @var \EReg
	 */
	static public $expr_float;
	/**
	 * @var \EReg
	 */
	static public $expr_int;
	/**
	 * @var \EReg
	 */
	static public $expr_splitter;
	/**
	 * @var \EReg
	 */
	static public $expr_trim;
	/**
	 * @var mixed
	 * Global replacements which are used across all `Template` instances. This
	 * has lower priority than the context argument of `execute()`.
	 */
	static public $globals;
	/**
	 * @var ArrayIterator
	 */
	static public $hxKeepArrayIterator;
	/**
	 * @var \EReg
	 */
	static public $splitter;

	/**
	 * @var \StringBuf
	 */
	public $buf;
	/**
	 * @var mixed
	 */
	public $context;
	/**
	 * @var TemplateExpr
	 */
	public $expr;
	/**
	 * @var mixed
	 */
	public $macros;
	/**
	 * @var List_hx
	 */
	public $stack;

	/**
	 * Creates a new `Template` instance from `str`.
	 * `str` is parsed into tokens, which are stored for internal use. This
	 * means that multiple `execute()` operations on a single `Template` instance
	 * are more efficient than one `execute()` operations on multiple `Template`
	 * instances.
	 * If `str` is `null`, the result is unspecified.
	 * 
	 * @param string $str
	 * 
	 * @return void
	 */
	public function __construct ($str) {
		$tokens = $this->parseTokens($str);
		$this->expr = $this->parseBlock($tokens);
		if (!$tokens->isEmpty()) {
			throw Exception::thrown("Unexpected '" . (\Std::string($tokens->first()->s)??'null') . "'");
		}
	}

	/**
	 * Executes `this` `Template`, taking into account `context` for
	 * replacements and `macros` for callback functions.
	 * If `context` has a field `name`, its value replaces all occurrences of
	 * `::name::` in the `Template`. Otherwise `Template.globals` is checked instead,
	 * If `name` is not a field of that either, `::name::` is replaced with `null`.
	 * If `macros` has a field `name`, all occurrences of `$$name(args)` are
	 * replaced with the result of calling that field. The first argument is
	 * always the `resolve()` method, followed by the given arguments.
	 * If `macros` has no such field, the result is unspecified.
	 * If `context` is `null`, the result is unspecified. If `macros` is `null`,
	 * no macros are used.
	 * 
	 * @param mixed $context
	 * @param mixed $macros
	 * 
	 * @return string
	 */
	public function execute ($context, $macros = null) {
		$this->macros = ($macros === null ? new HxAnon() : $macros);
		$this->context = $context;
		$this->stack = new List_hx();
		$this->buf = new \StringBuf();
		$this->run($this->expr);
		return $this->buf->b;
	}

	/**
	 * @param string $v
	 * 
	 * @return \Closure
	 */
	public function makeConst ($v) {
		Template::$expr_trim->match($v);
		$v = Template::$expr_trim->matched(1);
		if (HxString::charCodeAt($v, 0) === 34) {
			$str = \mb_substr($v, 1, mb_strlen($v) - 2);
			return function () use (&$str) {
				return $str;
			};
		}
		if (Template::$expr_int->match($v)) {
			$i = \Std::parseInt($v);
			return function () use (&$i) {
				return $i;
			};
		}
		if (Template::$expr_float->match($v)) {
			$f = \Std::parseFloat($v);
			return function () use (&$f) {
				return $f;
			};
		}
		$me = $this;
		return function () use (&$v, &$me) {
			return $me->resolve($v);
		};
	}

	/**
	 * @param List_hx $l
	 * 
	 * @return \Closure
	 */
	public function makeExpr ($l) {
		return $this->makePath($this->makeExpr2($l), $l);
	}

	/**
	 * @param List_hx $l
	 * 
	 * @return \Closure
	 */
	public function makeExpr2 ($l) {
		$this->skipSpaces($l);
		$p = $l->pop();
		$this->skipSpaces($l);
		if ($p === null) {
			throw Exception::thrown("<eof>");
		}
		if ($p->s) {
			return $this->makeConst($p->p);
		}
		$__hx__switch = ($p->p);
		if ($__hx__switch === "!") {
			$e = $this->makeExpr($l);
			return function () use (&$e) {
				$v = $e();
				if ($v !== null) {
					return $v === false;
				} else {
					return true;
				}
			};
		} else if ($__hx__switch === "(") {
			$this->skipSpaces($l);
			$e1 = $this->makeExpr($l);
			$this->skipSpaces($l);
			$p1 = $l->pop();
			if (($p1 === null) || $p1->s) {
				throw Exception::thrown($p1);
			}
			if ($p1->p === ")") {
				return $e1;
			}
			$this->skipSpaces($l);
			$e2 = $this->makeExpr($l);
			$this->skipSpaces($l);
			$p2 = $l->pop();
			$this->skipSpaces($l);
			if (($p2 === null) || ($p2->p !== ")")) {
				throw Exception::thrown($p2);
			}
			$__hx__switch = ($p1->p);
			if ($__hx__switch === "!=") {
				return function () use (&$e1, &$e2) {
					return !Boot::equal($e1(), $e2());
				};
			} else if ($__hx__switch === "&&") {
				return function () use (&$e1, &$e2) {
					return $e1() && $e2();
				};
			} else if ($__hx__switch === "*") {
				return function () use (&$e1, &$e2) {
					return $e1() * $e2();
				};
			} else if ($__hx__switch === "+") {
				return function () use (&$e1, &$e2) {
					return Boot::addOrConcat($e1(), $e2());
				};
			} else if ($__hx__switch === "-") {
				return function () use (&$e1, &$e2) {
					return $e1() - $e2();
				};
			} else if ($__hx__switch === "/") {
				return function () use (&$e1, &$e2) {
					return $e1() / $e2();
				};
			} else if ($__hx__switch === "<") {
				return function () use (&$e1, &$e2) {
					return $e1() < $e2();
				};
			} else if ($__hx__switch === "<=") {
				return function () use (&$e1, &$e2) {
					return $e1() <= $e2();
				};
			} else if ($__hx__switch === "==") {
				return function () use (&$e1, &$e2) {
					return Boot::equal($e1(), $e2());
				};
			} else if ($__hx__switch === ">") {
				return function () use (&$e1, &$e2) {
					return $e1() > $e2();
				};
			} else if ($__hx__switch === ">=") {
				return function () use (&$e1, &$e2) {
					return $e1() >= $e2();
				};
			} else if ($__hx__switch === "||") {
				return function () use (&$e1, &$e2) {
					return $e1() || $e2();
				};
			} else {
				throw Exception::thrown("Unknown operation " . ($p1->p??'null'));
			}
		} else if ($__hx__switch === "-") {
			$e3 = $this->makeExpr($l);
			return function () use (&$e3) {
				return -$e3();
			};
		}
		throw Exception::thrown($p->p);
	}

	/**
	 * @param \Closure $e
	 * @param List_hx $l
	 * 
	 * @return \Closure
	 */
	public function makePath ($e, $l) {
		$p = $l->first();
		if (($p === null) || ($p->p !== ".")) {
			return $e;
		}
		$l->pop();
		$field = $l->pop();
		if (($field === null) || !$field->s) {
			throw Exception::thrown($field->p);
		}
		$f = $field->p;
		Template::$expr_trim->match($f);
		$f = Template::$expr_trim->matched(1);
		return $this->makePath(function () use (&$f, &$e) {
			return \Reflect::field($e(), $f);
		}, $l);
	}

	/**
	 * @param List_hx $tokens
	 * 
	 * @return TemplateExpr
	 */
	public function parse ($tokens) {
		$t = $tokens->pop();
		$p = $t->p;
		if ($t->s) {
			return TemplateExpr::OpStr($p);
		}
		if ($t->l !== null) {
			$pe = new List_hx();
			$_g = 0;
			$_g1 = $t->l;
			while ($_g < $_g1->length) {
				$pe->add($this->parseBlock($this->parseTokens(($_g1->arr[$_g++] ?? null))));
			}
			return TemplateExpr::OpMacro($p, $pe);
		}
		$kwdEnd = function ($kwd) use (&$p) {
			$pos = -1;
			$length = mb_strlen($kwd);
			if (\mb_substr($p, 0, $length) === $kwd) {
				$pos = $length;
				$s = \mb_substr($p, $length, null);
				$_g_offset = 0;
				while ($_g_offset < mb_strlen($s)) {
					if (\StringTools::fastCodeAt($s, $_g_offset++) === 32) {
						++$pos;
					} else {
						break;
					}
				}
			}
			return $pos;
		};
		$pos = $kwdEnd("if");
		if ($pos > 0) {
			$p = \mb_substr($p, $pos, mb_strlen($p) - $pos);
			$e = $this->parseExpr($p);
			$eif = $this->parseBlock($tokens);
			$t = $tokens->first();
			$eelse = null;
			if ($t === null) {
				throw Exception::thrown("Unclosed 'if'");
			}
			if ($t->p === "end") {
				$tokens->pop();
				$eelse = null;
			} else if ($t->p === "else") {
				$tokens->pop();
				$eelse = $this->parseBlock($tokens);
				$t = $tokens->pop();
				if (($t === null) || ($t->p !== "end")) {
					throw Exception::thrown("Unclosed 'else'");
				}
			} else {
				$t->p = \mb_substr($t->p, 4, mb_strlen($t->p) - 4);
				$eelse = $this->parse($tokens);
			}
			return TemplateExpr::OpIf($e, $eif, $eelse);
		}
		$pos = $kwdEnd("foreach");
		if ($pos >= 0) {
			$p = \mb_substr($p, $pos, mb_strlen($p) - $pos);
			$e = $this->parseExpr($p);
			$efor = $this->parseBlock($tokens);
			$t = $tokens->pop();
			if (($t === null) || ($t->p !== "end")) {
				throw Exception::thrown("Unclosed 'foreach'");
			}
			return TemplateExpr::OpForeach($e, $efor);
		}
		if (Template::$expr_splitter->match($p)) {
			return TemplateExpr::OpExpr($this->parseExpr($p));
		}
		return TemplateExpr::OpVar($p);
	}

	/**
	 * @param List_hx $tokens
	 * 
	 * @return TemplateExpr
	 */
	public function parseBlock ($tokens) {
		$l = new List_hx();
		while (true) {
			$t = $tokens->first();
			if ($t === null) {
				break;
			}
			if (!$t->s && (($t->p === "end") || ($t->p === "else") || (\mb_substr($t->p, 0, 7) === "elseif "))) {
				break;
			}
			$l->add($this->parse($tokens));
		}
		if ($l->length === 1) {
			return $l->first();
		}
		return TemplateExpr::OpBlock($l);
	}

	/**
	 * @param string $data
	 * 
	 * @return \Closure
	 */
	public function parseExpr ($data) {
		$l = new List_hx();
		$expr = $data;
		while (Template::$expr_splitter->match($data)) {
			$p = Template::$expr_splitter->matchedPos();
			if ($p->pos !== 0) {
				$l->add(new HxAnon([
					"p" => \mb_substr($data, 0, $p->pos),
					"s" => true,
				]));
			}
			$p1 = Template::$expr_splitter->matched(0);
			$l->add(new HxAnon([
				"p" => $p1,
				"s" => HxString::indexOf($p1, "\"") >= 0,
			]));
			$data = Template::$expr_splitter->matchedRight();
		}
		if (mb_strlen($data) !== 0) {
			$_g_offset = 0;
			$_g_s = $data;
			while ($_g_offset < mb_strlen($_g_s)) {
				$_g1_key = $_g_offset;
				if (\StringTools::fastCodeAt($_g_s, $_g_offset++) !== 32) {
					$l->add(new HxAnon([
						"p" => \mb_substr($data, $_g1_key, null),
						"s" => true,
					]));
					break;
				}
			}
		}
		$e = null;
		try {
			$e = $this->makeExpr($l);
			if (!$l->isEmpty()) {
				throw Exception::thrown($l->first()->p);
			}
		} catch(\Throwable $_g) {
			NativeStackTrace::saveStack($_g);
			$_g1 = Exception::caught($_g)->unwrap();
			if (is_string($_g1)) {
				throw Exception::thrown("Unexpected '" . ($_g1??'null') . "' in " . ($expr??'null'));
			} else {
				throw $_g;
			}
		}
		return function () use (&$e, &$expr) {
			try {
				return $e();
			} catch(\Throwable $_g) {
				NativeStackTrace::saveStack($_g);
				throw Exception::thrown("Error : " . (\Std::string(Exception::caught($_g)->unwrap())??'null') . " in " . ($expr??'null'));
			}
		};
	}

	/**
	 * @param string $data
	 * 
	 * @return List_hx
	 */
	public function parseTokens ($data) {
		$tokens = new List_hx();
		while (Template::$splitter->match($data)) {
			$p = Template::$splitter->matchedPos();
			if ($p->pos > 0) {
				$tokens->add(new HxAnon([
					"p" => \mb_substr($data, 0, $p->pos),
					"s" => true,
					"l" => null,
				]));
			}
			if (HxString::charCodeAt($data, $p->pos) === 58) {
				$tokens->add(new HxAnon([
					"p" => \mb_substr($data, $p->pos + 2, $p->len - 4),
					"s" => false,
					"l" => null,
				]));
				$data = Template::$splitter->matchedRight();
				continue;
			}
			$parp = $p->pos + $p->len;
			$npar = 1;
			$params = new \Array_hx();
			$part = "";
			while (true) {
				$c = HxString::charCodeAt($data, $parp);
				++$parp;
				if ($c === 40) {
					++$npar;
				} else if ($c === 41) {
					--$npar;
					if ($npar <= 0) {
						break;
					}
				} else if ($c === null) {
					throw Exception::thrown("Unclosed macro parenthesis");
				}
				if (($c === 44) && ($npar === 1)) {
					$params->arr[$params->length++] = $part;
					$part = "";
				} else {
					$part = ($part??'null') . (\mb_chr($c)??'null');
				}
			}
			$params->arr[$params->length++] = $part;
			$tokens->add(new HxAnon([
				"p" => Template::$splitter->matched(2),
				"s" => false,
				"l" => $params,
			]));
			$data = \mb_substr($data, $parp, mb_strlen($data) - $parp);
		}
		if (mb_strlen($data) > 0) {
			$tokens->add(new HxAnon([
				"p" => $data,
				"s" => true,
				"l" => null,
			]));
		}
		return $tokens;
	}

	/**
	 * @param string $v
	 * 
	 * @return mixed
	 */
	public function resolve ($v) {
		if ($v === "__current__") {
			return $this->context;
		}
		$value = \Reflect::getProperty($this->context, $v);
		if (($value !== null) || \Reflect::hasField($this->context, $v)) {
			return $value;
		}
		$_g_head = $this->stack->h;
		while ($_g_head !== null) {
			$val = $_g_head->item;
			$_g_head = $_g_head->next;
			$value = \Reflect::getProperty($val, $v);
			if (($value !== null) || \Reflect::hasField($val, $v)) {
				return $value;
			}
		}
		return \Reflect::field(Template::$globals, $v);
	}

	/**
	 * @param TemplateExpr $e
	 * 
	 * @return void
	 */
	public function run ($e) {
		$__hx__switch = ($e->index);
		if ($__hx__switch === 0) {
			$this->buf->add(\Std::string($this->resolve($e->params[0])));
		} else if ($__hx__switch === 1) {
			$this->buf->add(\Std::string($e->params[0]()));
		} else if ($__hx__switch === 2) {
			$_g = $e->params[2];
			$v = $e->params[0]();
			if (($v === null) || ($v === false)) {
				if ($_g !== null) {
					$this->run($_g);
				}
			} else {
				$this->run($e->params[1]);
			}
		} else if ($__hx__switch === 3) {
			$this->buf->add($e->params[0]);
		} else if ($__hx__switch === 4) {
			$_g_head = $e->params[0]->h;
			while ($_g_head !== null) {
				$val = $_g_head->item;
				$_g_head = $_g_head->next;
				$this->run($val);
			}
		} else if ($__hx__switch === 5) {
			$_g = $e->params[1];
			$v = $e->params[0]();
			try {
				$x = $v->iterator();
				if (Boot::dynamicField($x, 'hasNext') === null) {
					throw Exception::thrown(null);
				}
				$v = $x;
			} catch(\Throwable $_g1) {
				NativeStackTrace::saveStack($_g1);
				try {
					if (Boot::dynamicField($v, 'hasNext') === null) {
						throw Exception::thrown(null);
					}
				} catch(\Throwable $_g1) {
					throw Exception::thrown("Cannot iter on " . (\Std::string($v)??'null'));
				}
			}
			$this->stack->push($this->context);
			$v1 = $v;
			while ($v1->hasNext()) {
				$this->context = $v1->next();
				$this->run($_g);
			}
			$this->context = $this->stack->pop();
		} else if ($__hx__switch === 6) {
			$_g = $e->params[0];
			$v = \Reflect::field($this->macros, $_g);
			$pl = new \Array_hx();
			$old = $this->buf;
			$pl->arr[$pl->length++] = Boot::getInstanceClosure($this, 'resolve');
			$_g_head = $e->params[1]->h;
			while ($_g_head !== null) {
				$val = $_g_head->item;
				$_g_head = $_g_head->next;
				if ($val->index === 0) {
					$x = $this->resolve($val->params[0]);
					$pl->arr[$pl->length++] = $x;
				} else {
					$this->buf = new \StringBuf();
					$this->run($val);
					$pl->arr[$pl->length++] = $this->buf->b;
				}
			}
			$this->buf = $old;
			try {
				$this->buf->add(\Std::string(\Reflect::callMethod($this->macros, $v, $pl)));
			} catch(\Throwable $_g1) {
				NativeStackTrace::saveStack($_g1);
				$_g2 = Exception::caught($_g1)->unwrap();
				$plstr = null;
				try {
					$plstr = $pl->join(",");
				} catch(\Throwable $_g1) {
					$plstr = "???";
				}
				throw Exception::thrown("Macro call " . ($_g??'null') . "(" . ($plstr??'null') . ") failed (" . (\Std::string($_g2)??'null') . ")");
			}
		}
	}

	/**
	 * @param List_hx $l
	 * 
	 * @return void
	 */
	public function skipSpaces ($l) {
		$p = $l->first();
		while ($p !== null) {
			$s = $p->p;
			$_g_offset = 0;
			while ($_g_offset < mb_strlen($s)) {
				if (\StringTools::fastCodeAt($s, $_g_offset++) !== 32) {
					return;
				}
			}
			$l->pop();
			$p = $l->first();
		}
	}

	/**
	 * @internal
	 * @access private
	 */
	static public function __hx__init ()
	{
		static $called = false;
		if ($called) return;
		$called = true;


		self::$splitter = new \EReg("(::[A-Za-z0-9_ ()&|!+=/><*.\"-]+::|\\\$\\\$([A-Za-z0-9_-]+)\\()", "");
		self::$expr_splitter = new \EReg("(\\(|\\)|[ \x0D\x0A\x09]*\"[^\"]*\"[ \x0D\x0A\x09]*|[!+=/><*.&|-]+)", "");
		self::$expr_trim = new \EReg("^[ ]*([^ ]+)[ ]*\$", "");
		self::$expr_int = new \EReg("^[0-9]+\$", "");
		self::$expr_float = new \EReg("^([+-]?)(?=\\d|,\\d)\\d*(,\\d*)?([Ee]([+-]?\\d+))?\$", "");
		self::$globals = new HxAnon();
		self::$hxKeepArrayIterator = new ArrayIterator(new \Array_hx());
	}
}

Boot::registerClass(Template::class, 'haxe.Template');
Template::__hx__init();
