"""
This module defines the :py:class:`Scene` class, which serves as a "universe" or context for all actions
performed by a script. The global scene object is accessible as module-level variable :py:data:`ovito.scene`.
The scene manages a list of :py:class:`~ovito.pipeline.Pipeline` objects, which will be visible in images and videos
when rendering the scene through a :py:class:`~ovito.vis.Viewport`. Furthermore, you can save the entire
scene definition including all pipelines to a :file:`.ovito` session state file, which can be opened in the graphical OVITO application.
"""

# Placeholder, which will point to the global scene object once the ovito module is fully initialized.
scene = None

import sys
import os

# Workaround for a clash between the FreeType library that is statically linked into Matplotlib's binary wheels
# and the system's shared FreeType library, which the Qt framework uses.
#
# Matplotlib's extension module 'matplotlib.ft2font' contains its own copy of the FreeType library, but it exports
# the C symbols of that library. Qt, which gets loaded into the same process by OVITO, links against the system's
# shared FreeType library instead. As soon as Qt renders the first text string, FreeType dynamically loads the HarfBuzz
# library by calling dlopen() with the RTLD_GLOBAL flag, which makes the symbols of the system FreeType library (and its
# dependencies) globally visible. Any extension module that is loaded after that point gets its FreeType calls
# redirected by the dynamic linker to the system library. For Matplotlib, this version mismatch leads to text rendering
# errors such as "FT_Render_Glyph failed with error 0x62: raster overflow".
#
# To prevent this, we make the import machinery load 'matplotlib.ft2font' with the RTLD_DEEPBIND flag, which gives
# the symbols provided by the module itself precedence over the ones in the process' global namespace.
# Note: RTLD_DEEPBIND is a GNU extension, which is only available on Linux systems.
if sys.platform.startswith('linux') and hasattr(os, 'RTLD_DEEPBIND'):

    import importlib.abc
    import importlib.machinery
    import importlib.util

    class _DeepBindExtensionLoader(importlib.machinery.ExtensionFileLoader):
        """Loads a C extension module with the RTLD_DEEPBIND flag set."""
        def create_module(self, spec):
            old_flags = sys.getdlopenflags()
            sys.setdlopenflags(old_flags | os.RTLD_DEEPBIND)
            try:
                return super().create_module(spec)
            finally:
                sys.setdlopenflags(old_flags)

    class _MatplotlibImportHook(importlib.abc.MetaPathFinder):
        """Intercepts the import of Matplotlib's FreeType extension module to load it in a special way."""
        def find_spec(self, fullname, path, target=None):
            if fullname != 'matplotlib.ft2font' or not path:
                return None
            try:
                spec = importlib.machinery.PathFinder.find_spec(fullname, path, target)
                if spec is None or not isinstance(spec.loader, importlib.machinery.ExtensionFileLoader):
                    return None # Let the standard import machinery handle the module.
                return importlib.util.spec_from_file_location(fullname, spec.origin,
                    loader=_DeepBindExtensionLoader(fullname, spec.origin))
            except Exception:
                return None # Never let this workaround break the import of Matplotlib.

    sys.meta_path.insert(0, _MatplotlibImportHook())

# Initialize sub-modules.
import ovito.data
import ovito.vis
import ovito.modifiers
import ovito.pipeline
import ovito.io
import ovito.gui
import ovito.nonpublic

# Load all extension modules.
import ovito._extensions

# The public symbols of this root module:
__all__ = ['version', 'version_string', 'scene', 'Scene', 'enable_logging']
