from pathlib import Path
from json import loads
import re

from hotdoc.core.exceptions import HotdocSourceException
from hotdoc.core.extension import Extension
from hotdoc.core.tree import Page
from hotdoc.core.project import Project
from hotdoc.run_hotdoc import Application
from hotdoc.core.formatter import Formatter
from hotdoc.utils.loggable import Logger, warn, info

import typing as T

if T.TYPE_CHECKING:
    import argparse

Logger.register_warning_code('unknown-refman-link', HotdocSourceException, 'refman-links')

class RefmanLinksExtension(Extension):
    extension_name = 'refman-links'
    argument_prefix = 'refman'

    def __init__(self, app: Application, project: Project):
        self.project: Project
        super().__init__(app, project)
        self._data_file: T.Optional[Path] = None
        self._data: T.Dict[str, str] = {}

    @staticmethod
    def add_arguments(parser: 'argparse.ArgumentParser'):
        group = parser.add_argument_group(
            'Refman links',
            'Custom Meson extension',
        )

        # Add Arguments with `group.add_argument(...)`
        group.add_argument(
            f'--refman-data-file',
            help="JSON file with the mappings to replace",
            default=None,
        )

    def parse_config(self, config: T.Dict[str, T.Any]) -> None:
        super().parse_config(config)
        self._data_file = config.get('refman_data_file')

    def _formatting_page_cb(self, formatter: Formatter, page: Page) -> None:
        ''' Replace Meson refman tags

        Links of the form [[function]] are automatically replaced
        with valid links to the correct URL. To reference objects / types use the
        [[@object]] syntax.
        '''
        link_regex = re.compile(r'(\[\[#?@?([ \n\t]*[a-zA-Z0-9_]+[ \n\t]*\.)*[ \n\t]*[a-zA-Z0-9_]+[ \n\t]*\]\])(.)?', re.MULTILINE)
        for m in link_regex.finditer(page.formatted_contents):
            i = m.group(1)
            obj_id: str = i[2:-2]
            obj_id = re.sub(r'[ \n\t]', '', obj_id)  # Remove whitespaces

            # Marked as inside a code block?
            in_code_block = False
            if obj_id.startswith('#'):
                in_code_block = True
                obj_id = obj_id[1:]

            if obj_id not in self._data:
                warn('unknown-refman-link', f'{Path(page.name).name}: Unknown Meson refman link: "{obj_id}"')
                continue

            # Just replaces [[!file.id]] paths with the page file (no fancy HTML)
            if obj_id.startswith('!'):
                page.formatted_contents = page.formatted_contents.replace(i, self._data[obj_id])
                continue

            # Fancy links for functions and methods
            text = obj_id
            if text.startswith('@'):
                text = text[1:]
            elif in_code_block:
                if m.group(3) != '(':
                    text = text + '()'
            else:
                text = text + '()'
            if not in_code_block:
                text = f'<code>{text}</code>'
            link = f'<a href="{self._data[obj_id]}"><ins>{text}</ins></a>'
            page.formatted_contents = page.formatted_contents.replace(i, link, 1)

    def setup(self) -> None:
        super().setup()

        if not self._data_file:
            info('Meson refman extension DISABLED')
            return

        raw = Path(self._data_file).read_text(encoding='utf-8')
        self._data = loads(raw)

        # Register formatter
        for ext in self.project.extensions.values():
            ext = T.cast(Extension, ext)
            ext.formatter.formatting_page_signal.connect(self._formatting_page_cb)
        info('Meson refman extension LOADED')

    @staticmethod
    def get_dependencies() -> T.List[T.Type[Extension]]:
        return []  # In case this extension has dependencies on other extensions

def get_extension_classes() -> T.List[T.Type[Extension]]:
    return [RefmanLinksExtension]