use std::rc::Rc;

use crate::{
    compile::syntax::{
        CollectErrors, Expression, FunctionBody,
        error::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
    },
    vm::value::{ConsCell, Keyword, Value},
};

#[derive(Debug, PartialEq)]
pub struct WhileExpression {
    pub condition: Rc<Expression>,
    pub body: FunctionBody,
}

#[derive(Debug, PartialEq)]
pub struct LoopExpression {
    pub body: FunctionBody,
}

impl WhileExpression {
    pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
        let Value::Cons(cons) = value else {
            return Err(ParseError {
                input: input.clone(),
                error: ParseErrorKind::Expected(
                    ExpectedWhat::ProperList,
                    ExpectedWhere::AfterKeyword(Keyword::While),
                ),
            });
        };
        let ConsCell(car, cdr) = cons.as_ref();
        let condition = Expression::parse_inner(car);
        let body = FunctionBody::parse(cdr, input, Keyword::While)?;
        Ok(Self { condition, body })
    }
}

impl LoopExpression {
    pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
        let body = FunctionBody::parse(value, input, Keyword::Loop)?;
        Ok(Self { body })
    }
}

impl CollectErrors<ParseError> for WhileExpression {
    fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
        let a = self.condition.collect_errors(errors);
        let b = self.body.collect_errors(errors);
        a | b
    }
}

impl CollectErrors<ParseError> for LoopExpression {
    fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
        self.body.collect_errors(errors)
    }
}
