/// The types used by the generated parser to convert C4 into something usable
/// by the accessor.

/// Key-Value
#[derive(Clone)]
pub struct Property {
    id: String,
    value: String
}

impl Property {
    pub fn new(_id: String, _value: String) -> Self {
        Property { id: _id, value: _value }
    }
}

/// An invocation of a selector, with the id of the selector and the value it 
/// must have to match.
#[derive(Clone)]
pub struct Selection {
    id: String,
    value: String
}

impl Selection {
    pub fn new(_id: String, _value: String) -> Self {
        Selection { id: _id, value: _value }
    }
}

/// The set of selections for a specific rule. These are the selections that are
/// AND'd together in evaluation.
#[derive(Clone)]
pub struct SelectionSet {
    selections: Vec<Selection>
}

impl SelectionSet {
    pub fn new(_selections: Vec<Selection>) -> Self {
        SelectionSet { selections: _selections }
    }

    pub fn add_selection(&mut self, s: Selection) {
        self.selections.push(s)
    }

    pub fn with_selection(&self, s: Selection) -> Self {
        let mut ss = self.selections.clone();
        ss.push(s);

        SelectionSet { selections: ss, .. *self }
    }
}

/// An expression is either a vector of selection sets surrounding some expressions
/// or a property
pub enum Expression {
    Select {
       selections: Vec<SelectionSet>,
       expressions: Vec<Expression>
    },
    Property(Property)
}

pub struct Configuration {
    pub expressions: Vec<Expression>
}

impl Configuration {
    pub fn new(_expressions: Vec<Expression>) -> Self {
        Configuration { expressions: _expressions }
    }
}

#[cfg(test)]
mod tests {
    use parser::generated::*;
    use parser::output::*;

    fn dump_error_and_fail(e: ParseError) {
        println!("At line: {}, column: {}, offset: {}", e.line, e.column, e.offset);
        for exp in &e.expected {
            println!("expected to match {}", exp);
        }
        assert!(false, "failed to parse!");

    fn check_cfg<F>(c: &str, f: F) where F: Fn(Configuration) {
        match configuration(c) {
            Ok(c) => f(c),
            Err(e) => dump_error_and_fail(e)
        }
    }
            

    #[test]
    fn test_simple_config() {
        check_cfg(
            r##"
                $country="us" $region="west" { 
                    taxRate: "8.0"; 
                }
            "##,
            |c: Configuration| {
                assert_eq!(1, c.expressions.len());
            }
        );
    }

    #[test]
    fn test_multi_selection() {
        check_cfg(
            r##"
                $country="us" $region="west",
                $country="ca" $region="ca" { 
                    taxRate: "8.0"; 
                }
            "##,
            |c: Configuration| {
                assert_eq!(1, c.expressions.len());
            }
        );
    }
}
    }