use std::rc::Rc;

use rug::ops::{AddFrom, CompleteRound};

use crate::environment::Environment;

use super::{Error, Node, NodeEnum, Precedence, constant::ConstantValue};

#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Range {
    from: Rc<NodeEnum>,
    to: Rc<NodeEnum>,
}

impl Node for Range {
    fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
        let from_evaluated = self.from.evaluate(env)?;
        let to_evaluated = self.to.evaluate(env)?;

        Ok(Self::new(from_evaluated, to_evaluated))
    }

    fn as_string(&self, env: Option<&Environment>) -> String {
        format!("{}..{}", self.from.as_string(env), self.to.as_string(env))
    }

    fn precedence(&self) -> Precedence {
        Precedence::Primary
    }
}

impl Range {
    pub fn new(from: Rc<NodeEnum>, to: Rc<NodeEnum>) -> Rc<NodeEnum> {
        Rc::new(Self { from, to }.into())
    }

    pub fn min(&self) -> Result<&ConstantValue, Error> {
        match self.from.as_ref() {
            NodeEnum::Constant(c) => Ok(c.get_value()),
            _ => Err(Error::MismatchedType(format!(
                "Minimum (from) in range was a {}, but it needs to be a Constant",
                self.from.type_str()
            ))),
        }
    }

    pub fn max(&self) -> Result<&ConstantValue, Error> {
        match self.to.as_ref() {
            NodeEnum::Constant(c) => Ok(c.get_value()),
            _ => Err(Error::MismatchedType(format!(
                "Maximum (to) in range was a {}, but it needs to be a Constant",
                self.to.type_str()
            ))),
        }
    }
}

// impl Iterator for &Range {
//     type Item = ConstantValue;
//
//     fn next(&mut self) -> Option<Self::Item> {
//         if self.it.is_none() {
//             return None;
//         }
//
//         let max = match self.to.as_ref() {
//             NodeEnum::Constant(c) => c.get_value(),
//             _ => panic!("Max is not a fucking constant dipshit"),
//         };
//
//         let result = {
//             let added = self.it.clone().unwrap() + 1;
//             let comp = added < max.clone();
//             if comp { self.it.clone() } else { None }
//         };
//         self.it.as_mut().unwrap().add_from(1);
//         result
//     }
// }

pub struct RangeIter {
    it: ConstantValue,
    start: ConstantValue,
    end: ConstantValue,
}

impl<'a> Iterator for RangeIter {
    type Item = ConstantValue;

    fn next(&mut self) -> Option<Self::Item> {
        let result = {
            if self.it < self.end {
                Some(self.it.clone())
            } else {
                None
            }
        };
        self.it += 1;
        result
    }
}

impl IntoIterator for &Range {
    type Item = ConstantValue;
    type IntoIter = RangeIter;

    fn into_iter(self) -> Self::IntoIter {
        let it = match self.from.as_ref() {
            NodeEnum::Constant(c) => c.get_value().clone(),
            _ => panic!("This range does not have a defined start"),
        };
        let end = match self.to.as_ref() {
            NodeEnum::Constant(c) => c.get_value().clone(),
            _ => panic!("This range does not have a defined end"),
        };
        RangeIter {
            start: it.clone(),
            end,
            it,
        }
    }
}
