"""Dispatcher used when transforming a mypy AST to the IR form.

mypyc.irbuild.builder and mypyc.irbuild.main are closely related.
"""

from __future__ import annotations

from typing import NoReturn

from mypy.nodes import (
    AssertStmt,
    AssertTypeExpr,
    AssignmentExpr,
    AssignmentStmt,
    AwaitExpr,
    Block,
    BreakStmt,
    BytesExpr,
    CallExpr,
    CastExpr,
    ClassDef,
    ComparisonExpr,
    ComplexExpr,
    ConditionalExpr,
    ContinueStmt,
    Decorator,
    DelStmt,
    DictExpr,
    DictionaryComprehension,
    EllipsisExpr,
    EnumCallExpr,
    ExpressionStmt,
    FloatExpr,
    ForStmt,
    FuncDef,
    GeneratorExpr,
    GlobalDecl,
    IfStmt,
    Import,
    ImportAll,
    ImportFrom,
    IndexExpr,
    IntExpr,
    LambdaExpr,
    ListComprehension,
    ListExpr,
    MatchStmt,
    MemberExpr,
    MypyFile,
    NamedTupleExpr,
    NameExpr,
    NewTypeExpr,
    NonlocalDecl,
    OperatorAssignmentStmt,
    OpExpr,
    OverloadedFuncDef,
    ParamSpecExpr,
    PassStmt,
    PromoteExpr,
    RaiseStmt,
    ReturnStmt,
    RevealExpr,
    SetComprehension,
    SetExpr,
    SliceExpr,
    StarExpr,
    StrExpr,
    SuperExpr,
    TempNode,
    TryStmt,
    TupleExpr,
    TypeAliasExpr,
    TypeApplication,
    TypedDictExpr,
    TypeVarExpr,
    TypeVarTupleExpr,
    UnaryExpr,
    Var,
    WhileStmt,
    WithStmt,
    YieldExpr,
    YieldFromExpr,
)
from mypyc.ir.ops import Value
from mypyc.irbuild.builder import IRBuilder, IRVisitor, UnsupportedException
from mypyc.irbuild.classdef import transform_class_def
from mypyc.irbuild.expression import (
    transform_assignment_expr,
    transform_bytes_expr,
    transform_call_expr,
    transform_comparison_expr,
    transform_complex_expr,
    transform_conditional_expr,
    transform_dict_expr,
    transform_dictionary_comprehension,
    transform_ellipsis,
    transform_float_expr,
    transform_generator_expr,
    transform_index_expr,
    transform_int_expr,
    transform_list_comprehension,
    transform_list_expr,
    transform_member_expr,
    transform_name_expr,
    transform_op_expr,
    transform_set_comprehension,
    transform_set_expr,
    transform_slice_expr,
    transform_str_expr,
    transform_super_expr,
    transform_tuple_expr,
    transform_unary_expr,
)
from mypyc.irbuild.function import (
    transform_decorator,
    transform_func_def,
    transform_lambda_expr,
    transform_overloaded_func_def,
)
from mypyc.irbuild.statement import (
    transform_assert_stmt,
    transform_assignment_stmt,
    transform_await_expr,
    transform_block,
    transform_break_stmt,
    transform_continue_stmt,
    transform_del_stmt,
    transform_expression_stmt,
    transform_for_stmt,
    transform_if_stmt,
    transform_import,
    transform_import_all,
    transform_import_from,
    transform_operator_assignment_stmt,
    transform_raise_stmt,
    transform_return_stmt,
    transform_try_stmt,
    transform_while_stmt,
    transform_with_stmt,
    transform_yield_expr,
    transform_yield_from_expr,
)


class IRBuilderVisitor(IRVisitor):
    """Mypy node visitor that dispatches to node transform implementations.

    This class should have no non-trivial logic.

    This visitor is separated from the rest of code to improve modularity and
    to avoid import cycles.

    This is based on the visitor pattern
    (https://en.wikipedia.org/wiki/Visitor_pattern).
    """

    # This gets passed to all the implementations and contains all the
    # state and many helpers. The attribute is initialized outside
    # this class since this class and IRBuilder form a reference loop.
    builder: IRBuilder

    def visit_mypy_file(self, mypyfile: MypyFile) -> None:
        assert False, "use transform_mypy_file instead"

    def visit_class_def(self, cdef: ClassDef) -> None:
        transform_class_def(self.builder, cdef)

    def visit_import(self, node: Import) -> None:
        transform_import(self.builder, node)

    def visit_import_from(self, node: ImportFrom) -> None:
        transform_import_from(self.builder, node)

    def visit_import_all(self, node: ImportAll) -> None:
        transform_import_all(self.builder, node)

    def visit_func_def(self, fdef: FuncDef) -> None:
        transform_func_def(self.builder, fdef)

    def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None:
        transform_overloaded_func_def(self.builder, o)

    def visit_decorator(self, dec: Decorator) -> None:
        transform_decorator(self.builder, dec)

    def visit_block(self, block: Block) -> None:
        transform_block(self.builder, block)

    # Statements

    def visit_expression_stmt(self, stmt: ExpressionStmt) -> None:
        transform_expression_stmt(self.builder, stmt)

    def visit_return_stmt(self, stmt: ReturnStmt) -> None:
        transform_return_stmt(self.builder, stmt)

    def visit_assignment_stmt(self, stmt: AssignmentStmt) -> None:
        transform_assignment_stmt(self.builder, stmt)

    def visit_operator_assignment_stmt(self, stmt: OperatorAssignmentStmt) -> None:
        transform_operator_assignment_stmt(self.builder, stmt)

    def visit_if_stmt(self, stmt: IfStmt) -> None:
        transform_if_stmt(self.builder, stmt)

    def visit_while_stmt(self, stmt: WhileStmt) -> None:
        transform_while_stmt(self.builder, stmt)

    def visit_for_stmt(self, stmt: ForStmt) -> None:
        transform_for_stmt(self.builder, stmt)

    def visit_break_stmt(self, stmt: BreakStmt) -> None:
        transform_break_stmt(self.builder, stmt)

    def visit_continue_stmt(self, stmt: ContinueStmt) -> None:
        transform_continue_stmt(self.builder, stmt)

    def visit_raise_stmt(self, stmt: RaiseStmt) -> None:
        transform_raise_stmt(self.builder, stmt)

    def visit_try_stmt(self, stmt: TryStmt) -> None:
        transform_try_stmt(self.builder, stmt)

    def visit_with_stmt(self, stmt: WithStmt) -> None:
        transform_with_stmt(self.builder, stmt)

    def visit_pass_stmt(self, stmt: PassStmt) -> None:
        pass

    def visit_assert_stmt(self, stmt: AssertStmt) -> None:
        transform_assert_stmt(self.builder, stmt)

    def visit_del_stmt(self, stmt: DelStmt) -> None:
        transform_del_stmt(self.builder, stmt)

    def visit_global_decl(self, stmt: GlobalDecl) -> None:
        # Pure declaration -- no runtime effect
        pass

    def visit_nonlocal_decl(self, stmt: NonlocalDecl) -> None:
        # Pure declaration -- no runtime effect
        pass

    def visit_match_stmt(self, stmt: MatchStmt) -> None:
        self.bail("Match statements are not yet supported", stmt.line)

    # Expressions

    def visit_name_expr(self, expr: NameExpr) -> Value:
        return transform_name_expr(self.builder, expr)

    def visit_member_expr(self, expr: MemberExpr) -> Value:
        return transform_member_expr(self.builder, expr)

    def visit_super_expr(self, expr: SuperExpr) -> Value:
        return transform_super_expr(self.builder, expr)

    def visit_call_expr(self, expr: CallExpr) -> Value:
        return transform_call_expr(self.builder, expr)

    def visit_unary_expr(self, expr: UnaryExpr) -> Value:
        return transform_unary_expr(self.builder, expr)

    def visit_op_expr(self, expr: OpExpr) -> Value:
        return transform_op_expr(self.builder, expr)

    def visit_index_expr(self, expr: IndexExpr) -> Value:
        return transform_index_expr(self.builder, expr)

    def visit_conditional_expr(self, expr: ConditionalExpr) -> Value:
        return transform_conditional_expr(self.builder, expr)

    def visit_comparison_expr(self, expr: ComparisonExpr) -> Value:
        return transform_comparison_expr(self.builder, expr)

    def visit_int_expr(self, expr: IntExpr) -> Value:
        return transform_int_expr(self.builder, expr)

    def visit_float_expr(self, expr: FloatExpr) -> Value:
        return transform_float_expr(self.builder, expr)

    def visit_complex_expr(self, expr: ComplexExpr) -> Value:
        return transform_complex_expr(self.builder, expr)

    def visit_str_expr(self, expr: StrExpr) -> Value:
        return transform_str_expr(self.builder, expr)

    def visit_bytes_expr(self, expr: BytesExpr) -> Value:
        return transform_bytes_expr(self.builder, expr)

    def visit_ellipsis(self, expr: EllipsisExpr) -> Value:
        return transform_ellipsis(self.builder, expr)

    def visit_list_expr(self, expr: ListExpr) -> Value:
        return transform_list_expr(self.builder, expr)

    def visit_tuple_expr(self, expr: TupleExpr) -> Value:
        return transform_tuple_expr(self.builder, expr)

    def visit_dict_expr(self, expr: DictExpr) -> Value:
        return transform_dict_expr(self.builder, expr)

    def visit_set_expr(self, expr: SetExpr) -> Value:
        return transform_set_expr(self.builder, expr)

    def visit_list_comprehension(self, expr: ListComprehension) -> Value:
        return transform_list_comprehension(self.builder, expr)

    def visit_set_comprehension(self, expr: SetComprehension) -> Value:
        return transform_set_comprehension(self.builder, expr)

    def visit_dictionary_comprehension(self, expr: DictionaryComprehension) -> Value:
        return transform_dictionary_comprehension(self.builder, expr)

    def visit_slice_expr(self, expr: SliceExpr) -> Value:
        return transform_slice_expr(self.builder, expr)

    def visit_generator_expr(self, expr: GeneratorExpr) -> Value:
        return transform_generator_expr(self.builder, expr)

    def visit_lambda_expr(self, expr: LambdaExpr) -> Value:
        return transform_lambda_expr(self.builder, expr)

    def visit_yield_expr(self, expr: YieldExpr) -> Value:
        return transform_yield_expr(self.builder, expr)

    def visit_yield_from_expr(self, o: YieldFromExpr) -> Value:
        return transform_yield_from_expr(self.builder, o)

    def visit_await_expr(self, o: AwaitExpr) -> Value:
        return transform_await_expr(self.builder, o)

    def visit_assignment_expr(self, o: AssignmentExpr) -> Value:
        return transform_assignment_expr(self.builder, o)

    # Constructs that shouldn't ever show up

    def visit_enum_call_expr(self, o: EnumCallExpr) -> Value:
        assert False, "can't compile analysis-only expressions"

    def visit__promote_expr(self, o: PromoteExpr) -> Value:
        assert False, "can't compile analysis-only expressions"

    def visit_namedtuple_expr(self, o: NamedTupleExpr) -> Value:
        assert False, "can't compile analysis-only expressions"

    def visit_newtype_expr(self, o: NewTypeExpr) -> Value:
        assert False, "can't compile analysis-only expressions"

    def visit_temp_node(self, o: TempNode) -> Value:
        assert False, "can't compile analysis-only expressions"

    def visit_type_alias_expr(self, o: TypeAliasExpr) -> Value:
        assert False, "can't compile analysis-only expressions"

    def visit_type_application(self, o: TypeApplication) -> Value:
        assert False, "can't compile analysis-only expressions"

    def visit_type_var_expr(self, o: TypeVarExpr) -> Value:
        assert False, "can't compile analysis-only expressions"

    def visit_paramspec_expr(self, o: ParamSpecExpr) -> Value:
        assert False, "can't compile analysis-only expressions"

    def visit_type_var_tuple_expr(self, o: TypeVarTupleExpr) -> Value:
        assert False, "can't compile analysis-only expressions"

    def visit_typeddict_expr(self, o: TypedDictExpr) -> Value:
        assert False, "can't compile analysis-only expressions"

    def visit_reveal_expr(self, o: RevealExpr) -> Value:
        assert False, "can't compile analysis-only expressions"

    def visit_var(self, o: Var) -> None:
        assert False, "can't compile Var; should have been handled already?"

    def visit_cast_expr(self, o: CastExpr) -> Value:
        assert False, "CastExpr should have been handled in CallExpr"

    def visit_assert_type_expr(self, o: AssertTypeExpr) -> Value:
        assert False, "AssertTypeExpr should have been handled in CallExpr"

    def visit_star_expr(self, o: StarExpr) -> Value:
        assert False, "should have been handled in Tuple/List/Set/DictExpr or CallExpr"

    # Helpers

    def bail(self, msg: str, line: int) -> NoReturn:
        """Reports an error and aborts compilation up until the last accept() call

        (accept() catches the UnsupportedException and keeps on
        processing. This allows errors to be non-blocking without always
        needing to write handling for them.
        """
        self.builder.error(msg, line)
        raise UnsupportedException()
