"""
Behaviors/Elevation
===================

.. rubric:: Classes implements a circular and rectangular elevation effects.

To create a widget with rectangular or circular elevation effect,
you must create a new class that inherits from the
:class:`~RectangularElevationBehavior` or :class:`~CircularElevationBehavior`
class.

For example, let's create an button with a rectangular elevation effect:

.. code-block:: python

    from kivy.lang import Builder
    from kivy.uix.behaviors import ButtonBehavior

    from kivymd.app import MDApp
    from kivymd.uix.behaviors import (
        RectangularRippleBehavior,
        BackgroundColorBehavior,
        RectangularElevationBehavior,
    )

    KV = '''
    <RectangularElevationButton>:
        size_hint: None, None
        size: "250dp", "50dp"


    Screen:

        # With elevation effect
        RectangularElevationButton:
            pos_hint: {"center_x": .5, "center_y": .6}
            elevation: 11

        # Without elevation effect
        RectangularElevationButton:
            pos_hint: {"center_x": .5, "center_y": .4}
    '''


    class RectangularElevationButton(
        RectangularRippleBehavior,
        RectangularElevationBehavior,
        ButtonBehavior,
        BackgroundColorBehavior,
    ):
        md_bg_color = [0, 0, 1, 1]


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


    Example().run()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/rectangular-elevation-effect.gif
    :align: center

Similarly, create a button with a circular elevation effect:

.. code-block:: python

    from kivy.lang import Builder
    from kivy.uix.image import Image
    from kivy.uix.behaviors import ButtonBehavior

    from kivymd.app import MDApp
    from kivymd.uix.behaviors import (
        CircularRippleBehavior,
        CircularElevationBehavior,
    )

    KV = '''
    #:import images_path kivymd.images_path


    <CircularElevationButton>:
        size_hint: None, None
        size: "100dp", "100dp"
        source: f"{images_path}/kivymd_logo.png"


    Screen:

        # With elevation effect
        CircularElevationButton:
            pos_hint: {"center_x": .5, "center_y": .6}
            elevation: 5

        # Without elevation effect
        CircularElevationButton:
            pos_hint: {"center_x": .5, "center_y": .4}
            elevation: 0
    '''


    class CircularElevationButton(
        CircularRippleBehavior,
        CircularElevationBehavior,
        ButtonBehavior,
        Image,
    ):
        md_bg_color = [0, 0, 1, 1]


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


    Example().run()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/circular-elevation-effect.gif
    :align: center
"""

from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ListProperty, ObjectProperty, NumericProperty
from kivy.properties import AliasProperty
from kivy.metrics import dp

Builder.load_string(
    """
<RectangularElevationBehavior>
    canvas.before:
        Color:
            a: self._soft_shadow_a
        Rectangle:
            texture: self._soft_shadow_texture
            size: self._soft_shadow_size
            pos: self._soft_shadow_pos
        Color:
            a: self._hard_shadow_a
        Rectangle:
            texture: self._hard_shadow_texture
            size: self._hard_shadow_size
            pos: self._hard_shadow_pos
        Color:
            a: 1


<CircularElevationBehavior>
    canvas.before:
        Color:
            a: self._soft_shadow_a
        Rectangle:
            texture: self._soft_shadow_texture
            size: self._soft_shadow_size
            pos: self._soft_shadow_pos
        Color:
            a: self._hard_shadow_a
        Rectangle:
            texture: self._hard_shadow_texture
            size: self._hard_shadow_size
            pos: self._hard_shadow_pos
        Color:
            a: 1
"""
)


class CommonElevationBehavior(object):
    _elevation = NumericProperty(1)

    def _get_elevation(self):
        return self._elevation

    def _set_elevation(self, elevation):
        try:
            self._elevation = elevation
        except KeyError:
            self._elevation = 1

    elevation = AliasProperty(
        _get_elevation, _set_elevation, bind=("_elevation",)
    )
    """
    Elevation value.

    :attr:`elevation` is an :class:`~kivy.properties.AliasProperty` that
    returns the value for :attr:`elevation`.
    """

    _soft_shadow_texture = ObjectProperty()
    _soft_shadow_size = ListProperty([0, 0])
    _soft_shadow_pos = ListProperty([0, 0])
    _soft_shadow_a = NumericProperty(0)
    _hard_shadow_texture = ObjectProperty()
    _hard_shadow_size = ListProperty([0, 0])
    _hard_shadow_pos = ListProperty([0, 0])
    _hard_shadow_a = NumericProperty(0)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.bind(
            elevation=self._update_shadow,
            pos=self._update_shadow,
            size=self._update_shadow,
        )

    def _update_shadow(self, *args):
        raise NotImplemented


class RectangularElevationBehavior(CommonElevationBehavior):
    def _update_shadow(self, *args):
        if self.elevation > 0:
            ratio = self.width / (self.height if self.height != 0 else 1)
            if -2 < ratio < 2:
                self._shadow = App.get_running_app().theme_cls.quad_shadow
                width = soft_width = self.width * 1.9
                height = soft_height = self.height * 1.9
            elif ratio <= -2:
                self._shadow = App.get_running_app().theme_cls.rec_st_shadow
                ratio = abs(ratio)
                if ratio > 5:
                    ratio = ratio * 22
                else:
                    ratio = ratio * 11.5

                width = soft_width = self.width * 1.9
                height = self.height + dp(ratio)
                soft_height = self.height + dp(ratio) + dp(self.elevation) * 0.5
            else:
                self._shadow = App.get_running_app().theme_cls.quad_shadow
                width = soft_width = self.width * 1.8
                height = soft_height = self.height * 1.8

            x = self.center_x - width / 2
            soft_x = self.center_x - soft_width / 2
            self._soft_shadow_size = (soft_width, soft_height)
            self._hard_shadow_size = (width, height)

            y = (
                self.center_y
                - soft_height / 2
                - dp(0.1 * 1.5 ** self.elevation)
            )
            self._soft_shadow_pos = (soft_x, y)
            self._soft_shadow_a = 0.1 * 1.1 ** self.elevation
            self._soft_shadow_texture = self._shadow.textures[
                str(int(round(self.elevation - 1)))
            ]

            y = self.center_y - height / 2 - dp(0.5 * 1.18 ** self.elevation)
            self._hard_shadow_pos = (x, y)
            self._hard_shadow_a = 0.4 * 0.9 ** self.elevation
            self._hard_shadow_texture = self._shadow.textures[
                str(int(round(self.elevation)))
            ]

        else:
            self._soft_shadow_a = 0
            self._hard_shadow_a = 0


class CircularElevationBehavior(CommonElevationBehavior):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._shadow = App.get_running_app().theme_cls.round_shadow

    def _update_shadow(self, *args):
        if self.elevation > 0:
            width = self.width * 2
            height = self.height * 2

            x = self.center_x - width / 2
            self._soft_shadow_size = (width, height)

            self._hard_shadow_size = (width, height)

            y = self.center_y - height / 2 - dp(0.1 * 1.5 ** self.elevation)
            self._soft_shadow_pos = (x, y)
            self._soft_shadow_a = 0.1 * 1.1 ** self.elevation
            self._soft_shadow_texture = self._shadow.textures[
                str(int(round(self.elevation)))
            ]

            y = self.center_y - height / 2 - dp(0.5 * 1.18 ** self.elevation)
            self._hard_shadow_pos = (x, y)
            self._hard_shadow_a = 0.4 * 0.9 ** self.elevation
            self._hard_shadow_texture = self._shadow.textures[
                str(int(round(self.elevation - 1)))
            ]

        else:
            self._soft_shadow_a = 0
            self._hard_shadow_a = 0
