r"""
Currently, this package is experimental and may change in the future.
"""
from __future__ import absolute_import
import sys
import importlib.util

from pathlib import Path

from importlib.abc import MetaPathFinder
from importlib.machinery import ExtensionFileLoader

LOADING_STACK = []
LIB_EXTS = [".pyd", ".so"]

def find_lib_path(paths, vtk_module_name):
    for ext in LIB_EXTS:
        for base_path in paths:
            # vtk-wheel => vtkCommonCore.cpython-310-darwin.so
            # paraview  => vtkCommonCore.so
            # Caution: vtkIOXML vs vtkIOXMLParser
            for f in Path(base_path).glob(f"{vtk_module_name}[.-]*"):
                resolved_file = f.resolve()
                if resolved_file.is_file() and ext in resolved_file.suffixes:
                    return str(resolved_file)


class VTKMetaHook(MetaPathFinder):
    """Attach a custom  loaded for vtk native library loading to defer loading of pure python dependencies"""
    def find_spec(self, fullname, path, target=None):
        if fullname.startswith("vtkmodules.vtk"):
            vtk_module_name = fullname.split(".")[1]
            module_path = find_lib_path(path, vtk_module_name)
            if module_path is None:
                return None

            LOADING_STACK.append(fullname)
            return importlib.util.spec_from_file_location(fullname, module_path, loader=VTKLoader(fullname, module_path))

        return None


class VTKLoader(ExtensionFileLoader):
    """Flush any pending dependency load once initialize() phase is done"""
    def exec_module(self, module):
        super().exec_module(module)

        # Process pending dependencies only if the module match the first load request
        if len(LOADING_STACK) and LOADING_STACK[0] == module.__name__:
            LOADING_STACK.clear()
            on_vtk_module_init_completed()



# Register our hook for vtk library loader
sys.meta_path.insert(0, VTKMetaHook())


def _windows_dll_path():
    import os
    _vtk_python_path = '@VTK_PYTHON_SITE_PACKAGES_SUFFIX@/vtkmodules'
    _vtk_dll_path = '@CMAKE_INSTALL_BINDIR@'
    # Compute the DLL path based on the location of the file and traversing up
    # the installation prefix to append the DLL path.
    _vtk_dll_directory = os.path.dirname(os.path.abspath(__file__))
    # Loop while we have components to remove.
    while _vtk_python_path not in ('', '.', '/'):
        # Strip a directory away.
        _vtk_python_path = os.path.dirname(_vtk_python_path)
        _vtk_dll_directory = os.path.dirname(_vtk_dll_directory)
    _vtk_dll_directory = os.path.join(_vtk_dll_directory, _vtk_dll_path)
    if os.path.exists(_vtk_dll_directory):
        # We never remove this path; it is required for VTK to work and there's
        # no scope where we can easily remove the directory again.
        _ = os.add_dll_directory(_vtk_dll_directory)

    # Build tree support.
    try:
        from . import _build_paths

        # Add any paths needed for the build tree.
        for path in _build_paths.paths:
            if os.path.exists(path):
                _ = os.add_dll_directory(path)
    except ImportError:
        # Relocatable install tree (or non-Windows).
        pass


# CPython 3.8 added behaviors which modified the DLL search path on Windows to
# only search "blessed" paths. When importing SMTK, ensure that SMTK's DLLs are
# in this set of "blessed" paths.
if sys.version_info >= (3, 8) and sys.platform == 'win32':
    _windows_dll_path()


#------------------------------------------------------------------------------
# this little trick is for static builds of VTK. In such builds, if
# the user imports this Python package in a non-statically linked Python
# interpreter i.e. not of the of the VTK-python executables, then we import the
# static components importer module.
def _load_vtkmodules_static():
    if 'vtkmodules_vtkCommonCore' not in sys.builtin_module_names:
        import _vtkmodules_static

@_vtkmodules_static_import@


#------------------------------------------------------------------------------
# list the contents
__all__ = [
    @_vtkmodules_all@
]
#------------------------------------------------------------------------------
# get the version
__version__ = "@VTK_MAJOR_VERSION@.@VTK_MINOR_VERSION@.@VTK_BUILD_VERSION@"

#------------------------------------------------------------------------------
# describe import dependencies to properly define Python @override
MODULE_MAPPER = {
    "vtkCommonDataModel": [
        "vtkmodules.util.data_model",
    ],
    "vtkCommonExecutionModel": [
        "vtkmodules.util.execution_model",
    ],
}
LOADED_MODULES = set()
PENDING_LOADED_MODULES = set()

def register_vtk_module_dependencies(vtk_module_name, *import_names):
    """Method to call for registering external override on vtkmodule load"""
    MODULE_MAPPER.setdefault(vtk_module_name, []).extend(import_names)

    # If already loaded let's make sure we import it now
    if vtk_module_name in LOADED_MODULES:
        for import_name in import_names:
            importlib.import_module(import_name)


def on_vtk_module_init(module_name):
    """Automatically called by vtkmodule when they are loaded"""
    if module_name in LOADED_MODULES:
        return

    PENDING_LOADED_MODULES.add(module_name)


def on_vtk_module_init_completed():
    pending = list(PENDING_LOADED_MODULES)
    PENDING_LOADED_MODULES.clear()

    for module_name in pending:
        LOADED_MODULES.add(module_name)
        for import_name in MODULE_MAPPER.get(module_name, []):
            importlib.import_module(import_name)
