use crate::{
    calc_result::CalcResult,
    expressions::{parser::Node, token::Error, types::CellReferenceIndex},
    model::Model,
};

// 2^48-1
const MAX: f64 = 281474976710655.0;

impl Model {
    // BITAND( number1, number2)
    pub(crate) fn fn_bitand(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
        if args.len() != 2 {
            return CalcResult::new_args_number_error(cell);
        }
        let number1 = match self.get_number(&args[0], cell) {
            Ok(f) => f,
            Err(s) => return s,
        };
        let number2 = match self.get_number(&args[1], cell) {
            Ok(f) => f,
            Err(s) => return s,
        };
        if number1.trunc() != number1 || number2.trunc() != number2 {
            return CalcResult::new_error(Error::NUM, cell, "numbers must be integers".to_string());
        }
        if number1 < 0.0 || number2 < 0.0 {
            return CalcResult::new_error(
                Error::NUM,
                cell,
                "numbers must be positive or zero".to_string(),
            );
        }

        if number1 > MAX || number2 > MAX {
            return CalcResult::new_error(
                Error::NUM,
                cell,
                "numbers must be less than 2^48-1".to_string(),
            );
        }

        let number1 = number1.trunc() as i64;
        let number2 = number2.trunc() as i64;
        let result = number1 & number2;
        CalcResult::Number(result as f64)
    }

    // BITOR(number1, number2)
    pub(crate) fn fn_bitor(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
        if args.len() != 2 {
            return CalcResult::new_args_number_error(cell);
        }
        let number1 = match self.get_number(&args[0], cell) {
            Ok(f) => f,
            Err(s) => return s,
        };
        let number2 = match self.get_number(&args[1], cell) {
            Ok(f) => f,
            Err(s) => return s,
        };
        if number1.trunc() != number1 || number2.trunc() != number2 {
            return CalcResult::new_error(Error::NUM, cell, "numbers must be integers".to_string());
        }
        if number1 < 0.0 || number2 < 0.0 {
            return CalcResult::new_error(
                Error::NUM,
                cell,
                "numbers must be positive or zero".to_string(),
            );
        }

        if number1 > MAX || number2 > MAX {
            return CalcResult::new_error(
                Error::NUM,
                cell,
                "numbers must be less than 2^48-1".to_string(),
            );
        }

        let number1 = number1.trunc() as i64;
        let number2 = number2.trunc() as i64;
        let result = number1 | number2;
        CalcResult::Number(result as f64)
    }

    // BITXOR(number1, number2)
    pub(crate) fn fn_bitxor(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
        if args.len() != 2 {
            return CalcResult::new_args_number_error(cell);
        }
        let number1 = match self.get_number(&args[0], cell) {
            Ok(f) => f,
            Err(s) => return s,
        };
        let number2 = match self.get_number(&args[1], cell) {
            Ok(f) => f,
            Err(s) => return s,
        };
        if number1.trunc() != number1 || number2.trunc() != number2 {
            return CalcResult::new_error(Error::NUM, cell, "numbers must be integers".to_string());
        }
        if number1 < 0.0 || number2 < 0.0 {
            return CalcResult::new_error(
                Error::NUM,
                cell,
                "numbers must be positive or zero".to_string(),
            );
        }

        if number1 > MAX || number2 > MAX {
            return CalcResult::new_error(
                Error::NUM,
                cell,
                "numbers must be less than 2^48-1".to_string(),
            );
        }

        let number1 = number1.trunc() as i64;
        let number2 = number2.trunc() as i64;
        let result = number1 ^ number2;
        CalcResult::Number(result as f64)
    }

    // BITLSHIFT(number, shift_amount)
    pub(crate) fn fn_bitlshift(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
        if args.len() != 2 {
            return CalcResult::new_args_number_error(cell);
        }
        let number = match self.get_number(&args[0], cell) {
            Ok(f) => f,
            Err(s) => return s,
        };
        let shift = match self.get_number(&args[1], cell) {
            Ok(f) => f,
            Err(s) => return s,
        };
        if number.trunc() != number {
            return CalcResult::new_error(Error::NUM, cell, "numbers must be integers".to_string());
        }
        if number < 0.0 {
            return CalcResult::new_error(
                Error::NUM,
                cell,
                "numbers must be positive or zero".to_string(),
            );
        }

        if number > MAX {
            return CalcResult::new_error(
                Error::NUM,
                cell,
                "numbers must be less than 2^48-1".to_string(),
            );
        }

        if shift.abs() > 53.0 {
            return CalcResult::new_error(
                Error::NUM,
                cell,
                "shift amount must be less than 53".to_string(),
            );
        }

        let number = number.trunc() as i64;
        let shift = shift.trunc() as i64;
        let result = if shift > 0 {
            number << shift
        } else {
            number >> -shift
        };
        let result = result as f64;
        if result.abs() > MAX {
            return CalcResult::new_error(Error::NUM, cell, "BITLSHIFT overflow".to_string());
        }
        CalcResult::Number(result)
    }

    // BITRSHIFT(number, shift_amount)
    pub(crate) fn fn_bitrshift(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
        if args.len() != 2 {
            return CalcResult::new_args_number_error(cell);
        }
        let number = match self.get_number(&args[0], cell) {
            Ok(f) => f,
            Err(s) => return s,
        };
        let shift = match self.get_number(&args[1], cell) {
            Ok(f) => f,
            Err(s) => return s,
        };
        if number.trunc() != number {
            return CalcResult::new_error(Error::NUM, cell, "numbers must be integers".to_string());
        }
        if number < 0.0 {
            return CalcResult::new_error(
                Error::NUM,
                cell,
                "numbers must be positive or zero".to_string(),
            );
        }

        if number > MAX {
            return CalcResult::new_error(
                Error::NUM,
                cell,
                "numbers must be less than 2^48-1".to_string(),
            );
        }

        if shift.abs() > 53.0 {
            return CalcResult::new_error(
                Error::NUM,
                cell,
                "shift amount must be less than 53".to_string(),
            );
        }

        let number = number.trunc() as i64;
        let shift = shift.trunc() as i64;
        let result = if shift > 0 {
            number >> shift
        } else {
            number << -shift
        };
        let result = result as f64;
        if result.abs() > MAX {
            return CalcResult::new_error(Error::NUM, cell, "BITRSHIFT overflow".to_string());
        }
        CalcResult::Number(result)
    }
}
