"""
Core visualization operations based on PyVista.

Actual implementation of _Renderer and _Projection classes.
"""

# Authors: Alexandre Gramfort <alexandre.gramfort@inria.fr>
#          Eric Larson <larson.eric.d@gmail.com>
#          Guillaume Favelier <guillaume.favelier@gmail.com>
#          Joan Massich <mailsik@gmail.com>
#
# License: Simplified BSD

from contextlib import contextmanager
from distutils.version import LooseVersion
import os
import platform
import sys
import warnings

import numpy as np
import vtk

from ._abstract import _AbstractRenderer
from ._utils import (_get_colormap_from_array, _alpha_blend_background,
                     ALLOWED_QUIVER_MODES, _init_qt_resources)
from ...fixes import _get_args
from ...transforms import apply_trans
from ...utils import copy_base_doc_to_subclass_doc, _check_option


with warnings.catch_warnings():
    warnings.filterwarnings("ignore", category=DeprecationWarning)
    import pyvista
    from pyvista import Plotter, PolyData, Line, close_all, UnstructuredGrid
    try:
        from pyvistaqt import BackgroundPlotter  # noqa
    except ImportError:
        from pyvista import BackgroundPlotter
    from pyvista.plotting.plotting import _ALL_PLOTTERS
VTK9 = LooseVersion(getattr(vtk, 'VTK_VERSION', '9.0')) >= LooseVersion('9.0')


_FIGURES = dict()


class _Figure(object):
    def __init__(self,
                 plotter=None,
                 show=False,
                 title='PyVista Scene',
                 size=(600, 600),
                 shape=(1, 1),
                 background_color='black',
                 smooth_shading=True,
                 off_screen=False,
                 notebook=False):
        self.plotter = plotter
        self.display = None
        self.background_color = background_color
        self.smooth_shading = smooth_shading
        self.notebook = notebook

        self.store = dict()
        self.store['window_size'] = size
        self.store['shape'] = shape
        self.store['off_screen'] = off_screen
        self.store['border'] = False
        # multi_samples > 1 is broken on macOS + Intel Iris + volume rendering
        self.store['multi_samples'] = 1 if sys.platform == 'darwin' else 4

        if not self.notebook:
            self.store['show'] = show
            self.store['title'] = title
            self.store['auto_update'] = False
            self.store['menu_bar'] = False
            self.store['toolbar'] = False
            self.store['update_app_icon'] = False

        self._nrows, self._ncols = self.store['shape']
        self._azimuth = self._elevation = None

    def build(self):
        if self.notebook:
            plotter_class = Plotter
        else:
            plotter_class = BackgroundPlotter

        if self.plotter is None:
            if not self.notebook:
                from PyQt5.QtWidgets import QApplication
                app = QApplication.instance()
                if app is None:
                    app = QApplication(["MNE"])
                self.store['app'] = app
            plotter = plotter_class(**self.store)
            plotter.background_color = self.background_color
            self.plotter = plotter
            if not self.notebook and hasattr(plotter_class, 'set_icon'):
                _init_qt_resources()
                _process_events(plotter)
                kind = 'bigsur-' if platform.mac_ver()[0] >= '10.16' else ''
                plotter.set_icon(f":/mne-{kind}icon.png")
        _process_events(self.plotter)
        _process_events(self.plotter)
        return self.plotter

    def is_active(self):
        if self.plotter is None:
            return False
        return hasattr(self.plotter, 'ren_win')


class _Projection(object):
    """Class storing projection information.

    Attributes
    ----------
    xy : array
        Result of 2d projection of 3d data.
    pts : None
        Scene sensors handle.
    """

    def __init__(self, xy=None, pts=None):
        """Store input projection information into attributes."""
        self.xy = xy
        self.pts = pts

    def visible(self, state):
        """Modify visibility attribute of the sensors."""
        self.pts.SetVisibility(state)


@copy_base_doc_to_subclass_doc
class _PyVistaRenderer(_AbstractRenderer):
    """Class managing rendering scene.

    Attributes
    ----------
    plotter: Plotter
        Main PyVista access point.
    name: str
        Name of the window.
    """

    def __init__(self, fig=None, size=(600, 600), bgcolor='black',
                 name="PyVista Scene", show=False, shape=(1, 1),
                 notebook=None, smooth_shading=True):
        from .renderer import MNE_3D_BACKEND_TESTING
        from .._3d import _get_3d_option