# /*##########################################################################
#
# Copyright (c) 2004-2019 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ###########################################################################*/
"""RST directive to include snapshot of a Qt application in Sphinx doc.

Configuration variable in conf.py:

- snapshotqt_image_type: image file extension (default 'png').
- snapshotqt_script_dir: relative path of the root directory for scripts from
  the documentation source directory (i.e., the directory of conf.py)
  (default: '..').
"""

__authors__ = ["H. Payno", "T. Vincent"]
__license__ = "MIT"
__date__ = "07/12/2018"

import os
import logging
import sys
from docutils.parsers.rst.directives.images import Image
from docutils.parsers.rst import directives

# from docutils.par
# note: conf.py is patching the PATH so this will be the 'current' qt version

home = os.path.abspath(os.path.join(__file__, "..", "..", "..", ".."))


if not os.environ.get("DIRECTIVE_SNAPSHOT_QT") == "True":
    """
    In case we don't wan't to regenerate screenshot, simply apply Figure
    directive
    """

    class SnapshotQtDirective(Image):
        option_spec = Image.option_spec.copy()
        option_spec["script"] = directives.unchanged
        has_content = True

        def run(self):
            self.options["figwidth"] = "image"
            self.content = []

            # Create an image filename from arguments
            return Image.run(self)

    def makescreenshot(*args, **kwargs):
        raise RuntimeError(
            "not defined without env variable SILX_GENERATE_SCREENSHOT set to True"
        )

    def setup(app):
        app.add_config_value("snapshotqt_image_type", "png", "env")
        app.add_config_value("snapshotqt_script_dir", "..", "env")
        app.add_directive("snapshotqt", SnapshotQtDirective)
        return {"version": "0.1"}

else:
    from silx.gui import qt

    logging.basicConfig()
    _logger = logging.getLogger(__name__)

    # RST directive ###############################################################

    class SnapshotQtDirective(Image):
        """Image of a Qt application snapshot.

        Directive Type: "snapshotqt"
        Doctree Elements: As for figure
        Directive Arguments: One or more, required (script URI + script arguments).
        Directive Options: Possible.
        Directive Content: Interpreted as the figure caption and optional legend.

        A "snapshotqt" is a rst `figure
        <http://docutils.sourceforge.net/docs/ref/rst/directives.html#figure>`_
        that is generated from a Python script that uses Qt.

        The path of the script to take a snapshot is relative to
        the path given in conf.py 'snapshotqt_script_dir' value.

        ::

            .. snapshotqt: img/demo.py
               :align: center
               :height: 5cm

               source code


        you can also define a snapshot from a script, using the :script: option
        .. note:: on this path are given from the project root level

        ::
            .. snapshotqt: img/demo.py
               :align: center
               :height: 5cm
               :script: myscript.py
        """

        option_spec = Image.option_spec.copy()
        option_spec["script"] = directives.unchanged
        has_content = True

        def run(self):
            assert len(self.arguments) > 0
            # Run script stored in arguments and replace by snapshot filename
            script = self.options.pop("script", None)
            env = self.state.document.settings.env

            image_ext = env.config.snapshotqt_image_type.lower()
            script_name = self.arguments[0].replace(image_ext, "py")
            output_script = os.path.join(env.app.outdir, script_name)

            image_file_source_path = env.relfn2path(self.arguments[0])[0]
            image_file_source_path = os.path.join(
                home, env.srcdir, image_file_source_path
            )

            def createNeededDirs(_dir):
                parentDir = os.path.dirname(_dir)
                if parentDir not in ("", os.sep):
                    createNeededDirs(parentDir)
                if os.path.exists(_dir) is False:
                    os.mkdir(_dir)

            createNeededDirs(os.path.dirname(output_script))

            has_source_code = not (self.content is None or len(self.content) == 0)
            if has_source_code:
                with open(output_script, "w") as _file:
                    _file.write("# from silx.gui import qt\n")
                    _file.write("# app = qt.QApplication([])\n")
                    for _line in self.content:
                        _towrite = _line.lstrip(" ")
                        if not _towrite.startswith(":"):
                            _file.write(_towrite + "\n")
                    _file.write("app.exec()")
                self.content = []
                if script is not None:
                    _logger.warning(
                        "Cannot specify a script if source code (content) is given."
                        "Ignore script option"
                    )
                makescreenshot(
                    script_or_module=output_script, filename=image_file_source_path
                )
            else:
                # script
                if script is None:
                    _logger.warning(
                        "no source code or script defined in the snapshot"
                        "directive, fail to generate a screenshot"
                    )
                else:
                    script_path = os.path.join(home, script)
                    makescreenshot(
                        script_or_module=script_path, filename=image_file_source_path
                    )

            #
            # Use created image as in Figure
            return super(SnapshotQtDirective, self).run()

    def setup(app):
        app.add_config_value("snapshotqt_image_type", "png", "env")
        app.add_config_value("snapshotqt_script_dir", "..", "env")
        app.add_directive("snapshotqt", SnapshotQtDirective)
        return {"version": "0.1"}

    # screensImageFileDialogH5.hot function ########################################################

    def makescreenshot(script_or_module, filename):
        _logger.info(
            "generate screenshot for %s from %s, binding is %s"
            "" % (filename, script_or_module, qt.BINDING)
        )

        def grabWindow(winID):
            screen = qt.QApplication.primaryScreen()
            return screen.grabWindow(winID)

        global _count
        _count = 15
        global _TIMEOUT
        _TIMEOUT = 1000.0  # in ms
        app = qt.QApplication.instance() or qt.QApplication([])
        _logger.debug("Using Qt bindings: %s", qt)

        def _grabActiveWindowAndClose():
            global _count
            activeWindow = qt.QApplication.activeWindow()
            if activeWindow is not None:
                if activeWindow.isVisible():
                    # hot fix since issue with pySide2 API
                    if qt.BINDING == "PySide2":
                        pixmap = activeWindow.grab()
                    else:
                        pixmap = grabWindow(activeWindow.winId())
                    saveOK = pixmap.save(filename)
                    if not saveOK:
                        _logger.error("Cannot save snapshot to %s", filename)
                else:
                    _logger.error("activeWindow is not visible.")
                app.quit()
            else:
                _count -= 1
                if _count > 0:
                    # Only restart a timer if everything is OK
                    qt.QTimer.singleShot(_TIMEOUT, _grabActiveWindowAndClose)
                else:
                    app.quit()
                    raise TimeoutError(
                        "Aborted: It took too long to have an active window."
                    )

        script_or_module = os.path.abspath(script_or_module)

        sys.argv = [script_or_module]
        sys.path.append(os.path.abspath(os.path.dirname(script_or_module)))
        qt.QTimer.singleShot(_TIMEOUT, _grabActiveWindowAndClose)
        with open(script_or_module) as f:
            code = compile(f.read(), script_or_module, "exec")
            exec(code, globals(), locals())
