"""
Components/Snackbar
===================

.. seealso::

    `Material Design spec, Snackbars <https://material.io/components/snackbars>`_

.. rubric:: Snackbars provide brief messages about app processes at the bottom
    of the screen.

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/snackbar.png
    :align: center

Usage
-----

.. code-block:: python

    from kivy.lang import Builder

    from kivymd.app import MDApp

    KV = '''
    #:import Snackbar kivymd.uix.snackbar.Snackbar


    Screen:

        MDRaisedButton:
            text: "Create simple snackbar"
            on_release: Snackbar(text="This is a snackbar!").open()
            pos_hint: {"center_x": .5, "center_y": .5}
    '''


    class Test(MDApp):
        def build(self):
            return Builder.load_string(KV)


    Test().run()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/snackbar-simple.gif
    :align: center

Usage with snackbar_x, snackbar_y
---------------------------------

.. code-block:: python

    Snackbar(
        text="This is a snackbar!",
        snackbar_x="10dp",
        snackbar_y="10dp",
        size_hint_x=(
            Window.width - (dp(10) * 2)
        ) / Window.width
    ).open()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/snackbar-padding.gif
    :align: center

Control width
-------------

.. code-block:: python

    Snackbar(
        text="This is a snackbar!",
        snackbar_x="10dp",
        snackbar_y="10dp",
        size_hint_x=.5
    ).open()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/snackbar-percent-width.png
    :align: center

Custom text color
-----------------

.. code-block:: python

    Snackbar(
        text="[color=#ddbb34]This is a snackbar![/color]",
        snackbar_y="10dp",
        snackbar_y="10dp",
        size_hint_x=.7
    ).open()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/snackbar-custom-color.png
    :align: center

Usage with button
-----------------

.. code-block:: python

    snackbar = Snackbar(
        text="This is a snackbar!",
        snackbar_x="10dp",
        snackbar_y="10dp",
    )
    snackbar.size_hint_x = (
        Window.width - (snackbar.snackbar_x * 2)
    ) / Window.width
    snackbar.buttons = [
        MDFlatButton(
            text="UPDATE",
            text_color=(1, 1, 1, 1),
            on_release=snackbar.dismiss,
        ),
        MDFlatButton(
            text="CANCEL",
            text_color=(1, 1, 1, 1),
            on_release=snackbar.dismiss,
        ),
    ]
    snackbar.open()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/snackbar-button.png
    :align: center

Using a button with custom color
--------------------------------

.. code-block:: python

    Snackbar(
        ...
        bg_color=(0, 0, 1, 1),
    ).open()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/snackbar-button-custom-color.png
    :align: center

Custom usage
------------

.. code-block:: python

    from kivy.lang import Builder
    from kivy.animation import Animation
    from kivy.clock import Clock
    from kivy.metrics import dp

    from kivymd.app import MDApp
    from kivymd.uix.snackbar import Snackbar


    KV = '''
    Screen:

        MDFloatingActionButton:
            id: button
            x: root.width - self.width - dp(10)
            y: dp(10)
            on_release: app.snackbar_show()
    '''


    class Test(MDApp):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.screen = Builder.load_string(KV)
            self.snackbar = None
            self._interval = 0

        def build(self):
            return self.screen

        def wait_interval(self, interval):
            self._interval += interval
            if self._interval > self.snackbar.duration + 0.5:
                anim = Animation(y=dp(10), d=.2)
                anim.start(self.screen.ids.button)
                Clock.unschedule(self.wait_interval)
                self._interval = 0
                self.snackbar = None

        def snackbar_show(self):
            if not self.snackbar:
                self.snackbar = Snackbar(text="This is a snackbar!")
                self.snackbar.open()
                anim = Animation(y=dp(72), d=.2)
                anim.bind(on_complete=lambda *args: Clock.schedule_interval(
                    self.wait_interval, 0))
                anim.start(self.screen.ids.button)


    Test().run()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/snackbar-custom-usage.gif
    :align: center

Custom Snackbar
---------------

.. code-block:: python

    from kivy.lang import Builder
    from kivy.core.window import Window
    from kivy.properties import StringProperty, NumericProperty

    from kivymd.app import MDApp
    from kivymd.uix.button import MDFlatButton
    from kivymd.uix.snackbar import BaseSnackbar

    KV = '''
    <CustomSnackbar>

        MDIconButton:
            pos_hint: {'center_y': .5}
            icon: root.icon
            opposite_colors: True

        MDLabel:
            id: text_bar
            size_hint_y: None
            height: self.texture_size[1]
            text: root.text
            font_size: root.font_size
            theme_text_color: 'Custom'
            text_color: 'ffffff'
            shorten: True
            shorten_from: 'right'
            pos_hint: {'center_y': .5}


    Screen:

        MDRaisedButton:
            text: "SHOW"
            pos_hint: {"center_x": .5, "center_y": .45}
            on_press: app.show()
    '''


    class CustomSnackbar(BaseSnackbar):
        text = StringProperty(None)
        icon = StringProperty(None)
        font_size = NumericProperty("15sp")


    class Test(MDApp):
        def build(self):
            return Builder.load_string(KV)

        def show(self):
            snackbar = CustomSnackbar(
                text="This is a snackbar!",
                icon="information",
                snackbar_x="10dp",
                snackbar_y="10dp",
                buttons=[MDFlatButton(text="ACTION", text_color=(1, 1, 1, 1))]
            )
            snackbar.size_hint_x = (
                Window.width - (snackbar.snackbar_x * 2)
            ) / Window.width
            snackbar.open()


    Test().run()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/snackbar-custom.png
    :align: center
"""

__all__ = ("Snackbar", "BaseSnackbar")

import os

from kivy.animation import Animation
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.properties import (
    BooleanProperty,
    ColorProperty,
    ListProperty,
    NumericProperty,
    OptionProperty,
    StringProperty,
)

from kivymd import uix_path
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.button import BaseButton
from kivymd.uix.card import MDCard

with open(
    os.path.join(uix_path, "snackbar", "snackbar.kv"), encoding="utf-8"
) as kv_file:
    Builder.load_string(kv_file.read())


class BaseSnackbar(MDCard, FakeRectangularElevationBehavior):
    """
    :Events:
        :attr:`on_open`
            Called when a dialog is opened.
        :attr:`on_dismiss`
            When the front layer rises.

    Abstract base class for all Snackbars.
    This class handles sizing, positioning, shape and events for Snackbars

    All Snackbars will be made off of this `BaseSnackbar`.

    `BaseSnackbar` will always try to fill the remainder of the screen with
    your Snackbar.

    To make your Snackbar dynamic and symetric with snackbar_x.

    Set size_hint_x like below:

    .. code-block:: python

        size_hint_z = (
            Window.width - (snackbar_x * 2)
        ) / Window.width
    """

    duration = NumericProperty(3)
    """
    The amount of time that the snackbar will stay on screen for.

    :attr:`duration` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `3`.
    """

    auto_dismiss = BooleanProperty(True)
    """
    Whether to use automatic closing of the snackbar or not.

    :attr:`auto_dismiss` is a :class:`~kivy.properties.BooleanProperty`
    and defaults to `'True'`.
    """

    bg_color = ColorProperty(None)
    """
    Snackbar background.

    :attr:`bg_color` is a :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    buttons = ListProperty()
    """
    Snackbar buttons.

    :attr:`buttons` is a :class:`~kivy.properties.ListProperty`
    and defaults to `'[]'`
    """

    radius = ListProperty([5, 5, 5, 5])
    """
    Snackbar radius.

    :attr:`radius` is a :class:`~kivy.properties.ListProperty`
    and defaults to `'[5, 5, 5, 5]'`
    """

    snackbar_animation_dir = OptionProperty(
        "Bottom",
        options=["Top", "Bottom", "Left", "Right"],
    )
    """
    Snackbar animation direction.

    Available options are: `"Top"`, `"Bottom"`, `"Left"`, `"Right"`

    :attr:`snackbar_animation_dir` is an :class:`~kivy.properties.OptionProperty`
    and defaults to `'Bottom'`.
    """

    snackbar_x = NumericProperty("0dp")
    """
    The snackbar x position in the screen

    :attr:`snackbar_x` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `0dp`.
    """

    snackbar_y = NumericProperty("0dp")
    """
    The snackbar x position in the screen

    :attr:`snackbar_y` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `0dp`.
    """

    _interval = 0

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.register_event_type("on_open")
        self.register_event_type("on_dismiss")

    def dismiss(self, *args):
        """Dismiss the snackbar."""

        def dismiss(interval):
            if self.snackbar_animation_dir == "Top":
                anim = Animation(y=(Window.height + self.height), d=0.2)
            elif self.snackbar_animation_dir == "Left":
                anim = Animation(x=-self.width, d=0.2)
            elif self.snackbar_animation_dir == "Right":
                anim = Animation(x=Window.width, d=0.2)
            else:
                anim = Animation(y=-self.height, d=0.2)

            anim.bind(
                on_complete=lambda *args: Window.parent.remove_widget(self)
            )
            anim.start(self)

        Clock.schedule_once(dismiss, 0.5)
        self.dispatch("on_dismiss")

    def open(self):
        """Show the snackbar."""

        def wait_interval(interval):
            self._interval += interval
            if self._interval > self.duration:
                self.dismiss()
                Clock.unschedule(wait_interval)
                self._interval = 0

        for c in Window.parent.children:
            if isinstance(c, BaseSnackbar):
                return

        if self.snackbar_y > (Window.height - self.height):
            self.snackbar_y = Window.height - self.height

        self._calc_radius()

        if self.size_hint_x == 1:
            self.size_hint_x = (Window.width - self.snackbar_x) / Window.width

        if (
            self.snackbar_animation_dir == "Top"
            or self.snackbar_animation_dir == "Bottom"
        ):
            self.x = self.snackbar_x

            if self.snackbar_animation_dir == "Top":
                self.y = Window.height + self.height
            else:
                self.y = -self.height

            Window.parent.add_widget(self)

            if self.snackbar_animation_dir == "Top":
                anim = Animation(
                    y=self.snackbar_y
                    if self.snackbar_y != 0
                    else Window.height - self.height,
                    d=0.2,
                )
            else:
                anim = Animation(
                    y=self.snackbar_y if self.snackbar_y != 0 else 0, d=0.2
                )

        elif (
            self.snackbar_animation_dir == "Left"
            or self.snackbar_animation_dir == "Right"
        ):
            self.y = self.snackbar_y

            if self.snackbar_animation_dir == "Left":
                self.x = -Window.width
            else:
                self.x = Window.width

            Window.parent.add_widget(self)
            anim = Animation(
                x=self.snackbar_x if self.snackbar_x != 0 else 0, d=0.2
            )

        if self.auto_dismiss:
            anim.bind(
                on_complete=lambda *args: Clock.schedule_interval(
                    wait_interval, 0
                )
            )
        anim.start(self)
        self.dispatch("on_open")

    def on_open(self, *args):
        """Called when a dialog is opened."""

    def on_dismiss(self, *args):
        """Called when the dialog is closed."""

    def on_buttons(self, instance, value):
        def on_buttons(interval):
            for button in value:
                if issubclass(button.__class__, (BaseButton,)):
                    self.add_widget(button)
                else:
                    raise ValueError(
                        f"The {button} object must be inherited from the base class <BaseButton>"
                    )

        Clock.schedule_once(on_buttons)

    def _calc_radius(self):
        if (
            self.snackbar_animation_dir == "Top"
            or self.snackbar_animation_dir == "Bottom"
        ):

            if self.snackbar_y == 0 and self.snackbar_x == 0:

                if self.size_hint_x == 1:
                    self.radius = [0, 0, 0, 0]
                else:
                    if self.snackbar_animation_dir == "Top":
                        self.radius = [0, 0, self.radius[2], 0]
                    else:
                        self.radius = [0, self.radius[1], 0, 0]

            elif self.snackbar_y != 0 and self.snackbar_x == 0:

                if self.size_hint_x == 1:
                    self.radius = [0, 0, 0, 0]
                else:
                    if self.snackbar_y >= Window.height - self.height:
                        self.radius = [0, 0, self.radius[2], 0]
                    else:
                        self.radius = [0, self.radius[1], self.radius[2], 0]

            elif self.snackbar_y == 0 and self.snackbar_x != 0:

                if self.size_hint_x == 1:
                    if self.snackbar_animation_dir == "Top":
                        self.radius = [0, 0, 0, self.radius[3]]
                    else:
                        self.radius = [self.radius[0], 0, 0, 0]
                else:
                    if self.snackbar_animation_dir == "Top":
                        self.radius = [0, 0, self.radius[2], self.radius[3]]
                    else:
                        self.radius = [self.radius[0], self.radius[1], 0, 0]

            else:  # self.snackbar_y != 0 and self.snackbar_x != 0

                if self.size_hint_x == 1:
                    self.radius = [self.radius[0], 0, 0, self.radius[3]]
                elif self.snackbar_y >= Window.height - self.height:
                    self.radius = [0, 0, self.radius[2], self.radius[3]]

        elif (
            self.snackbar_animation_dir == "Left"
            or self.snackbar_animation_dir == "Right"
        ):

            if self.snackbar_y == 0 and self.snackbar_x == 0:

                if self.size_hint_x == 1:
                    self.radius = [0, 0, 0, 0]
                else:
                    self.radius = [0, self.radius[1], 0, 0]

            elif self.snackbar_y != 0 and self.snackbar_x == 0:

                if self.size_hint_x == 1:
                    self.radius = [0, 0, 0, 0]
                else:
                    self.radius = [0, self.radius[1], self.radius[2], 0]

            elif self.snackbar_y == 0 and self.snackbar_x != 0:

                if self.size_hint_x == 1:
                    self.radius = [self.radius[0], 0, 0, 0]
                else:
                    self.radius = [self.radius[0], self.radius[1], 0, 0]

            else:  # self.snackbar_y != 0 and self.snackbar_x != 0

                if self.size_hint_x == 1:
                    if self.snackbar_y >= Window.height - self.height:
                        self.radius = [0, 0, 0, self.radius[3]]
                    else:
                        self.radius = [self.radius[0], 0, 0, self.radius[3]]
                elif self.snackbar_y >= Window.height - self.height:
                    self.radius = [0, 0, self.radius[2], self.radius[3]]


class Snackbar(BaseSnackbar):
    """
    Snackbar inherits all its functionality from `BaseSnackbar`
    """

    text = StringProperty()
    """
    The text that will appear in the snackbar.

    :attr:`text` is a :class:`~kivy.properties.StringProperty`
    and defaults to `''`.
    """

    font_size = NumericProperty("15sp")
    """
    The font size of the text that will appear in the snackbar.

    :attr:`font_size` is a :class:`~kivy.properties.NumericProperty` and
    defaults to `'15sp'`.
    """
