"""
Effects/FadingEdgeEffect
========================

.. versionadded:: 1.0.0

The `FadingEdgeEffect` class implements a fade effect for `KivyMD` widgets:

.. code-block:: python

    from kivy.lang import Builder
    from kivy.uix.scrollview import ScrollView

    from kivymd.app import MDApp
    from kivymd.effects.fadingedge.fadingedge import FadingEdgeEffect
    from kivymd.uix.list import OneLineListItem

    KV = '''
    MDScreen:

        FadeScrollView:
            fade_height: self.height / 2
            fade_color: root.md_bg_color

            MDList:
                id: container
    '''


    class FadeScrollView(FadingEdgeEffect, ScrollView):
        pass


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

        def on_start(self):
            for i in range(20):
                self.root.ids.container.add_widget(
                    OneLineListItem(text=f"Single-line item {i}")
                )


    Test().run()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/fading-edge-effect-white.gif
    :align: center

.. note:: Use the same color value for the fade_color parameter as for the
    parent widget.
"""

from typing import Union

from kivy.clock import Clock
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from kivy.metrics import dp
from kivy.properties import BooleanProperty, ColorProperty, NumericProperty

from kivymd.theming import ThemableBehavior

__all_ = ("FadingEdgeEffect",)


class FadingEdgeEffect(ThemableBehavior):
    """
    The class implements the fade effect.

    .. versionadded:: 1.0.0
    """

    fade_color = ColorProperty(None)
    """
    Fade color.

    :attr:`fade_color` is an :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    fade_height = NumericProperty(0)
    """
    Fade height.

    :attr:`fade_height` is an :class:`~kivy.properties.ColorProperty`
    and defaults to `0`.
    """

    edge_top = BooleanProperty(True)
    """
    Display fade edge top.

    :attr:`edge_top` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `True`.
    """

    edge_bottom = BooleanProperty(True)
    """
    Display fade edge bottom.

    :attr:`edge_bottom` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `True`.
    """

    _height_segment = 10

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Clock.schedule_once(self.set_fade)

    # TODO: Perhaps it would be better if we used a Shader for the fade effect.
    #  But, I think the canvas instructions shouldn't affect performance
    def set_fade(self, interval: Union[int, float]) -> None:
        """Draws a bottom and top fade border on the canvas."""

        fade_color = (
            self.theme_cls.primary_color
            if not self.fade_color
            else self.fade_color
        )
        height_segment = (
            self.fade_height if self.fade_height else dp(100)
        ) // self._height_segment
        alpha = 1.1

        with self.canvas:
            for i in range(self._height_segment):
                alpha -= 0.1

                Color(rgba=(fade_color[:-1] + [round(alpha, 1)]))
                rectangle_top = (
                    Rectangle(
                        pos=(self.x, self.height - (i * height_segment)),
                        size=(self.width, height_segment),
                    )
                    if self.edge_top
                    else None
                )
                rectangle_bottom = (
                    Rectangle(
                        pos=(self.x, i * height_segment),
                        size=(self.width, height_segment),
                    )
                    if self.edge_bottom
                    else None
                )
                # How I hate lambda functions because of their length :(
                # But I don’t want to call the arguments by short,
                # incomprehensible names 'a', 'b', 'c'.
                self.bind(
                    pos=lambda instance_fadind_edge_effect, window_size, rectangle_top=rectangle_top, rectangle_bottom=rectangle_bottom, index=i: self.update_canvas(
                        instance_fadind_edge_effect,
                        window_size,
                        rectangle_top,
                        rectangle_bottom,
                        index,
                    ),
                    size=lambda instance_fadind_edge_effect, window_size, rectangle_top=rectangle_top, rectangle_bottom=rectangle_bottom, index=i: self.update_canvas(
                        instance_fadind_edge_effect,
                        window_size,
                        rectangle_top,
                        rectangle_bottom,
                        index,
                    ),
                )

    def update_canvas(
        self,
        instance_fadind_edge_effect,
        size: list[int, int],
        rectangle_top: Rectangle,
        rectangle_bottom: Rectangle,
        index: int,
    ) -> None:
        """
        Updates the position and size of the fade border on the canvas.
        Called when the application screen is resized.
        """

        height_segment = (
            self.fade_height if self.fade_height else dp(100)
        ) // self._height_segment

        if rectangle_top:
            rectangle_top.pos = (
                instance_fadind_edge_effect.x,
                size[1]
                - (index * height_segment - instance_fadind_edge_effect.y),
            )
            rectangle_top.size = (size[0], height_segment)
        if rectangle_bottom:
            rectangle_bottom.pos = (
                instance_fadind_edge_effect.x,
                index * height_segment + instance_fadind_edge_effect.y,
            )
            rectangle_bottom.size = (size[0], height_segment)