use crate::{
    Component, Context, Key, MatchCond, Pending, Rbe as Rbe1, RbeError, RbeTable, Ref, Value, Values, rbe::Rbe,
    rbe_error,
};

#[derive(Debug, Clone)]
pub struct EmptyIter<K, V, R, Ctx>
where
    K: Key,
    V: Value,
    R: Ref,
    Ctx: Context,
{
    is_first: bool,
    rbe: Rbe1<K, V, R, Ctx>,
    values: Values<K, V, Ctx>,
}

impl<K, V, R, Ctx> EmptyIter<K, V, R, Ctx>
where
    K: Key,
    V: Value,
    R: Ref,
    Ctx: Context,
{
    pub fn new(rbe: &Rbe<Component>, table: &RbeTable<K, V, R, Ctx>, values: &Values<K, V, Ctx>) -> Self {
        let rbe1 = cnv_rbe(rbe, table);
        Self {
            is_first: true,
            rbe: rbe1,
            values: values.clone(),
        }
    }
}

impl<K, V, R, Ctx> Iterator for EmptyIter<K, V, R, Ctx>
where
    K: Key,
    V: Value,
    R: Ref,
    Ctx: Context,
{
    type Item = Result<Pending<V, R>, rbe_error::RbeError<K, V, R, Ctx>>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.is_first {
            self.is_first = false;
            if self.rbe.nullable() {
                Some(Ok(Pending::new()))
            } else {
                Some(Err(RbeError::EmptyCandidates {
                    rbe: Box::new(self.rbe.clone()),
                    values: self.values.clone(),
                }))
            }
        } else {
            None
        }
    }
}

fn cnv_rbe<K, V, R, Ctx>(rbe: &Rbe<Component>, table: &RbeTable<K, V, R, Ctx>) -> Rbe1<K, V, R, Ctx>
where
    K: Key,
    V: Value,
    R: Ref,
    Ctx: Context,
{
    match rbe {
        Rbe::Empty => Rbe1::Empty,
        Rbe::And { values } => {
            let values1 = values.iter().map(|c| cnv_rbe(c, table)).collect();
            Rbe1::And { exprs: values1 }
        },
        Rbe::Or { values } => {
            let values1 = values.iter().map(|c| cnv_rbe(c, table)).collect();
            Rbe1::Or { exprs: values1 }
        },
        Rbe::Symbol { value, card } => {
            let key = cnv_key(value, table);
            let cond = cnv_cond(value, table);
            Rbe1::Symbol {
                key,
                cond,
                card: (*card).clone(),
            }
        },
        Rbe::Fail { error } => Rbe1::Fail {
            error: RbeError::MsgError {
                msg: format!("{error}"),
            },
        },
        Rbe::Star { value } => Rbe1::Star {
            expr: Box::new(cnv_rbe(value, table)),
        },
        Rbe::Plus { value } => Rbe1::Plus {
            expr: Box::new(cnv_rbe(value, table)),
        },
        Rbe::Repeat { value, card } => Rbe1::Repeat {
            expr: Box::new(cnv_rbe(value, table)),
            card: (*card).clone(),
        },
    }
}

fn cnv_cond<K, V, R, Ctx>(c: &Component, table: &RbeTable<K, V, R, Ctx>) -> MatchCond<K, V, R, Ctx>
where
    K: Key,
    V: Value,
    R: Ref,
    Ctx: Context,
{
    table.get_condition(c).unwrap().clone()
}

fn cnv_key<K, V, R, Ctx>(c: &Component, table: &RbeTable<K, V, R, Ctx>) -> K
where
    K: Key,
    V: Value,
    R: Ref,
    Ctx: Context,
{
    table.get_key(c).unwrap().clone()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{Cardinality, Max, Min, SingleCond};

    impl Key for u8 {}
    impl Value for u16 {}
    impl Ref for u32 {}

    type K = u8;
    type V = u16;
    type R = u32;
    type Ctx = char;

    fn make_table_with_symbol() -> (RbeTable<K, V, R, Ctx>, Component) {
        let mut table = RbeTable::new();
        let cond: MatchCond<K, V, R, Ctx> = MatchCond::single(
            SingleCond::new()
                .with_name("any")
                .with_cond(|_v, _c| Ok(Pending::new())),
        );
        let c = table.add_component(1, &cond);
        (table, c)
    }

    #[test]
    fn cnv_rbe_empty() {
        let table: RbeTable<K, V, R, Ctx> = RbeTable::new();
        let rbe = Rbe::Empty;
        let result = cnv_rbe(&rbe, &table);
        assert_eq!(result, Rbe1::Empty);
    }

    #[test]
    fn cnv_rbe_symbol() {
        let (table, c) = make_table_with_symbol();
        let rbe = Rbe::symbol(c, 1, Max::IntMax(3));
        let result = cnv_rbe(&rbe, &table);
        match result {
            Rbe1::Symbol { key, card, .. } => {
                assert_eq!(key, 1);
                assert_eq!(card, Cardinality::from(Min::from(1), Max::IntMax(3)));
            },
            other => panic!("Expected Symbol, got {:?}", other),
        }
    }

    #[test]
    fn cnv_rbe_and() {
        let (table, c) = make_table_with_symbol();
        let rbe = Rbe::and(vec![Rbe::Empty, Rbe::symbol(c, 1, Max::IntMax(1))]);
        let result = cnv_rbe(&rbe, &table);
        match result {
            Rbe1::And { exprs } => {
                assert_eq!(exprs.len(), 2);
                assert_eq!(exprs[0], Rbe1::Empty);
                assert!(matches!(exprs[1], Rbe1::Symbol { .. }));
            },
            other => panic!("Expected And, got {:?}", other),
        }
    }

    #[test]
    fn cnv_rbe_or() {
        let (table, c) = make_table_with_symbol();
        let rbe = Rbe::or(vec![Rbe::Empty, Rbe::symbol(c, 0, Max::IntMax(1))]);
        let result = cnv_rbe(&rbe, &table);
        match result {
            Rbe1::Or { exprs } => {
                assert_eq!(exprs.len(), 2);
                assert_eq!(exprs[0], Rbe1::Empty);
                assert!(matches!(exprs[1], Rbe1::Symbol { .. }));
            },
            other => panic!("Expected Or, got {:?}", other),
        }
    }

    #[test]
    fn cnv_rbe_star() {
        let (table, c) = make_table_with_symbol();
        let rbe = Rbe::star(Rbe::symbol(c, 1, Max::IntMax(1)));
        let result = cnv_rbe(&rbe, &table);
        match result {
            Rbe1::Star { expr } => {
                assert!(matches!(*expr, Rbe1::Symbol { .. }));
            },
            other => panic!("Expected Star, got {:?}", other),
        }
    }

    #[test]
    fn cnv_rbe_plus() {
        let (table, c) = make_table_with_symbol();
        let rbe = Rbe::plus(Rbe::symbol(c, 1, Max::IntMax(1)));
        let result = cnv_rbe(&rbe, &table);
        match result {
            Rbe1::Plus { expr } => {
                assert!(matches!(*expr, Rbe1::Symbol { .. }));
            },
            other => panic!("Expected Plus, got {:?}", other),
        }
    }

    #[test]
    fn cnv_rbe_repeat() {
        let (table, c) = make_table_with_symbol();
        let rbe = Rbe::repeat(Rbe::symbol(c, 1, Max::IntMax(1)), 2, Max::IntMax(5));
        let result = cnv_rbe(&rbe, &table);
        match result {
            Rbe1::Repeat { expr, card } => {
                assert!(matches!(*expr, Rbe1::Symbol { .. }));
                assert_eq!(card, Cardinality::from(Min::from(2), Max::IntMax(5)));
            },
            other => panic!("Expected Repeat, got {:?}", other),
        }
    }

    #[test]
    fn cnv_rbe_fail() {
        let table: RbeTable<K, V, R, Ctx> = RbeTable::new();
        let rbe = Rbe::Fail {
            error: crate::deriv_error::DerivError::MkOrValuesFail,
        };
        let result = cnv_rbe(&rbe, &table);
        assert!(matches!(result, Rbe1::Fail { .. }));
    }

    #[test]
    fn cnv_rbe_nested_and_or() {
        let (table, c) = make_table_with_symbol();
        let rbe = Rbe::and(vec![
            Rbe::or(vec![Rbe::Empty, Rbe::symbol(c, 1, Max::IntMax(1))]),
            Rbe::Empty,
        ]);
        let result = cnv_rbe(&rbe, &table);
        match result {
            Rbe1::And { exprs } => {
                assert_eq!(exprs.len(), 2);
                assert!(matches!(exprs[0], Rbe1::Or { .. }));
                assert_eq!(exprs[1], Rbe1::Empty);
            },
            other => panic!("Expected And, got {:?}", other),
        }
    }
}
