mod builtin_impls;
pub mod parse;
pub mod prelude;
mod prelude_internal;

use crate::prelude_internal::*;

pub trait Value {
	fn render(&self, w: &mut dyn Write) -> FmtResult {
		Ok(())
	}

	fn lookup(&self, name: &str) -> Option<&dyn Value> {
		None
	}
}

pub struct Template {
	commands: Vec<Command>,
}
impl Template {
	pub fn render(&self, env: &dyn Value, target: &mut dyn Write) {
		for command in self.commands.iter() {
			match command {
				Command::Print(s) => {
					let _ = write!(target, "{s}");
				},
				Command::LookupPrint(s) => {
					if let Some(value) = env.lookup(s) {
						value.render(target);
					} else {
						let _ = write!(target, "<<<'{s}' not found>>>");
					}
				},
			}
		}
	}
}

pub enum Command {
	Print(String),
	LookupPrint(String),
}

pub fn parse(input: &str) -> Template {
	let mut state = parse::ParseState::new(input);
	let mut commands = vec![];

	state.parse_root(&mut commands);

	Template { commands }
}

pub fn render(input: &str, env: &dyn Value) -> String {
	let template = parse(input);
	let mut output = String::new();

	template.render(env, &mut output);

	output
}

#[macro_export]
macro_rules! env {
	($($name:literal : $value:expr),* $(,)?) => {
		&[$(($name, &$value as &dyn crate::Value),)*]
	};
}

#[cfg(test)]
mod test {
	macro_rules! go {
		(($input:expr, $env:expr), $output:expr) => {
			assert_eq!(crate::render($input, $env), $output)
		};
	}

	#[test]
	fn simple() {
		go!(("Hello, World!", &()), "Hello, World!");
	}

	#[test]
	fn lookup() {
		go!(
			("Hello, {name}!", env! { "name": "Steve" }),
			"Hello, Steve!"
		);
	}
}
