use crate::{
    builtins::{
        function::NativeFunctionData,
        value::{from_value, to_value, ResultValue, Value, ValueData},
    },
    exec::Interpreter,
};
use rand::random;
use std::f64;
pub fn abs(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .abs()
    }))
}
pub fn acos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .acos()
    }))
}
pub fn asin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .asin()
    }))
}
pub fn atan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .atan()
    }))
}
pub fn atan2(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .atan2(args.get(1).expect("Could not get argument").to_num())
    }))
}
pub fn cbrt(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .cbrt()
    }))
}
pub fn ceil(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .ceil()
    }))
}
pub fn cos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .cos()
    }))
}
pub fn exp(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .exp()
    }))
}
pub fn floor(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .floor()
    }))
}
pub fn log(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .log(f64::consts::E)
    }))
}
pub fn max(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    let mut max = f64::NEG_INFINITY;
    for arg in args {
        let num = arg.to_num();
        max = max.max(num);
    }
    Ok(to_value(max))
}
pub fn min(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    let mut max = f64::INFINITY;
    for arg in args {
        let num = arg.to_num();
        max = max.min(num);
    }
    Ok(to_value(max))
}
pub fn pow(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.len() >= 2 {
        let num: f64 = from_value(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64");
        let power: f64 = from_value(args.get(1).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64");
        num.powf(power)
    } else {
        f64::NAN
    }))
}
pub fn _random(_: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(random::<f64>()))
}
pub fn round(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .round()
    }))
}
pub fn sin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .sin()
    }))
}
pub fn sqrt(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .sqrt()
    }))
}
pub fn tan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
    Ok(to_value(if args.is_empty() {
        f64::NAN
    } else {
        from_value::<f64>(args.get(0).expect("Could not get argument").clone())
            .expect("Could not convert argument to f64")
            .tan()
    }))
}
pub fn create_constructor(global: &Value) -> Value {
    let math = ValueData::new_obj(Some(global));
    math.set_field_slice("E", to_value(f64::consts::E));
    math.set_field_slice("LN2", to_value(f64::consts::LN_2));
    math.set_field_slice("LN10", to_value(f64::consts::LN_10));
    math.set_field_slice("LOG2E", to_value(f64::consts::LOG2_E));
    math.set_field_slice("LOG10E", to_value(f64::consts::LOG10_E));
    math.set_field_slice("SQRT1_2", to_value(0.5_f64.sqrt()));
    math.set_field_slice("SQRT2", to_value(f64::consts::SQRT_2));
    math.set_field_slice("PI", to_value(f64::consts::PI));
    make_builtin_fn!(abs, named "abs", with length 1, of math);
    make_builtin_fn!(acos, named "acos", with length 1, of math);
    make_builtin_fn!(asin, named "asin", with length 1, of math);
    make_builtin_fn!(atan, named "atan", with length 1, of math);
    make_builtin_fn!(atan2, named "atan2", with length 2, of math);
    make_builtin_fn!(cbrt, named "cbrt", with length 1, of math);
    make_builtin_fn!(ceil, named "ceil", with length 1, of math);
    make_builtin_fn!(cos,  named "cos", with length 1, of math);
    make_builtin_fn!(exp, named "exp", with length 1, of math);
    make_builtin_fn!(floor, named "floor", with length 1, of math);
    make_builtin_fn!(log, named "log", with length 1, of math);
    make_builtin_fn!(max, named "max", with length 2, of math);
    make_builtin_fn!(min, named "min", with length 2, of math);
    make_builtin_fn!(pow, named "pow", with length 2, of math);
    make_builtin_fn!(_random, named "random", of math);
    make_builtin_fn!(round, named "round", with length 1, of math);
    make_builtin_fn!(sin, named "sin", with length 1, of math);
    make_builtin_fn!(sqrt, named "sqrt", with length 1, of math);
    make_builtin_fn!(tan, named "tan", with length 1, of math);
    math
}