use regex;
use regex::Regex;
use interface;
use interface::TermPlaceholder as Placeholder;
use interface::Term;
use bad_idea::BrokenF32;
use std::result;
use std::error;
use std::fmt;

#[derive(Debug, PartialEq, Eq)]
pub struct Error {
    expected: String,
    line: usize,
    column: usize,
}

impl Error {
    fn new(expected: String, line: usize, column: usize) -> Self {
        Error {
            expected: expected,
            line: line,
            column: column,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "Expected {} at {}:{}",
               self.expected,
               self.line,
               self.column)
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        "encountered unexpected input during parsing"
    }
}

type AR<F, B, S> = <F as interface::ATermFactory<B, S>>::ATermRef;
type Result<'t, T> = result::Result<(T, &'t str), Error>;

pub trait ATermRead<'t, B, S>: interface::ATermFactory<B, S>
    where S: From<&'t str>
{
    fn read_ascii(&self, input: &'t str) -> Result<'t, AR<Self, B, S>>;
    fn read_taf(&self, input: &'t str) -> Result<'t, AR<Self, B, S>>;
    fn read_baf(&self, input: &'t str) -> Result<'t, AR<Self, B, S>>;
    fn read_saf(&self, input: &'t str) -> Result<'t, AR<Self, B, S>>;
}

impl<'t, B, F, S> ATermRead<'t, B, S> for F
    where B: Clone,
          F: interface::ATermFactory<B, S>,
          S: From<&'t str>
{
    #[allow(unknown_lints, zero_ptr)] // clippy: shut up about a macro from another crate
    fn read_ascii(&self, input: &'t str) -> Result<'t, AR<F, B, S>> {
        lazy_static! {
            static ref INT: Regex = Regex::new(r"^[+\-]?[0-9]+").unwrap();
            static ref REAL: Regex =
                Regex::new(r"^[+\-]?[0-9]+\.[0-9]*([eE][+\-]?[0-9]+)?").unwrap();
            static ref CONS: Regex =
                Regex::new(
                    concat!(r"^[A-Za-z][A-Za-z0-9_-]*|",
                            r#"^"([^\x00-\x08\x0A-\x1F\x7F"\\]|\\(n|t|"|\\|[0-9][0-9][0-9]))*""#))
                        .unwrap();
        }
        let input = input.trim_left();
        if input.starts_with('<') {
            read_ascii_placeholder(self, input)
        } else if input.starts_with('[') {
            read_list(self, input, '[', ']').map(|(v, i)| (self.list(v), i))
        } else if input.starts_with('(') {
            read_ascii_cons_args_annos(self, input, "")
        } else if let Some(real) = REAL.find(input) {
            read_ascii_annos(self,
                             &input[real.end()..],
                             Term::Real(BrokenF32(real.as_str()
                                 .parse()
                                 .expect("Error parsing Real"))))
        } else if let Some(int) = INT.find(input) {
            read_ascii_int(self, input, int)
        } else if let Some(cons) = CONS.find(input) {
            read_ascii_cons_args_annos(self, &input[cons.end()..], cons.as_str())
        } else {
            Err(Error::new("one of '<', '[', '(', CONSTRUCTOR, INT, REAL, LONG".to_string(),
                           0,
                           0))
        }
    }

    #[allow(unused_variables)]
    fn read_taf(&self, input: &'t str) -> Result<'t, AR<F, B, S>> {
        unimplemented!()
    }

    #[allow(unused_variables)]
    fn read_baf(&self, input: &'t str) -> Result<'t, AR<F, B, S>> {
        unimplemented!()
    }

    #[allow(unused_variables)]
    fn read_saf(&self, input: &'t str) -> Result<'t, AR<F, B, S>> {
        unimplemented!()
    }
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn read_ascii_placeholder<'a, 't, B, F, S>(f: &F, input: &'t str) -> Result<'t, AR<F, B, S>>
    where B: Clone,
          F: interface::ATermFactory<B, S>,
          S: From<&'t str>
{
    let input = input.trim_left();
    if input.starts_with("<int>") {
        read_ascii_annos(f, &input[5..], f.t_placeholder(Placeholder::Int))
    } else if input.starts_with("<str>") {
        read_ascii_annos(f, &input[5..], f.t_placeholder(Placeholder::String))
    } else if input.starts_with("<real>") {
        read_ascii_annos(f, &input[6..], f.t_placeholder(Placeholder::Real))
    } else if input.starts_with("<term>") {
        read_ascii_annos(f, &input[6..], f.t_placeholder(Placeholder::Term))
    } else if input.starts_with("<appl(") {
        read_list(f, &input[5..], '(', ')')
            .and_then(|(v, i)| if !i.starts_with('>') {
                     Err(Error::new("'>'".to_string(), 0, 0))
                 } else {
                     read_ascii_annos(f,
                             &i[1..],
                             f.t_placeholder(
                                 Placeholder::Application(
                                     v.into_iter().collect::<Vec<_>>().into_boxed_slice())))
                 })
    } else if input.starts_with("<list>") {
        read_ascii_annos(f, &input[6..], f.t_placeholder(Placeholder::List))
    } else if input.starts_with("<placeholder>") {
        read_ascii_annos(f, &input[13..], f.t_placeholder(Placeholder::Placeholder))
    } else if input.starts_with("<blob>") {
        read_ascii_annos(f, &input[6..], f.t_placeholder(Placeholder::Blob))
    } else if input.starts_with("<long>") {
        read_ascii_annos(f, &input[6..], f.t_placeholder(Placeholder::Long))
    } else {
        Err(Error::new("one of 'int', 'str', 'real', 'term', 'appl', 'list', 'placeholder', \
                             'blob', 'long'".to_string(), 0, 0))
    }
}

fn read_ascii_int<'a, 't, B, F, S>(f: &F,
                                input: &'t str,
                                int: regex::Match<'t>)
                                -> Result<'t, AR<F, B, S>>
    where B: Clone,
          F: interface::ATermFactory<B, S>,
          S: From<&'t str>
{
    let after_int = &input[int.end()..];
    if after_int.starts_with('l') || after_int.starts_with('L') {
        read_ascii_annos(f,
                         &input[(int.end() + 1)..],
                         f.t_long(int.as_str().parse().expect("Overflow on Long")))
    } else {
        read_ascii_annos(f,
                         &input[int.end()..],
                         f.t_int(int.as_str().parse().expect("Overflow on Int")))
    }
}

fn read_ascii_annos<'a, 't, B, F, S>(f: &F,
                                  input: &'t str,
                                  term: Term<<F as interface::ATermFactory<B, S>>::ATermRef, B, S>)
                                  -> Result<'t, AR<F, B, S>>
    where B: Clone,
          F: interface::ATermFactory<B, S>,
          S: From<&'t str>
{
    let input = input.trim_left();
    if input.starts_with('{') {
        read_list(f, input, '{', '}').map(|(v, i)| (f.with_annos(term, v), i))
    } else {
        Ok((f.no_annos(term), input))
    }
}

fn read_list<'a, 't, B, F, S>(f: &F,
                           input: &'t str,
                           start: char,
                           end: char)
                           -> Result<'t, Vec<AR<F, B, S>>>
    where B: Clone,
          F: interface::ATermFactory<B, S>,
          S: From<&'t str>
{
    let input = input.trim_left();
    if input.starts_with(start) {
        let mut input = input[1..].trim_left();
        let mut vec = Vec::new();
        if !input.starts_with(end) {
            if let Ok((term, rest_input)) = f.read_ascii(input) {
                vec.push(term);
                input = rest_input.trim_left();
            } else {
                return Err(Error::new(format!("term or '{}'", end), 0, 0));
            }
            while !input.starts_with(end) {
                if !(input.starts_with(',')) {
                    return Err(Error::new("','".to_string(), 0, 0));
                }
                if let Ok((term, rest_input)) = f.read_ascii(&input[1..]) {
                    vec.push(term);
                    input = rest_input.trim_left();
                } else {
                    return Err(Error::new(format!("term or '{}'", end), 0, 0));
                }
            }
        }
        Ok((vec, &input[1..]))
    } else {
        Err(Error::new(format!("'{}'", start), 0, 0))
    }
}

fn read_ascii_cons_args_annos<'a, 't, 'c : 't, B, F, S>(f: &F,
                                            input: &'t str,
                                            constructor: &'c str)
                                            -> Result<'t, AR<F, B, S>>
    where B: Clone,
          F: interface::ATermFactory<B, S>,
          S: From<&'t str>
{
    use std::iter;
    let input = input.trim_left();
    if input.starts_with('(') {
        read_list(f, input, '(', ')')
            .and_then(|(v, i)| read_ascii_annos(f, i, f.t_application(constructor.into(), v)))
    } else {
        read_ascii_annos(f,
                         input,
                         f.t_application(constructor.into(), iter::empty()))
    }
}

#[cfg(test)]
mod tests {
    use rc::{ATermFactory, ATerm};
    use interface::{Term, TermPlaceholder as Placeholder};
    use parse::ATermRead;
    use std::rc::Rc;

    #[test]
    fn empty_string() {
        let factory: ATermFactory<()> = ATermFactory::new();
        assert!(factory.read_ascii("").is_err())
    }

    #[test]
    fn a() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let a = Rc::new(ATerm::no_annos(Term::Application("a".to_string(), Box::new([]))));
        assert_eq!(factory.read_ascii("a"), Ok((a, "")))
    }

    #[test]
    fn a_parens() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let a = Rc::new(ATerm::no_annos(Term::Application("a".to_string(), Box::new([]))));
        assert_eq!(factory.read_ascii("a()"), Ok((a, "")))
    }

    #[test]
    fn a_braces() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let a = Rc::new(ATerm::no_annos(Term::Application("a".to_string(), Box::new([]))));
        assert_eq!(factory.read_ascii("a{}"), Ok((a, "")))
    }

    #[test]
    fn a_parens_braces() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let a = Rc::new(ATerm::no_annos(Term::Application("a".to_string(), Box::new([]))));
        assert_eq!(factory.read_ascii("a(){}"), Ok((a, "")))
    }

    #[test]
    fn empty_list() {
        let factory: ATermFactory<()> = ATermFactory::new();
        assert_eq!(factory.read_ascii("[]"),
                   Ok((Rc::new(ATerm::no_annos(Term::List(Box::new([])))), "")))
    }

    #[test]
    fn single_element_list() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let a = Rc::new(ATerm::no_annos(Term::Application("a".to_string(), Box::new([]))));
        assert_eq!(factory.read_ascii("[a]"),
                   Ok((Rc::new(ATerm::no_annos(Term::List(Box::new([a])))), "")))
    }

    #[test]
    fn two_element_list() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let a = Rc::new(ATerm::no_annos(Term::Application("a".to_string(), Box::new([]))));
        assert_eq!(factory.read_ascii("[a,a]"),
                   Ok((Rc::new(ATerm::no_annos(Term::List(Box::new([a.clone(), a])))), "")))
    }

    #[test]
    fn a_ws() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let a = Rc::new(ATerm::no_annos(Term::Application("a".to_string(), Box::new([]))));
        assert_eq!(factory.read_ascii(" a"), Ok((a, "")))
    }

    #[test]
    fn a_ws_parens() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let a = Rc::new(ATerm::no_annos(Term::Application("a".to_string(), Box::new([]))));
        assert_eq!(factory.read_ascii(" a ( )"), Ok((a, "")))
    }

    #[test]
    fn a_ws_braces() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let a = Rc::new(ATerm::no_annos(Term::Application("a".to_string(), Box::new([]))));
        assert_eq!(factory.read_ascii(" a { }"), Ok((a, "")))
    }

    #[test]
    fn a_ws_parens_braces() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let a = Rc::new(ATerm::no_annos(Term::Application("a".to_string(), Box::new([]))));
        assert_eq!(factory.read_ascii(" a ( ) { }"), Ok((a, "")))
    }

    #[test]
    fn empty_list_ws() {
        let factory: ATermFactory<()> = ATermFactory::new();
        assert_eq!(factory.read_ascii("[  ]"),
                   Ok((Rc::new(ATerm::no_annos(Term::List(Box::new([])))), "")))
    }

    #[test]
    fn single_element_list_ws() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let a = Rc::new(ATerm::no_annos(Term::Application("a".to_string(), Box::new([]))));
        assert_eq!(factory.read_ascii("[ a ]"),
                   Ok((Rc::new(ATerm::no_annos(Term::List(Box::new([a])))), "")))
    }

    #[test]
    fn two_element_list_ws() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let a = Rc::new(ATerm::no_annos(Term::Application("a".to_string(), Box::new([]))));
        assert_eq!(factory.read_ascii("[ a , a ]"),
                   Ok((Rc::new(ATerm::no_annos(Term::List(Box::new([a.clone(), a])))), "")))
    }

    #[test]
    fn int() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let i = Rc::new(ATerm::no_annos(Term::Int(1234567)));
        assert_eq!(factory.read_ascii("1234567"), Ok((i, "")))
    }

    #[test]
    fn long() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let l = Rc::new(ATerm::no_annos(Term::Long(1234567)));
        assert_eq!(factory.read_ascii("1234567l"), Ok((l, "")))
    }

    #[test]
    fn string() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let s = Rc::new(ATerm::no_annos(Term::Application(r#""yadda yadda \" ab12""#.to_string(),
                                                          Box::new([]))));
        assert_eq!(factory.read_ascii(r#""yadda yadda \" ab12""#), Ok((s, "")))
    }

    #[test]
    fn placeholder() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let ip = Rc::new(ATerm::no_annos(Term::Placeholder(Placeholder::Int)));
        assert_eq!(factory.read_ascii("<int>"), Ok((ip, "")))
    }

    #[test]
    fn exam_example() {
        let factory: ATermFactory<()> = ATermFactory::new();
        let ip = Rc::new(ATerm::no_annos(Term::Placeholder(Placeholder::Int)));
        let sp = Rc::new(ATerm::no_annos(Term::Placeholder(Placeholder::String)));
        let tp = Rc::new(ATerm::no_annos(Term::Placeholder(Placeholder::Term)));
        let nine = Rc::new(ATerm::no_annos(Term::Int(9)));
        let ap =
            Rc::new(ATerm::no_annos(Term::Placeholder(Placeholder::Application(Box::new([tp,
                                                                                         nine])))));
        let e = Rc::new(ATerm::no_annos(Term::Application("exam".to_string(),
                                                          Box::new([ap, ip, sp]))));
        assert_eq!(factory.read_ascii("exam(<appl(<term>,9)>,<int>,<str>)"),
                   Ok((e, "")))
    }
}
