"""IPython extension that activates special Jupyter notebook features of Nengo.

At the moment this only activates the improved progress bar.

Use ``%load_ext nengo.ipynb`` in a Jupyter notebook to load the extension.

Note
----
This IPython extension cannot be unloaded.
"""

from html import escape
import warnings

import IPython

try:
    import notebook

    notebook_version = notebook.version_info
except ImportError:
    notebook_version = IPython.version_info

from nengo.rc import rc
from nengo.utils.ipython import has_ipynb_widgets
from nengo.utils.progress import ProgressBar, timestamp2timedelta

if has_ipynb_widgets():
    # pylint: disable=ungrouped-imports
    from IPython.display import display

    if IPython.version_info[0] <= 3:
        from IPython.html.widgets import DOMWidget
        import IPython.utils.traitlets as traitlets
    else:
        import ipywidgets
        from ipywidgets import DOMWidget
        import traitlets
else:
    raise ImportError(
        "Required dependency could not be loaded. Please install ipywidgets."
    )


def load_ipython_extension(ipython):
    if IPython.version_info[0] >= 5:
        warnings.warn(
            "Loading the nengo.ipynb notebook extension is no longer "
            "required. Progress bars are automatically activated for IPython "
            "version 5 and later."
        )
    elif has_ipynb_widgets() and rc.get("progress", "progress_bar") == "auto":
        warnings.warn(
            "The nengo.ipynb notebook extension is deprecated. Please upgrade "
            "to IPython version 5 or later."
        )

        IPythonProgressWidget.load_frontend(ipython)
        rc.set(
            "progress",
            "progress_bar",
            ".".join((__name__, IPython2ProgressBar.__name__)),
        )


class IPythonProgressWidget(DOMWidget):
    """IPython widget for displaying a progress bar."""

    # pylint: disable=too-many-public-methods
    _view_name = traitlets.Unicode("NengoProgressBar", sync=True)
    if notebook_version[0] >= 4:
        _view_module = traitlets.Unicode("nengo", sync=True)
    progress = traitlets.Float(0.0, sync=True)
    text = traitlets.Unicode("", sync=True)

    WIDGET = """
      var NengoProgressBar = widgets.DOMWidgetView.extend({
        render: function() {
          // Work-around for messed up CSS in IPython 4
          $('.widget-subarea').css({flex: '2 1 0%'});
          // $el is the DOM of the widget
          this.$el.css({width: '100%', marginBottom: '0.5em'});
          this.$el.html([
            '<div style="',
                'width: 100%;',
                'border: 1px solid #cfcfcf;',
                'border-radius: 4px;',
                'text-align: center;',
                'position: relative;">',
              '<div class="pb-text" style="',
                  'position: absolute;',
                  'width: 100%;">',
                '0%',
              '</div>',
              '<div class="pb-bar" style="',
                  'background-color: #bdd2e6;',
                  'width: 0%;',
                  'transition: width 0.1s linear;">',
                '&nbsp;',
              '</div>',
            '</div>'].join(''));
        },

        update: function() {
          this.$el.css({width: '100%', marginBottom: '0.5em'});
          var progress = 100 * this.model.get('progress');
          var text = this.model.get('text');
          this.$el.find('div.pb-bar').width(progress.toString() + '%');
          this.$el.find('div.pb-text').html(text);
        },
      });
    """

    FRONTEND = """
    define('nengo', ["jupyter-js-widgets"], function(widgets) {{
        {widget}

      return {{
        NengoProgressBar: NengoProgressBar
      }};
    }});""".format(
        widget=WIDGET
    )

    LEGACY_FRONTEND = """
    require(["widgets/js/widget", "widgets/js/manager"],
        function(widgets, manager) {{
      if (typeof widgets.DOMWidgetView == 'undefined') {{
        widgets = IPython;
      }}
      if (typeof manager.WidgetManager == 'undefined') {{
        manager = IPython;
      }}

      {widget}

      manager.WidgetManager.register_widget_view(
        'NengoProgressBar', NengoProgressBar);
    }});""".format(
        widget=WIDGET
    )

    LEGACY_4_FRONTEND = """
    define('nengo', ["widgets/js/widget"], function(widgets) {{
        {widget}

      return {{
        NengoProgressBar: NengoProgressBar
      }};
    }});""".format(
        widget=WIDGET
    )

    @classmethod
    def load_frontend(cls, ipython):
        """Loads the JavaScript front-end code required by then widget."""
        warnings.warn(
            "IPythonProgressWidget is deprecated. Please upgrade to "
            "IPython version 5 or later.",
            DeprecationWarning,
        )
        if notebook_version[0] < 4:
            ipython.run_cell_magic("javascript", "", cls.LEGACY_FRONTEND)
        elif ipywidgets.version_info[0] < 5:
            nb_ver_4x = notebook_version[0] == 4 and notebook_version[1] > 1
            if notebook_version[0] > 4 or nb_ver_4x:
                warnings.warn(
                    "Incompatible versions of notebook and ipywidgets "
                    "detected. Please update your ipywidgets package to "
                    "version 5 or above."
                )
            ipython.run_cell_magic("javascript", "", cls.LEGACY_4_FRONTEND)
        else:
            ipython.run_cell_magic("javascript", "", cls.FRONTEND)


class IPython2ProgressBar(ProgressBar):
    """IPython progress bar based on widgets."""

    def __init__(self, task):
        warnings.warn(
            "IPython2ProgressBar is deprecated. Please upgrade to IPython "
            "version 5 or later.",
            DeprecationWarning,
        )
        super().__init__(task)
        self._escaped_task = escape(task)
        self._widget = IPythonProgressWidget()
        self._initialized = False

    def update(self, progress):
        if not self._initialized:
            display(self._widget)
            self._initialized = True

        self._widget.progress = progress.progress
        if progress.finished:
            self._widget.text = "{} finished in {}.".format(
                self._escaped_task, timestamp2timedelta(progress.elapsed_seconds())
            )
        else:
            self._widget.text = "{task}&hellip; {progress:.0f}%, ETA: {eta}".format(
                task=self._escaped_task,
                progress=100 * progress.progress,
                eta=timestamp2timedelta(progress.eta()),
            )
