"""C language parser using tree-sitter."""

from __future__ import annotations

from typing import Any

import tree_sitter_c
from tree_sitter import Node

from viper.languages.base import Function, TreeSitterParser


class CTreeSitterParser(TreeSitterParser):
    """Tree-sitter based parser for the C language."""

    language_name = "c"
    file_extensions = [".c", ".h"]

    @staticmethod
    def _get_ts_language() -> Any:
        return tree_sitter_c.language()

    @staticmethod
    def _get_function_query() -> str:
        return "(function_definition) @func"

    def _extract_function(self, node: Node, code: bytes) -> Function:
        func = Function()
        func.start = node.start_point[0] + 1
        func.end = node.end_point[0] + 1

        ret_ptr = False
        detail_query = self._get_detail_query()
        for key, nodes in self.capture_nodes(node, detail_query).items():
            a = nodes[0]
            val = code[a.start_byte:a.end_byte].decode("utf-8")
            if key == "name":
                func.name = val
                func.line = a.start_point[0] + 1
            elif key == "return_type":
                func.return_type = val
            elif key == "signature":
                func.signature = val
            elif key == "ret_ptr":
                ret_ptr = True

        if ret_ptr:
            func.return_type += "*"
        # Normalise multi-line signatures
        if "\n" in func.signature:
            func.signature = " ".join(func.signature.split())

        return func

    @staticmethod
    def _get_detail_query() -> str:
        """Query to extract name, return type, signature from a function node."""
        return """
        (function_definition
            type: (primitive_type) @return_type
            declarator: [
                (function_declarator
                    declarator: (identifier) @name
                    parameters: (_) @signature
                ) @decl
                (pointer_declarator
                    declarator:
                    (function_declarator
                        declarator: (identifier) @name
                        parameters: (_) @signature
                    ) @decl
                ) @ret_ptr
            ]
        )
        """

    def _get_decision_node_types(self) -> set[str]:
        return {
            "if_statement",
            "for_statement",
            "while_statement",
            "do_statement",
            "case_statement",
            "conditional_expression",  # ternary ?:
            "&&",
            "||",
        }

    def _get_modified_decision_node_types(self) -> set[str]:
        return {
            "if_statement",
            "for_statement",
            "while_statement",
            "do_statement",
            "switch_statement",
            "conditional_expression",
            "&&",
            "||",
        }

    def _get_statement_node_types(self) -> set[str]:
        return {
            "expression_statement",
            "declaration",
            "return_statement",
            "if_statement",
            "for_statement",
            "while_statement",
            "do_statement",
            "switch_statement",
            "break_statement",
            "continue_statement",
            "goto_statement",
            "case_statement",
        }
