use crate::{Parse, ParseResult, Parser};
use sway_ast::keywords::{DoubleColonToken, OpenAngleBracketToken, SelfToken, StorageToken};
use sway_ast::{
    AngleBrackets, PathExpr, PathExprSegment, PathType, PathTypeSegment, QualifiedPathRoot,
};
use sway_types::{Ident, Spanned};
impl Parse for PathExpr {
    fn parse(parser: &mut Parser) -> ParseResult<PathExpr> {
        let root_opt = match parser.take() {
            Some(open_angle_bracket_token) => {
                let qualified_path_root = parser.parse()?;
                let close_angle_bracket_token = parser.parse()?;
                let angle_brackets = AngleBrackets {
                    open_angle_bracket_token,
                    inner: qualified_path_root,
                    close_angle_bracket_token,
                };
                let double_colon_token = parser.parse()?;
                Some((Some(angle_brackets), double_colon_token))
            }
            None => parser
                .take()
                .map(|double_colon_token| (None, double_colon_token)),
        };
        let prefix = parser.parse()?;
        let mut suffix: Vec<(DoubleColonToken, PathExprSegment)> = Vec::new();
        let mut incomplete_suffix = false;
        while let Some(double_colon_token) = parser.take() {
            if let Ok(segment) = parser.parse() {
                suffix.push((double_colon_token, segment));
            } else {
                incomplete_suffix = true;
                let dummy_path_expr_segment = PathExprSegment {
                    name: Ident::new(double_colon_token.span()),
                    generics_opt: None,
                };
                suffix.push((double_colon_token, dummy_path_expr_segment));
                break;
            }
        }
        Ok(PathExpr {
            root_opt,
            prefix,
            suffix,
            incomplete_suffix,
        })
    }
}
fn parse_ident(parser: &mut Parser) -> ParseResult<Ident> {
    if let Some(token) = parser.take::<StorageToken>() {
        Ok(Ident::from(token))
    } else if let Some(token) = parser.take::<SelfToken>() {
        Ok(Ident::from(token))
    } else {
        parser.parse::<Ident>()
    }
}
impl Parse for PathExprSegment {
    fn parse(parser: &mut Parser) -> ParseResult<PathExprSegment> {
        Ok(PathExprSegment {
            name: parse_ident(parser)?,
            generics_opt: parser.guarded_parse::<(DoubleColonToken, OpenAngleBracketToken), _>()?,
        })
    }
}
impl Parse for PathType {
    fn parse(parser: &mut Parser) -> ParseResult<PathType> {
        let root_opt = match parser.take() {
            Some(open_angle_bracket_token) => {
                let qualified_path_root = parser.parse()?;
                let close_angle_bracket_token = parser.parse()?;
                let angle_brackets = AngleBrackets {
                    open_angle_bracket_token,
                    inner: qualified_path_root,
                    close_angle_bracket_token,
                };
                let double_colon_token = parser.parse()?;
                Some((Some(angle_brackets), double_colon_token))
            }
            None => parser
                .take()
                .map(|double_colon_token| (None, double_colon_token)),
        };
        let prefix = parser.parse()?;
        let mut suffix = Vec::new();
        while let Some(double_colon_token) = parser.take() {
            let segment = parser.parse()?;
            suffix.push((double_colon_token, segment));
        }
        Ok(PathType {
            root_opt,
            prefix,
            suffix,
        })
    }
}
impl Parse for PathTypeSegment {
    fn parse(parser: &mut Parser) -> ParseResult<PathTypeSegment> {
        let name = parse_ident(parser)?;
        let generics_opt =
            if let Some(generics) = parser.guarded_parse::<OpenAngleBracketToken, _>()? {
                Some((None, generics))
            } else if let Some((double_colon_token, generics)) =
                parser.guarded_parse::<(DoubleColonToken, OpenAngleBracketToken), _>()?
            {
                Some((Some(double_colon_token), generics))
            } else {
                None
            };
        Ok(PathTypeSegment { name, generics_opt })
    }
}
impl Parse for QualifiedPathRoot {
    fn parse(parser: &mut Parser) -> ParseResult<QualifiedPathRoot> {
        let ty = parser.parse()?;
        let as_trait = match parser.take() {
            Some(as_token) => Some((as_token, parser.parse()?)),
            None => None,
        };
        Ok(QualifiedPathRoot { ty, as_trait })
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_utils::parse;
    use insta::*;
    #[test]
    fn parse_nested_path() {
        assert_ron_snapshot!(parse::<PathExpr>(r#"
            std::vec::Vec
        "#,), @r###"
        PathExpr(
          root_opt: None,
          prefix: PathExprSegment(
            name: Ident(
              to_string: "std",
              span: (13, 16),
            ),
            generics_opt: None,
          ),
          suffix: [
            (DoubleColonToken(
              span: (16, 18),
            ), PathExprSegment(
              name: Ident(
                to_string: "vec",
                span: (18, 21),
              ),
              generics_opt: None,
            )),
            (DoubleColonToken(
              span: (21, 23),
            ), PathExprSegment(
              name: Ident(
                to_string: "Vec",
                span: (23, 26),
              ),
              generics_opt: None,
            )),
          ],
        )
        "###);
    }
}