use mlua::FromLua;

use super::number::Number;

#[derive(Clone,Copy)]
pub struct Vector2(glam::Vec2);

impl Vector2{
	pub const fn new(x:f32,y:f32)->Self{
		Self(glam::vec2(x,y))
	}
}

pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
	let table=lua.create_table()?;

	//Vector2.new
	table.raw_set("new",
		lua.create_function(|_,(x,y):(Option<Number>,Option<Number>)|
			match (x,y){
				(Some(x),Some(y))=>Ok(Vector2::new(x.into(),y.into())),
				(None,None)=>Ok(Vector2(glam::Vec2::ZERO)),
				_=>Err(mlua::Error::runtime("Unsupported arguments to Vector2.new")),
			}
		)?
	)?;

	globals.set("Vector2",table)?;

	Ok(())
}

impl From<Vector2> for rbx_types::Vector2{
	fn from(Vector2(v):Vector2)->rbx_types::Vector2{
		rbx_types::Vector2::new(v.x,v.y)
	}
}

impl From<rbx_types::Vector2> for Vector2{
	fn from(value:rbx_types::Vector2)->Vector2{
		Vector2::new(value.x,value.y)
	}
}

impl mlua::UserData for Vector2{
	fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
		fields.add_field_method_get("magnitude",|_,Vector2(this)|Ok(this.length()));
		fields.add_field_method_get("Magnitude",|_,Vector2(this)|Ok(this.length()));
		fields.add_field_method_get("unit",|_,Vector2(this)|Ok(Vector2(this.normalize())));
		fields.add_field_method_get("Unit",|_,Vector2(this)|Ok(Vector2(this.normalize())));
		fields.add_field_method_get("x",|_,Vector2(this)|Ok(this.x));
		fields.add_field_method_get("X",|_,Vector2(this)|Ok(this.x));
		fields.add_field_method_get("y",|_,Vector2(this)|Ok(this.y));
		fields.add_field_method_get("Y",|_,Vector2(this)|Ok(this.y));
	}

	fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
		//methods.add_method("area",|_,this,()| Ok(this.length * this.width));

		methods.add_meta_function(mlua::MetaMethod::Add,|_,(Vector2(this),Vector2(val)):(Self,Self)|Ok(Self(this+val)));
		methods.add_meta_function(mlua::MetaMethod::Sub,|_,(Vector2(this),Vector2(val)):(Self,Self)|Ok(Self(this-val)));
		methods.add_meta_function(mlua::MetaMethod::Mul,|lua,(lhs,rhs):(mlua::Value,mlua::Value)|{
			match (lhs,rhs){
				(mlua::Value::UserData(lhs),mlua::Value::UserData(rhs))=>lhs.borrow_scoped(|Vector2(lhs):&Vector2|rhs.borrow_scoped(|Vector2(rhs):&Vector2|Self(lhs*rhs)))?,
				(lhs,mlua::Value::UserData(rhs))=>{
					let lhs=Number::from_lua(lhs,lua)?;
					rhs.borrow_scoped(|Vector2(rhs):&Vector2|Self(lhs.to_f32()*rhs))
				},
				(mlua::Value::UserData(lhs),rhs)=>{
					let rhs=Number::from_lua(rhs,lua)?;
					lhs.borrow_scoped(|Vector2(lhs):&Vector2|Self(lhs*rhs.to_f32()))
				},
				_=>Err(mlua::Error::runtime(format!("Expected Vector2")))
			}
		});
		methods.add_meta_function(mlua::MetaMethod::Div,|_,(Vector2(this),val):(Self,mlua::Value)|{
			match val{
				mlua::Value::Integer(n)=>Ok(Self(this/(n as f32))),
				mlua::Value::Number(n)=>Ok(Self(this/(n as f32))),
				mlua::Value::UserData(ud)=>ud.borrow_scoped(|Vector2(rhs):&Vector2|Self(this/rhs)),
				other=>Err(mlua::Error::runtime(format!("Attempt to divide Vector2 by {other:?}"))),
			}
		});
		methods.add_meta_function(mlua::MetaMethod::ToString,|_,Vector2(this):Self|
			Ok(format!("Vector2.new({},{})",
				this.x,
				this.y,
			))
		);
	}
}

type_from_lua_userdata!(Vector2);
