extern crate mlua;

use mlua::Lua as LuaContext;
use mlua::chunk;

pub trait ScriptLang {
    fn init(&mut self);
    fn update(&mut self, delta: f32);
}

pub struct Lua {
    ctx: LuaContext,
}

impl Lua {
    pub fn new() -> Self {
	// Create a lua context, load std libraries
	let ctx = LuaContext::new();
	// Create the ``couch'' api
	let couch = ctx.create_table().unwrap();
	couch.set("debug", ctx.create_function(debug).unwrap()).unwrap();
	couch.set("say_hello", ctx.create_function(say_hello).unwrap()).unwrap();
	// Hook in to globals
	ctx.globals().set("couch", couch).unwrap();
	let path = std::path::Path::new("main.lua");
	let buf = std::fs::read(&path).expect("Could not find main.lua");
	ctx.load(&buf).exec().unwrap();

	Lua {
	    ctx,
	}
    }
}

impl ScriptLang for Lua {
    fn init(&mut self) {
	self.ctx.load(&"init()").exec().ok();
    }

    fn update(&mut self, delta: f32) {
	self.ctx.load(chunk! {
	    update($delta)
	}).exec().ok();
    }
}

macro_rules! luafuncs {
    ($($name:ident($($arg:ident: $type: ty),*) $body:block),*) => {
	$(
	    fn $name(_: &LuaContext, ($($arg),*,): ($($type),*,)) -> Result<(), mlua::Error> $body
	)*
    };
}


luafuncs!{
    debug(msg: String) {
	println!("Couch Debug Message: {}", msg);
	Ok(())
    },
    say_hello(name: String, msg: String) {
	println!("Couch says {} to {}", msg, name);
	Ok(())
    }
}
