use crate::Number;
use crate::builtin::functions::core::reduce_with;
use crate::eval::EvalInto;
use crate::{Error, TulispContext, TulispObject};

pub(crate) fn add(ctx: &mut TulispContext) {
    fn add(ctx: &mut TulispContext, args: &TulispObject) -> Result<TulispObject, Error> {
        reduce_with(ctx, args, |a, b| Ok(a + b))
    }
    ctx.defspecial("+", add);

    fn sub(ctx: &mut TulispContext, args: &TulispObject) -> Result<TulispObject, Error> {
        if let Some(cons) = args.as_list_cons() {
            if cons.cdr().null() {
                let value: Number = cons.car().eval_into(ctx)?;
                let vv = Number::from(0) - value;
                Ok(vv.into())
            } else {
                reduce_with(ctx, args, |a, b| Ok(a - b))
            }
        } else {
            Err(Error::missing_argument(
                "Call to `sub` without any arguments".to_string(),
            ))
        }
    }
    ctx.defspecial("-", sub);

    fn mul(ctx: &mut TulispContext, args: &TulispObject) -> Result<TulispObject, Error> {
        reduce_with(ctx, args, |a, b| Ok(a * b))
    }
    ctx.defspecial("*", mul);

    fn div(ctx: &mut TulispContext, rest: &TulispObject) -> Result<TulispObject, Error> {
        reduce_with(ctx, rest, |a, b| {
            if b == 0 {
                Err(Error::out_of_range("Division by zero".to_string()))
            } else {
                Ok(a / b)
            }
        })
    }
    ctx.defspecial("/", div);

    ctx.defun("1+", |a: Number| a + 1);
    ctx.defun("1-", |a: Number| a - 1);
    ctx.defun("mod", |a: Number, b: Number| a % b);
}
