use mlua::{FromLua, Lua, MetaMethod, UserData, UserDataMethods};

use crate::lua::LuaEnv;

// Position and size structures
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Point {
    pub x: f64,
    pub y: f64,
}

impl Point {
    pub fn new(x: f64, y: f64) -> Self {
        Point { x, y }
    }
}

impl FromLua for Point {
    fn from_lua(value: mlua::Value, _: &Lua) -> mlua::Result<Self> {
        match value {
            mlua::Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
            _ => unreachable!(),
        }
    }
}

impl UserData for Point {
    fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
        methods.add_method_mut("set", |_, point, (x, y): (f64, f64)| {
            point.x = x;
            point.y = y;
            Ok(())
        });

        // methods.add_meta_function(MetaMethod::Call, |_, (x, y)| Ok(Point::new(x, y)));
        // methods.add_meta_function(MetaMethod::ToString, |_, (x, y): (f64, f64)| {
        //     Ok(format!("({}, {})", x, y))
        // });
    }
    fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
        fields.add_field_method_set("x", |_, this, x| {
            this.x = x;
            Ok(())
        });
        fields.add_field_method_set("y", |_, point, y: f64| {
            point.y = y;
            Ok(())
        });

        fields.add_field_method_get("x", |_, point| Ok(point.x));
        fields.add_field_method_get("y", |_, point| Ok(point.y));
    }
}

pub fn add_constructor(lua: &LuaEnv) -> crate::Result<()> {
    lua.func("Point", |_, (x, y)| Ok(Point::new(x, y)))?;

    Ok(())
}
