use crate::reader::Reader;
use crate::Result;
use crate::{error::NamelistError, namelist::NamelistGroup};

use crate::namelist::{Array, Item, LiteralConstant, Map};

pub struct Parser<'a> {
    reader: Reader<'a>,
}

impl<'a> Parser<'a> {
    pub fn new(input: &'a str) -> Parser<'a> {
        Parser {
            reader: Reader::new(input),
        }
    }

    fn skip_value_seperator(&mut self) {
        if let Some(b',') = self.reader.peek_next_byte() {
            self.reader.skip_byte();
        }
    }

    fn peek_identifer(&mut self) -> bool {
        self.reader
            .peek_next_byte()
            .map(|b| b.is_ascii_alphabetic() || b == b'_')
            .unwrap_or(false)
    }

    fn parse_identifier(&mut self) -> Result<&'a str> {
        let ident_begin = self.reader.offset();
        let Some(first_byte) = self.reader.peek_next_byte() else {
            return Err(self.unexpected_eof());
        };

        if !first_byte.is_ascii_alphabetic() {
            return Err(NamelistError::unexpected_byte(
                first_byte,
                "identifier",
                self.reader.position(),
            ));
        }

        self.reader.skip_byte();

        while self
            .reader
            .peek_next_byte()
            .map(|b| b.is_ascii_alphanumeric() || b == b'_')
            .unwrap_or(false)
        {
            self.reader.skip_byte();
        }
        let ident_end = self.reader.offset();
        Ok(self.reader.get_str(ident_begin, ident_end))
    }

    fn parse_bool(&mut self) -> Result<LiteralConstant> {
        let literal_begin = self.reader.offset();
        self.reader.skip_byte();
        while self
            .reader
            .peek_next_byte()
            .map(|b| b.is_ascii_alphabetic() || b == b'.')
            .unwrap_or(false)
        {
            self.reader.skip_byte();
        }
        let literal_end = self.reader.offset();

        let literal = self.reader.get_str(literal_begin, literal_end);

        if literal == ".TRUE." || literal == ".true." {
            Ok(LiteralConstant::Bool(true))
        } else if literal == ".FALSE." || literal == ".false." {
            Ok(LiteralConstant::Bool(false))
        } else {
            Err(NamelistError::unexpected_token(
                literal,
                ".TRUE. or .FALSE.",
                self.reader.position(),
            ))
        }
    }

    fn parse_index(&mut self) -> Result<usize> {
        let idx_begin = self.reader.offset();
        while self.reader.peek_next_byte().map(|b| b.is_ascii_digit()) == Some(true) {
            self.reader.skip_byte();
        }
        let slice = self.reader.get_str(idx_begin, self.reader.offset());
        slice
            .parse::<usize>()
            .map_err(|e| NamelistError::parse_index(e, self.reader.position()))
    }

    fn parse_numeric(&mut self) -> Result<LiteralConstant> {
        let literal_begin = self.reader.offset();
        let mut is_float = false;

        while let Some(c) = self.reader.peek_next_byte() {
            if c == b'.' {
                if is_float {
                    break;
                } else {
                    is_float = true;
                }
            } else if !c.is_ascii_digit() && c != b'-' {
                break;
            }
            self.reader.skip_byte();
        }

        let slice = self.reader.get_str(literal_begin, self.reader.offset());
        if is_float {
            Ok(LiteralConstant::Float(slice.parse::<f64>().map_err(
                |e| NamelistError::parse_float(e, self.reader.position()),
            )?))
        } else {
            Ok(LiteralConstant::Int(slice.parse::<i64>().map_err(|e| {
                NamelistError::parse_int(e, self.reader.position())
            })?))
        }
    }

    fn parse_string(&mut self) -> Result<LiteralConstant> {
        let quote = self.reader.next_byte().unwrap();
        let mut bytes = Vec::new();

        while let Some(next_byte) = self.reader.next_byte() {
            if next_byte == quote {
                if self.reader.peek_next_byte() != Some(quote) {
                    return Ok(LiteralConstant::String(String::from_utf8(bytes).map_err(
                        |_e| NamelistError::SyntaxError(self.reader.position()),
                    )?));
                } else {
                    self.reader.skip_byte();
                }
            }
            bytes.push(next_byte);
        }
        Err(self.unexpected_eof())
    }

    fn peek_constant(&mut self) -> bool {
        match self.reader.peek_next_byte() {
            Some(b'"') => true,
            Some(b'\'') => true,
            Some(b'.') => true,
            Some(b'-') => true,
            Some(b',') => true,
            Some(b) if b.is_ascii_digit() => true,
            _ => false,
        }
    }

    fn parse_literal_constant(&mut self) -> Result<LiteralConstant> {
        match self.reader.peek_next_byte() {
            Some(b'.') => self.parse_bool(),
            Some(b'-') => self.parse_numeric(),
            Some(b) if b.is_ascii_digit() => self.parse_numeric(),
            Some(b'"') | Some(b'\'') => self.parse_string(),
            Some(b',') => Ok(LiteralConstant::Null),
            Some(_) => Err(self.parse_unexpected_token("a literal constant or ','")),
            None => Err(self.unexpected_eof()),
        }
    }

    fn parse_compound_constant(&mut self) -> Result<(usize, LiteralConstant)> {
        self.reader.skip_whitespace();

        let first_literal = self.parse_literal_constant()?;

        self.reader.skip_whitespace();

        if let Some(b'*') = self.reader.peek_next_byte() {
            match first_literal {
                LiteralConstant::Int(i) if !i.is_negative() => {
                    self.reader.skip_byte();
                    self.reader.skip_whitespace();
                    let real_first_literal = self.parse_literal_constant()?;
                    Ok((i as usize, real_first_literal))
                }
                _ => Err(NamelistError::SyntaxError(self.reader.position())),
            }
        } else {
            Ok((1, first_literal))
        }
    }

    fn parse_rhs(&mut self) -> Result<Item> {
        self.reader.skip_whitespace();

        let (num_first_constants, first_constant) = self.parse_compound_constant()?;

        self.reader.skip_whitespace();
        self.skip_value_seperator();
        self.reader.skip_whitespace();

        if self.peek_constant() {
            let mut array = vec![(num_first_constants, first_constant)];
            while self.peek_constant() {
                let (r, c) = self.parse_compound_constant()?;
                self.skip_value_seperator();
                array.push((r, c));
                self.reader.skip_whitespace();
            }
            Ok(Array::List(array).into_item())
        } else if num_first_constants > 1 {
            Ok(Array::RepeatedConstant(num_first_constants, first_constant).into_item())
        } else {
            Ok(first_constant.into_item())
        }
    }

    fn parse_index_value_pairs(&mut self, idxvals: &mut Map<usize, Item>) -> Result<()> {
        self.reader.skip_whitespace();
        let idx = self.parse_index()?;

        self.reader.skip_whitespace();

        match self.reader.peek_next_byte() {
            Some(b')') => Ok(()),
            Some(_) => Err(self.parse_unexpected_token(")")),
            None => Err(self.unexpected_eof()),
        }?;
        self.reader.skip_byte();
        self.reader.skip_whitespace();

        match self.reader.peek_next_byte() {
            Some(b'%') => {
                let next_keyvals = match idxvals
                    .entry(idx)
                    .or_insert_with(|| Item::Derived(Map::new()))
                {
                    Item::Derived(m) => m,
                    _ => {
                        return Err(NamelistError::AlreadyAssignedIndexed {
                            idx,
                            position: self.reader.position(),
                        })
                    }
                };
                self.reader.skip_whitespace();
                self.parse_name_value_pairs(next_keyvals)
            }
            Some(b'=') => {
                self.reader.skip_byte();
                self.reader.skip_whitespace();
                let rhs = self.parse_rhs()?;

                if idxvals.contains_key(&idx) {
                    return Err(NamelistError::AlreadyAssignedIndexed {
                        idx,
                        position: self.reader.position(),
                    });
                }

                idxvals.insert(idx, rhs);

                Ok(())
            }
            Some(_) => Err(self.parse_unexpected_token("% or =")),
            None => Err(self.unexpected_eof()),
        }
    }

    fn parse_name_value_pairs(&mut self, keyvals: &mut Map<String, Item>) -> Result<()> {
        let key = self.parse_identifier()?;
        self.reader.skip_whitespace();

        match self.reader.peek_next_byte() {
            Some(b'%') => {
                self.reader.skip_byte();

                let next_keyvals = match keyvals
                    .entry(key.to_owned())
                    .or_insert_with(|| Item::Derived(Map::new()))
                {
                    Item::Derived(m) => m,
                    _ => {
                        return Err(NamelistError::AlreadyAssigned {
                            key: key.to_owned(),
                            position: self.reader.position(),
                        })
                    }
                };
                self.reader.skip_whitespace();
                self.parse_name_value_pairs(next_keyvals)
            }
            Some(b'(') => {
                self.reader.skip_byte();

                let next_idxvals = match keyvals
                    .entry(key.to_owned())
                    .or_insert_with(|| Array::new_indexed())
                {
                    Item::Array(Array::Indexed(idxmap)) => idxmap,
                    _ => {
                        return Err(NamelistError::AlreadyAssigned {
                            key: key.to_owned(),
                            position: self.reader.position(),
                        })
                    }
                };
                self.parse_index_value_pairs(next_idxvals)
            }
            Some(b'=') => {
                self.reader.skip_byte();
                self.reader.skip_whitespace();
                let rhs = self.parse_rhs()?;

                if keyvals.contains_key(key) {
                    return Err(NamelistError::AlreadyAssigned {
                        key: key.to_owned(),
                        position: self.reader.position(),
                    });
                }

                keyvals.insert(key.to_owned(), rhs);

                Ok(())
            }
            Some(_) => Err(self.parse_unexpected_token("% or =")),
            None => Err(self.unexpected_eof()),
        }
    }
    fn parse_begin(&mut self) -> Result<&'a str> {
        match self.reader.next_byte() {
            Some(b'&') => (),
            Some(_) => return Err(self.parse_unexpected_token("&")),
            None => return Err(self.unexpected_eof()),
        }

        self.parse_identifier()
    }

    fn parse_unexpected_token(&mut self, expected: &str) -> NamelistError {
        self.reader.skip_whitespace();
        let pos = self.reader.position();
        let start_offset = self.reader.offset();
        while self
            .reader
            .peek_next_byte()
            .map(|b| !b.is_ascii_whitespace())
            .unwrap_or(false)
        {
            self.reader.skip_byte();
        }
        let end_offset = self.reader.offset();

        let found_token = self.reader.get_str(start_offset, end_offset);

        NamelistError::unexpected_token(found_token, expected, pos)
    }

    fn unexpected_eof(&mut self) -> NamelistError {
        NamelistError::UnexpectedEOF(self.reader.position())
    }

    pub fn parse(&mut self) -> Result<NamelistGroup> {
        self.reader.skip_whitespace();
        let identifier = self.parse_begin()?;
        self.reader.skip_whitespace();

        let mut keyvals = Map::new();
        while self.peek_identifer() {
            self.parse_name_value_pairs(&mut keyvals)?;
            self.reader.skip_whitespace();
        }

        match self.reader.peek_next_byte() {
            Some(b'/') => self.reader.skip_byte(),
            Some(_) => return Err(self.parse_unexpected_token("/")),
            None => return Err(self.unexpected_eof()),
        }

        Ok(NamelistGroup::new(identifier, keyvals))
    }
}

impl<'a> Iterator for Parser<'a> {
    type Item = Result<NamelistGroup>;

    fn next(&mut self) -> Option<Self::Item> {
        self.reader.skip_whitespace();
        if self.reader.is_eof() {
            Some(self.parse())
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test() {
        println!(
            "{:?}",
            Parser::new(
                r#"&nml
 a%b(1)%a = 1
/
"#
            )
            .parse()
        );
        assert!(false);
    }
}
