#-----------------------------------------------------------------------------
# Copyright (c) Anaconda, Inc., and Bokeh Contributors.
# All rights reserved.
#
# The full license is in the file LICENSE.txt, distributed with this software.
#-----------------------------------------------------------------------------
''' Various kinds of input widgets and form controls.
'''
#-----------------------------------------------------------------------------
# Boilerplate
#-----------------------------------------------------------------------------
from __future__ import annotations
import logging # isort:skip
log = logging.getLogger(__name__)
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
# Standard library imports
from math import inf
from typing import Any as any
# Bokeh imports
from ...core.has_props import abstract
from ...core.properties import (
    Any,
    Auto,
    Bool,
    Color,
    ColorHex,
    Dict,
    Either,
    Enum,
    Float,
    Instance,
    Int,
    Interval,
    List,
    NonNegative,
    Null,
    Nullable,
    Override,
    Positive,
    Readonly,
    Required,
    Seq,
    String,
    Tuple,
)
from ...events import ModelEvent
from ...util.deprecation import deprecated
from ..dom import HTML
from ..formatters import TickFormatter
from ..ui import Tooltip
from .widget import Widget
#-----------------------------------------------------------------------------
# Globals and constants
#-----------------------------------------------------------------------------
__all__ = (
    'AutocompleteInput',
    'Checkbox',
    'PaletteSelect',
    'ColorPicker',
    'FileInput',
    'InputWidget',
    'MultiChoice',
    'MultiSelect',
    'NumericInput',
    'PasswordInput',
    'Select',
    'Spinner',
    'Switch',
    'TextAreaInput',
    'TextInput',
    'ColorMap',
)
#-----------------------------------------------------------------------------
# Dev API
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# General API
#-----------------------------------------------------------------------------
# TODO mark this as a one way event from server to client
class ClearInput(ModelEvent):
    """
    Notifies the input widget that its input/value needs to be cleared.
    This is specially useful for widgets whose value can't be simply cleared by
    assigning to ``value`` (or equivalent) property.
    """
    event_name = "clear_input"
    def __init__(self, model: InputWidget) -> None:
        if not isinstance(model, InputWidget):
            raise ValueError(f"{self.__class__.__name__} event only applies to input models, i.e. instances of bokeh.models.widgets.InputWidget")
        super().__init__(model=model)
[docs]
class Spinner(NumericInput):
    ''' Numeric Spinner input widget.
    '''
    # explicit __init__ to support Init signatures
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    value_throttled = Readonly(Either(Null, Float, Int), help="""
    Value reported at the end of interactions.
    """)
    mode = Override(default="float")
    step = Interval(Float, start=1e-16, end=inf, default=1, help="""
    The step added or subtracted to the current value.
    """)
    page_step_multiplier = Interval(Float, start=0, end=inf, default=10, help="""
    Defines the multiplication factor applied to step when the page up and page
    down keys are pressed.
    """)
    wheel_wait = Either(Int, Float, default=100, help="""
    Defines the debounce time in ms before updating `value_throttled` when the
    mouse wheel is used to change the input.
    """) 
@abstract
class ToggleInput(Widget):
    """ Base class for toggleable (boolean) input widgets. """
    # explicit __init__ to support Init signatures
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    active = Bool(default=False, help="""
    The state of the widget.
    """)
[docs]
class Checkbox(ToggleInput):
    """ A checkbox widget. """
    # explicit __init__ to support Init signatures
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    label = String(default="", help="""
    The label next to the checkbox.
    """) 
[docs]
class Switch(ToggleInput):
    """ A checkbox-like widget. """
    # explicit __init__ to support Init signatures
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    width = Override(default=32) 
class TextLikeInput(InputWidget):
    ''' Base class for text-like input widgets.
    '''
    # explicit __init__ to support Init signatures
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    value = String(default="", help="""
    Initial or entered text value.
    Change events are triggered whenever <enter> is pressed.
    """)
    value_input = String(default="", help="""
    Initial or current value.
    Change events are triggered whenever any update happens, i.e. on every
    keypress.
    """)
    placeholder = String(default="", help="""
    Placeholder for empty input field.
    """)
    max_length = Nullable(Int, help="""
    Max count of characters in field
    """)
[docs]
class TextInput(TextLikeInput):
    ''' Single-line input widget.
    '''
    # explicit __init__ to support Init signatures
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    prefix = Nullable(String, help="""
    An optional string prefix to display before the input. This is useful to
    indicate e.g. a variable the entered value will be assigned to.
    """)
    suffix = Nullable(String, help="""
    An optional string suffix to display after the input. This is useful to
    indicate e.g. the units of measurement of the entered value.
    """) 
[docs]
class TextAreaInput(TextLikeInput):
    ''' Multi-line input widget.
    '''
    # explicit __init__ to support Init signatures
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    cols = Int(default=20, help="""
    Specifies the width of the text area (in average character width). Default: 20
    """)
    rows = Int(default=2, help="""
    Specifies the height of the text area (in lines). Default: 2
    """)
    max_length = Override(default=500) 
Options = List(Either(String, Tuple(Any, String)))
OptionsGroups = Dict(String, Options)
NotSelected = "" # TODO symbol
[docs]
class Select(InputWidget):
    ''' Single-select widget.
    '''
    # explicit __init__ to support Init signatures
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    options = Either(Options, OptionsGroups, help="""
    Available selection options.
    Options may be provided either as a list of possible string values, which
    also act as options' labels, or as a list of tuples, each of the form
    ``(value, label)``, where ``value`` can be of any type, not necessarily
    a string. In the latter case, the visible widget text for each value will
    be corresponding given label.
    Option groupings can be provided by supplying a dictionary object whose
    values are in the aforementioned list format.
    """).accepts(List(Either(Null, String)), lambda v: [ NotSelected if item is None else item for item in v ])
    value = Any(default=NotSelected, help="""
    Initial or selected value.
    """).accepts(Null, lambda _: NotSelected) 
[docs]
class MultiSelect(InputWidget):
    ''' Multi-select widget.
    '''
    # explicit __init__ to support Init signatures
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    options = List(Either(String, Tuple(String, String)), help="""
    Available selection options. Options may be provided either as a list of
    possible string values, or as a list of tuples, each of the form
    ``(value, label)``. In the latter case, the visible widget text for each
    value will be corresponding given label.
    """)
    value = List(String, help="""
    Initial or selected values.
    """)
    size = Int(default=4, help="""
    The number of visible options in the dropdown list. (This uses the
    ``select`` HTML element's ``size`` attribute. Some browsers might not
    show less than 3 options.)
    """) 
[docs]
class MultiChoice(InputWidget):
    ''' MultiChoice widget.
    '''
    # explicit __init__ to support Init signatures
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    options = List(Either(String, Tuple(String, String)), help="""
    Available selection options. Options may be provided either as a list of
    possible string values, or as a list of tuples, each of the form
    ``(value, label)``. In the latter case, the visible widget text for each
    value will be corresponding given label.
    """)
    value = List(String, help="""
    Initial or selected values.
    """)
    delete_button = Bool(default=True, help="""
    Whether to add a button to remove a selected option.
    """)
    max_items = Nullable(Int, help="""
    The maximum number of items that can be selected.
    """)
    option_limit = Nullable(Int, help="""
    The number of choices that will be rendered in the dropdown.
    """)
    search_option_limit = Nullable(Int, help="""
    The number of choices that will be rendered in the dropdown
    when search string is entered.
    """)
    placeholder = Nullable(String, help="""
    A string that is displayed if not item is added.
    """)
    solid = Bool(default=True, help="""
    Specify whether the choices should be solidly filled.""") 
[docs]
class ColorPicker(InputWidget):
    ''' Color picker widget.
    '''
    # explicit __init__ to support Init signatures
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    color = ColorHex(default='#000000', help="""
    The initial color of the picked color (named or hexadecimal)
    """) 
[docs]
class PaletteSelect(InputWidget):
    ''' Color palette select widget.
    '''
    # explicit __init__ to support Init signatures
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    value = Required(String, help="""
    The name of the initial or selected color palette.
    """)
    items = Required(Seq(Tuple(String, Seq(Color))), help="""
    A selection of named color palettes to choose from.
    """)
    swatch_width = NonNegative(Int, default=100, help="""
    The width of the UI element showing the preview of a palette, in pixels.
    """)
    swatch_height = Either(Auto, NonNegative(Int), default="auto", help="""
    The height of the UI element showing the preview of a palette, either in
    pixels or automatically adjusted.
    """)
    ncols = Positive(Int, default=1, help="""
    The number of columns to split the display of the palettes into.
    """) 
[docs]
def ColorMap(*args: any, **kwargs: any) -> PaletteSelect:
    ''' Color palette select widget.
    .. deprecated:: 3.4.0
        Use ``PaletteSelect`` widget instead.
    '''
    deprecated((3, 4, 0), "ColorMap widget", "PaletteSelect widget")
    return PaletteSelect(*args, **kwargs) 
#-----------------------------------------------------------------------------
# Private API
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Code
#-----------------------------------------------------------------------------