# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/01_widgets.ipynb (unless otherwise specified).

__all__ = ['logger', 'CallbackDispatcher', 'Widget', 'Output', 'IntSlider', 'FloatSlider', 'IntProgress',
           'BoundedIntText', 'BoundedFloatText', 'IntText', 'FloatText', 'ToggleButtons', 'Checkbox', 'Dropdown',
           'RadioButtons', 'Select', 'Text', 'Textarea', 'Label', 'HTML', 'HTMLMath', 'Image', 'Button', 'ColorPicker',
           'GridBox', 'AlignedLabel', 'IntLabel', 'Grid']

# Cell
from fastcore.test import test_eq
from collections import namedtuple
import traitlets
from traitlets import Type, Unicode, observe, validate, Any, HasTraits, directional_link, link, Tuple, List, Int
from IPython.display import display, HTML, Javascript
import ipywidgets as ipyw
from IPython import get_ipython

# Cell
from .items import ItemList, ItemArray, Item
from .config import CONFIG
from .log import getLogger
logger = getLogger(__name__)

# Cell
class CallbackDispatcher(ipyw.CallbackDispatcher):
    """A structure for registering and running callbacks. We only need to use this
    child version of CallbackDispatcher when using a button, which uses it to
    register and call "on_click" callbacks.
    We replace the ipywidget version of __call__ because we don't want to prompt
    a traceback on an error. Instead we throw up an alert to inform the user there
    has been an error. """

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.logger = getLogger(__name__)

    def __call__(self, *args, **kwargs):
        """Call all of the registered callbacks."""
        value = None
        for callback in self.callbacks:
            try:
                local_value = callback(*args, **kwargs)
            except Exception as e:
                if CONFIG['ERRORS'].getboolean('catch_all'):
                    display(Javascript('alert("' + type(e).__name__ + ': ' + str(e) + '\\n\\nIf a problem occurs, restart the app. For farther information, check the log accordion in the settings tab.")'))
                else:
                    ip = get_ipython()
                    ip.showtraceback()
                self.logger.warning("Exception in callback %s: %s", callback, e, exc_info=True)
            else:
                value = local_value if local_value is not None else value
        return value

class Widget(ipyw.Widget, Item):

    def __init__(self, name=None, **kwargs):
        if name:
            self.name = name
            self.add_class(name)
        super().__init__(**kwargs)

    def notify_change(self, change):
        """ This function is just like ipyw.Widget.notify_change, but instead of calling
        super().notify_change in the last line, we run self._notify_observers explicitely in a try catch.
        https://github.com/jupyter-widgets/ipywidgets/blob/51322341d4f6d88449f9dbf6d538c60de6d23543/ipywidgets/widgets/widget.py#L550 """
        # Send the state to the frontend before the user-registered callbacks
        # are called.
        name = change['name']
        if self.comm is not None and self.comm.kernel is not None:
            # Make sure this isn't information that the front-end just sent us.
            if name in self.keys and self._should_send_property(name, getattr(self, name)):
                # Send new state to front-end
                self.send_state(key=name)

        try:
            '''https://github.com/ipython/traitlets/blob/b78a7d488b8241d30a5fc0c1074568c0de68a511/traitlets/traitlets.py#L1225'''
            return self._notify_observers(change)
        except Exception as e:
            logger.error(type(e).__name__ + ': ' + str(e))
            if CONFIG['ERRORS'].getboolean('catch_all'):
                display(Javascript('alert("' + type(e).__name__ + ': ' + str(e) + '\\n\\nIf a problem occurs, restart the app. For farther information, check the log accordion in the settings tab.")'))
            else:
                ip = get_ipython()
                ip.showtraceback()

    def visible(self):
        self.layout.visibility = 'visible'

    def invisible(self):
        self.layout.visibility = 'hidden'

    def show(self):
        if hasattr(self, '_display'):
            self.layout.display = self._display

    def hide(self):
        self._display = self.layout.display
        self.layout.display = 'none'

# Cell
class Output(Widget, ipyw.Output):
    pass

class IntSlider(Widget, ipyw.IntSlider):
    pass

class FloatSlider(Widget, ipyw.FloatSlider):
    pass

class IntProgress(Widget, ipyw.IntProgress):
    pass

class BoundedIntText(Widget, ipyw.BoundedIntText):
    pass

class BoundedFloatText(Widget, ipyw.BoundedFloatText):
    pass

class IntText(Widget, ipyw.IntText):
    pass

class FloatText(Widget, ipyw.FloatText):
    pass

class ToggleButtons(Widget, ipyw.ToggleButtons):
    pass

class Checkbox(Widget, ipyw.Checkbox):
    pass

class Dropdown(Widget, ipyw.Dropdown):
    pass

class RadioButtons(Widget, ipyw.RadioButtons):
    pass

class Select(Widget, ipyw.Select):
    pass

class Text(Widget, ipyw.Text):
    pass

class Textarea(Widget, ipyw.Textarea):
    pass

class Label(Widget, ipyw.Label):
    pass

class HTML(Widget, ipyw.HTML):
    pass

class HTMLMath(Widget, ipyw.HTMLMath):
    pass

class Image(Widget, ipyw.Image):
    pass

class Button(Widget, ipyw.Button):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self._click_handlers = CallbackDispatcher()

class ColorPicker(Widget, ipyw.ColorPicker):
    pass

class GridBox(Widget, ipyw.GridBox):
    pass

# Cell
class AlignedLabel(Widget, ipyw.VBox):

    value = Unicode()

    def __init__(self, value=None, alignment='center', **kwargs):
        super().__init__(**kwargs)
        if value:
            self.value = value
        if alignment == 'left':
            justify_content = 'flex-start'
        elif alignment == 'right':
            justify_content = 'flex-end'
        else:
            justify_content = 'center'

        # create and add label
        self.label = Label(str(value), layout=ipyw.Layout(display='flex', justify_content=justify_content))
        self.children = (self.label, )

        self.link_to_label()

    def link_to_label(self):
        traitlets.link((self, 'value'), (self.label, 'value'))

# Cell
class IntLabel(AlignedLabel):

    value = Int(0)
    string = Unicode('0')

    @observe('value')
    def _observe_value(self, change):
        self.string = str(change['new'])
        return change['new']

    def link_to_label(self):
        traitlets.link((self, 'string'), (self.label, 'value'))

# Cell
class Grid(ipyw.GridspecLayout, Widget):

    def __init__(self, n_rows, n_columns, **kwargs):
        super().__init__(n_rows, n_columns, **kwargs)
        self._collections = {}

    def __getitem__(self, key):
        if isinstance(key, str):
            return self._collections[key]
        else:
            return super().__getitem__(key)

    def add_widget_array(self, widget_array, i=0, j=0, name=None):
        if name:
            widget_list.name = name
        for l in range(widget_array.n_rows):
            for m in range(widget_array.n_columns):
                self[i+l, j+m] = widget_array[l, m]

    def add_widget_list(self, widget_list, i=0, j=0, direction='row', name=None):
        if name:
            widget_list.name = name
        if hasattr(widget_list, '_size'):
            size = widget_list._size
        else:
            size = len(widget_list)
        for l in range(size):
            if direction == 'col':
                self[i+l, j] = widget_list[l]
            elif direction == 'row':
                self[i, j+l] = widget_list[l]