use super::{Diagnostic, DiagnosticBuilder};
use crate::ast::AstSpan;
use crate::lexer::TokenPosition;
use crate::parser::{ParseError, ParseErrorKind};
use std::convert::From;

fn diagnostic_parenthesized_expression_empty(
    error: ParseError,
    left_parenthesis_position: TokenPosition,
) -> Diagnostic {
    DiagnosticBuilder::error("empty parenthesized expression")
        .primary_label(error.blame_span, "expected an expression before this `)`")
        .secondary_label(
            AstSpan::new(left_parenthesis_position),
            "parenthesized expression starts here",
        )
        .help("Remove the parentheses or put an expression inside them.")
        .build()
}

fn diagnostic_class_type_missing_type_argument(
    error: ParseError,
    left_angle_bracket_position: TokenPosition,
) -> Diagnostic {
    DiagnosticBuilder::error("missing type argument in `class<...>`")
        .primary_label(error.blame_span, "expected a type name here")
        .secondary_label(
            AstSpan::new(left_angle_bracket_position),
            "type argument list starts here",
        )
        .help("Write a type name, for example `class<Pawn>`.")
        .build()
}

fn diagnostic_class_type_missing_closing_angle_bracket(
    error: ParseError,
    left_angle_bracket_position: TokenPosition,
) -> Diagnostic {
    DiagnosticBuilder::error("missing closing `>` in `class<...>`")
        .primary_label(error.blame_span, "expected `>` here")
        .secondary_label(
            AstSpan::new(left_angle_bracket_position),
            "this `<` starts the type argument",
        )
        .help("Add `>` to close the class type expression.")
        .build()
}

fn diagnostic_parenthesized_expression_missing_closing_parenthesis(
    error: ParseError,
    left_parenthesis_position: TokenPosition,
) -> Diagnostic {
    DiagnosticBuilder::error("missing closing `)`")
        .primary_label(error.blame_span, "expected `)` here")
        .secondary_label(
            AstSpan::new(left_parenthesis_position),
            "this `(` starts the parenthesized expression",
        )
        .help("Add `)` to close the expression.")
        .build()
}

fn diagnostic_expression_expected(error: ParseError) -> Diagnostic {
    let mut builder = DiagnosticBuilder::error("expected expression")
        .primary_label(error.blame_span, "this token cannot start an expression")
        .help(
            "Expressions can start with literals, identifiers, `(`, `{`, or expression keywords.",
        );

    if let Some(related_span) = error.related_span {
        builder = builder.secondary_label(related_span, "expression context starts here");
    }

    builder.build()
}

fn diagnostic_class_type_invalid_type_argument(
    error: ParseError,
    left_angle_bracket_position: TokenPosition,
) -> Diagnostic {
    DiagnosticBuilder::error("invalid type argument in `class<...>`")
        .primary_label(error.blame_span, "expected a qualified type name here")
        .secondary_label(
            AstSpan::new(left_angle_bracket_position),
            "type argument list starts here",
        )
        .note("Only a qualified type name is accepted between `<` and `>` here.")
        .build()
}

fn diagnostic_new_too_many_arguments(
    error: ParseError,
    left_parenthesis_position: TokenPosition,
) -> Diagnostic {
    DiagnosticBuilder::error("too many arguments in `new(...)`")
        .primary_label(error.blame_span, "unexpected extra argument")
        .secondary_label(
            AstSpan::new(left_parenthesis_position),
            "this argument list accepts at most three arguments",
        )
        .note("The three slots are `outer`, `name`, and `flags`.")
        .help("Remove the extra argument.")
        .build()
}

fn diagnostic_new_missing_closing_parenthesis(
    error: ParseError,
    left_parenthesis_position: TokenPosition,
) -> Diagnostic {
    DiagnosticBuilder::error("missing closing `)` in `new(...)`")
        .primary_label(error.blame_span, "expected `)` here")
        .secondary_label(
            AstSpan::new(left_parenthesis_position),
            "this argument list starts here",
        )
        .help("Add `)` to close the argument list.")
        .build()
}

fn diagnostic_new_missing_class_specifier(
    error: ParseError,
    new_keyword_position: TokenPosition,
) -> Diagnostic {
    let mut builder = DiagnosticBuilder::error("missing class specifier in `new` expression")
        .primary_label(
            error.blame_span,
            "expected the class or expression to instantiate here",
        )
        .secondary_label(
            AstSpan::new(new_keyword_position),
            "`new` expression starts here",
        )
        .help("Add the class or expression to instantiate after `new` or `new(...)`.");

    if let Some(related_span) = error.related_span {
        builder = builder.secondary_label(related_span, "optional `new(...)` arguments end here");
    }

    builder.build()
}

impl From<ParseError> for Diagnostic {
    fn from(error: ParseError) -> Self {
        match error.kind {
            ParseErrorKind::ParenthesizedExpressionEmpty {
                left_parenthesis_position,
            } => diagnostic_parenthesized_expression_empty(error, left_parenthesis_position),

            ParseErrorKind::ClassTypeMissingTypeArgument {
                left_angle_bracket_position,
            } => diagnostic_class_type_missing_type_argument(error, left_angle_bracket_position),

            ParseErrorKind::ClassTypeMissingClosingAngleBracket {
                left_angle_bracket_position,
            } => diagnostic_class_type_missing_closing_angle_bracket(
                error,
                left_angle_bracket_position,
            ),

            ParseErrorKind::ParenthesizedExpressionMissingClosingParenthesis {
                left_parenthesis_position,
            } => diagnostic_parenthesized_expression_missing_closing_parenthesis(
                error,
                left_parenthesis_position,
            ),

            ParseErrorKind::ExpressionExpected => diagnostic_expression_expected(error),

            ParseErrorKind::ClassTypeInvalidTypeArgument {
                left_angle_bracket_position,
            } => diagnostic_class_type_invalid_type_argument(error, left_angle_bracket_position),

            ParseErrorKind::NewTooManyArguments {
                left_parenthesis_position,
            } => diagnostic_new_too_many_arguments(error, left_parenthesis_position),

            ParseErrorKind::NewMissingClosingParenthesis {
                left_parenthesis_position,
            } => diagnostic_new_missing_closing_parenthesis(error, left_parenthesis_position),

            ParseErrorKind::NewMissingClassSpecifier {
                new_keyword_position,
            } => diagnostic_new_missing_class_specifier(error, new_keyword_position),

            _ => DiagnosticBuilder::error(format!("error {:?} while parsing", error.kind))
                .primary_label(error.covered_span, "happened here")
                .build(),
        }
    }
}
