"""ANTLR-generated files for parsing OpenQASM 3 files.

This package sets up its import contents to be taken from the generated files
whose ANTLR version matches the installed version of the ANTLR runtime.  The
generated files should be placed in directories called ``_<major>_<minor>``,
where `major` is 4, and `minor` is the minor version of ANTLR (e.g. if ANTLR
4.10 was used, those files should be in ``_4_10``).

The ANTLR files from more than one version of ANTLR can be present at once.  This package will
dynamically load the correct version based on the installed version of the runtime.
"""

from importlib.abc import MetaPathFinder as _MetaPathFinder, Loader as _Loader
import pathlib
import sys

if sys.version_info < (3, 10):
    from importlib_metadata import version as _version
else:
    from importlib.metadata import version as _version

# The `antlr4` package is supplied by `antlr4_python3_runtime`.
_parts = [int(x) for x in _version("antlr4_python3_runtime").split(".")]
_resolved_dir = f"_{_parts[0]}_{_parts[1]}"
_antlr_dir = pathlib.Path(__file__).parent
if not (_antlr_dir / _resolved_dir).is_dir():
    _available = [path.parent.name[1:] for path in _antlr_dir.rglob("qasm3Parser.py")]
    if not _available:
        raise ImportError("No ANTLR-generated parsers found.")
    raise ImportError(
        f"Missing ANTLR-generated parser for version '{_parts[0]}.{_parts[1]}'."
        f" Available versions: {_available!r}"
    )


class ANTLRMetaPathFinder(_MetaPathFinder):
    """Redirect module/package lookups in `openqasm3.antlr` to the concrete implementations
    pre-generated by the ANTLR version that matches the installed version of the runtime."""

    def __init__(self, version_package: str):
        top_level = __package__.rsplit(".")[0]
        # Note the extra `.` in the domain because we don't want to handle ourselves.
        self._domain = f"{top_level}._antlr."
        self._versioned = f"{top_level}._antlr.{version_package}"

    def find_spec(self, fullname, path, target=None):
        from importlib.machinery import SourceFileLoader
        from importlib.util import spec_from_loader, find_spec

        if not fullname.startswith(self._domain) or fullname.startswith(self._versioned):
            return None
        newname = f"{self._versioned}.{fullname[len(self._domain):]}"
        # Get the spec and loader for the direct path to the versioned file, and rewrap them to have
        # the unversioned module name.  The modules aren't loaded (or executed) by this, but the
        # loader is configured so that when they are, their scopes all carry the unversioned name.
        return spec_from_loader(fullname, SourceFileLoader(fullname, find_spec(newname).origin))


sys.meta_path = [ANTLRMetaPathFinder(_resolved_dir)] + sys.meta_path

# ... and now the additional content of this module.

RUNTIME_VERSION = tuple(int(x) for x in _parts)
"""The runtime-detected version of the ANTLR runtime, as a tuple like ``sys.version_info``."""

# These imports are re-directed into concrete versioned ones.  Doing them
# manually here helps stop pylint complaining.
from . import qasm3Lexer, qasm3Parser, qasm3ParserVisitor, qasm3ParserListener
