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 = 'lib/python3.13/site-packages/vtkmodules'
    _vtk_dll_path = 'bin'
    # 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

#_load_vtkmodules_static()


#------------------------------------------------------------------------------
# list the contents
__all__ = [
        'vtkCommonCore',
    'vtkCommonMath',
    'vtkCommonTransforms',
    'vtkCommonDataModel',
    'vtkCommonExecutionModel',
    'vtkCommonMisc',
    'vtkFiltersCore',
    'vtkFiltersGeneral',
    'vtkRenderingCore',
    'vtkRenderingContext2D',
    'vtkChartsCore',
    'vtkInteractionStyle',
    'vtkFiltersSources',
    'vtkInteractionWidgets',
    'vtkViewsCore',
    'vtkViewsInfovis',
    'vtkCommonColor',
    'vtkViewsContext2D',
    'vtkImagingCore',
    'vtkImagingColor',
    'vtkIOXMLParser',
    'vtkIOXML',
    'vtkTestingRendering',
    'vtkPythonContext2D',
    'vtkRenderingFreeType',
    'vtkRenderingLabel',
    'vtkRenderingLOD',
    'vtkIOCore',
    'vtkIOImage',
    'vtkRenderingHyperTreeGrid',
    'vtkRenderingUI',
    'vtkRenderingOpenGL2',
    'vtkRenderingLICOpenGL2',
    'vtkRenderingImage',
    'vtkRenderingGridAxes',
    'vtkRenderingContextOpenGL2',
    'vtkFiltersCellGrid',
    'vtkRenderingCellGrid',
    'vtkImagingMath',
    'vtkRenderingVolume',
    'vtkRenderingVolumeOpenGL2',
    'vtkIOVeraOut',
    'vtkIOTecplotTable',
    'vtkIOSegY',
    'vtkIOParallelXML',
    'vtkIOPLY',
    'vtkIOMovie',
    'vtkIOOggTheora',
    'vtkIONetCDF',
    'vtkIOMotionFX',
    'vtkIOCellGrid',
    'vtkIOLegacy',
    'vtkIOGeometry',
    'vtkIOParallel',
    'vtkIOMINC',
    'vtkIOLSDyna',
    'vtkIOLANLX3D',
    'vtkIOImport',
    'vtkCommonSystem',
    'vtkParallelCore',
    'vtkIOIOSS',
    'vtkIOHDF',
    'vtkIOFLUENTCFF',
    'vtkIOVideo',
    'vtkIOFDS',
    'vtkIOInfovis',
    'vtkRenderingSceneGraph',
    'vtkRenderingVtkJS',
    'vtkIOExport',
    'vtkIOExportPDF',
    'vtkRenderingGL2PSOpenGL2',
    'vtkIOExportGL2PS',
    'vtkIOExodus',
    'vtkIOEngys',
    'vtkIOEnSight',
    'vtkIOERF',
    'vtkIOCityGML',
    'vtkIOChemistry',
    'vtkIOCesium3DTiles',
    'vtkIOCONVERGECFD',
    'vtkIOCGNSReader',
    'vtkIOAvmesh',
    'vtkIOAsynchronous',
    'vtkIOAMR',
    'vtkInteractionImage',
    'vtkInfovisLayout',
    'vtkImagingStencil',
    'vtkImagingStatistics',
    'vtkImagingGeneral',
    'vtkImagingMorphological',
    'vtkImagingFourier',
    'vtkIOSQL',
    'vtkRenderingAnnotation',
    'vtkImagingHybrid',
    'vtkGeovisCore',
    'vtkFiltersTopology',
    'vtkFiltersTensor',
    'vtkFiltersSelection',
    'vtkFiltersSMP',
    'vtkFiltersPython',
    'vtkFiltersProgrammable',
    'vtkFiltersModeling',
    'vtkFiltersPoints',
    'vtkFiltersStatistics',
    'vtkFiltersImaging',
    'vtkFiltersExtraction',
    'vtkFiltersGeometry',
    'vtkFiltersHybrid',
    'vtkFiltersHyperTree',
    'vtkFiltersTexture',
    'vtkFiltersParallel',
    'vtkFiltersParallelImaging',
    'vtkFiltersTemporal',
    'vtkFiltersGeometryPreview',
    'vtkFiltersGeneric',
    'vtkCommonComputationalGeometry',
    'vtkFiltersFlowPaths',
    'vtkFiltersAMR',
    'vtkDomainsChemistry',
    'vtkDomainsChemistryOpenGL2',
    'vtkCommonPython',
    'vtkImagingSources',
    'vtkInfovisCore',
    'vtkFiltersVerdict',
    'vtkFiltersReduction',
    'all',
    'gtk',
    'numpy_interface',
    'qt',
    'test',
    'tk',
    'util',
    'wx',

]
#------------------------------------------------------------------------------
# get the version
__version__ = "9.6.0"

#------------------------------------------------------------------------------
# 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)
