from breathe.directives.class_like import (
    DoxygenStructDirective,
    DoxygenClassDirective,
    DoxygenInterfaceDirective,
)
from breathe.directives.content_block import (
    DoxygenNamespaceDirective,
    DoxygenGroupDirective,
    DoxygenPageDirective,
)
from breathe.directives.file import DoxygenFileDirective, AutoDoxygenFileDirective
from breathe.directives.function import DoxygenFunctionDirective
from breathe.directives.index import DoxygenIndexDirective, AutoDoxygenIndexDirective
from breathe.directives.item import (
    DoxygenVariableDirective,
    DoxygenDefineDirective,
    DoxygenUnionDirective,
    DoxygenConceptDirective,
    DoxygenEnumDirective,
    DoxygenEnumValueDirective,
    DoxygenTypedefDirective,
)
from breathe.parser import DoxygenParserFactory
from breathe.project import ProjectInfoFactory
from breathe.process import AutoDoxygenProcessHandle

from sphinx.application import Sphinx

import os
import subprocess


def setup(app: Sphinx) -> None:
    directives = {
        "doxygenindex": DoxygenIndexDirective,
        "autodoxygenindex": AutoDoxygenIndexDirective,
        "doxygenfunction": DoxygenFunctionDirective,
        "doxygenstruct": DoxygenStructDirective,
        "doxygenclass": DoxygenClassDirective,
        "doxygeninterface": DoxygenInterfaceDirective,
        "doxygenvariable": DoxygenVariableDirective,
        "doxygendefine": DoxygenDefineDirective,
        "doxygenconcept": DoxygenConceptDirective,
        "doxygenenum": DoxygenEnumDirective,
        "doxygenenumvalue": DoxygenEnumValueDirective,
        "doxygentypedef": DoxygenTypedefDirective,
        "doxygenunion": DoxygenUnionDirective,
        "doxygennamespace": DoxygenNamespaceDirective,
        "doxygengroup": DoxygenGroupDirective,
        "doxygenfile": DoxygenFileDirective,
        "autodoxygenfile": AutoDoxygenFileDirective,
        "doxygenpage": DoxygenPageDirective,
    }

    # The directives need these global objects, so in order to smuggle
    # them in, we use env.temp_data. But it is cleared after each document
    # has been read, we use the source-read event to set them.
    # note: the parser factory contains a cache of the parsed XML
    # note: the project_info_factory also contains some caching stuff
    # TODO: is that actually safe for when reading in parallel?
    project_info_factory = ProjectInfoFactory(app)
    parser_factory = DoxygenParserFactory(app)

    def set_temp_data(
        app: Sphinx, project_info_factory=project_info_factory, parser_factory=parser_factory
    ):
        assert app.env is not None
        app.env.temp_data["breathe_project_info_factory"] = project_info_factory
        app.env.temp_data["breathe_parser_factory"] = parser_factory

    app.connect("source-read", lambda app, docname, source: set_temp_data(app))

    for name, directive in directives.items():
        app.add_directive(name, directive)

    app.add_config_value("breathe_projects", {}, True)  # Dict[str, str]
    app.add_config_value("breathe_default_project", "", True)  # str
    # Provide reasonable defaults for domain_by_extension mapping. Can be overridden by users.
    app.add_config_value(
        "breathe_domain_by_extension", {"py": "py", "cs": "cs"}, True
    )  # Dict[str, str]
    app.add_config_value("breathe_domain_by_file_pattern", {}, True)  # Dict[str, str]
    app.add_config_value("breathe_projects_source", {}, True)
    app.add_config_value("breathe_build_directory", "", True)
    app.add_config_value("breathe_default_members", (), True)
    app.add_config_value("breathe_show_define_initializer", False, "env")
    app.add_config_value("breathe_show_enumvalue_initializer", False, "env")
    app.add_config_value("breathe_show_include", True, "env")
    app.add_config_value("breathe_show_decl_file_include", False, "env")
    app.add_config_value("breathe_implementation_filename_extensions", [".c", ".cc", ".cpp"], True)
    app.add_config_value("breathe_doxygen_config_options", {}, True)
    app.add_config_value("breathe_doxygen_aliases", {}, True)
    app.add_config_value("breathe_use_project_refids", False, "env")
    app.add_config_value("breathe_order_parameters_first", False, "env")
    app.add_config_value("breathe_separate_member_pages", False, "env")

    breathe_css = "breathe.css"
    if os.path.exists(os.path.join(app.confdir, "_static", breathe_css)):
        app.add_css_file(breathe_css)

    def write_file(directory, filename, content):
        # Check the directory exists
        if not os.path.exists(directory):
            os.makedirs(directory)

        # Write the file with the provided contents
        with open(os.path.join(directory, filename), "w") as f:
            f.write(content)

    doxygen_handle = AutoDoxygenProcessHandle(
        subprocess.check_call, write_file, project_info_factory
    )

    def doxygen_hook(app: Sphinx):
        doxygen_handle.generate_xml(
            app.config.breathe_projects_source,
            app.config.breathe_doxygen_config_options,
            app.config.breathe_doxygen_aliases,
        )

    app.connect("builder-inited", doxygen_hook)
