"""
Components/FitImage
===================

Feature to automatically crop a `Kivy` image to fit your layout
Write by Benedikt Zwölfer

Referene - https://gist.github.com/benni12er/95a45eb168fc33a4fcd2d545af692dad


Example:
========

.. tabs::

    .. tab:: Declarative KV styles

        .. code-block:: kv

            MDBoxLayout:
                size_hint_y: None
                height: "200dp"
                orientation: 'vertical'

                FitImage:
                    size_hint_y: 3
                    source: 'images/img1.jpg'

                FitImage:
                    size_hint_y: 1
                    source: 'images/img2.jpg'

    .. tab:: Declarative python styles

        .. code-block:: python

            MDBoxLayout(
                FitImage(
                    size_hint_y=.3,
                    source='images/img1.jpg',
                ),
                FitImage(
                    size_hint_y=.7,
                    source='images/img2.jpg',
                ),
                size_hint_y=None,
                height="200dp",
                orientation='vertical',
            )

Example with round corners:
===========================

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

.. tabs::

    .. tab:: Declarative KV styles

        .. code-block:: python

            from kivy.lang import Builder

            from kivymd.app import MDApp

            KV = '''
            MDScreen:

                MDCard:
                    radius: 36
                    md_bg_color: "grey"
                    pos_hint: {"center_x": .5, "center_y": .5}
                    size_hint: .4, .8

                    FitImage:
                        source: "bg.jpg"
                        size_hint_y: .35
                        pos_hint: {"top": 1}
                        radius: 36, 36, 0, 0
            '''


            class Example(MDApp):
                def build(self):
                    self.theme_cls.theme_style = "Dark"
                    return Builder.load_string(KV)


            Example().run()

    .. tab:: Declarative python styles

        .. code-block:: python

            from kivymd.app import MDApp
            from kivymd.uix.card import MDCard
            from kivymd.uix.fitimage import FitImage
            from kivymd.uix.screen import MDScreen


            class Example(MDApp):
                def build(self):
                    self.theme_cls.theme_style = "Dark"
                    return (
                        MDScreen(
                            MDCard(
                                FitImage(
                                    source="bg.jpg",
                                    size_hint_y=0.35,
                                    pos_hint={"top": 1},
                                    radius=(36, 36, 0, 0),
                                ),
                                radius=36,
                                md_bg_color="grey",
                                pos_hint={"center_x": .5, "center_y": .5},
                                size_hint=(0.4, 0.8),
                            ),
                        )
                    )


            Example().run()
"""

__all__ = ("FitImage",)

from kivy.clock import Clock
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from kivy.properties import BooleanProperty, ObjectProperty
from kivy.uix.image import AsyncImage
from kivy.uix.widget import Widget

from kivymd.uix.behaviors import StencilBehavior
from kivymd.uix.boxlayout import MDBoxLayout


class FitImage(MDBoxLayout, StencilBehavior):
    """
    Fit image class.

    For more information, see in the
    :class:`~kivymd.uix.boxlayout.MDLayout` and
    :class:`~kivymd.uix.behaviors.StencilBehavior` classes documentation.
    """

    source = ObjectProperty()
    """
    Filename/source of your image.

    :attr:`source` is a :class:`~kivy.properties.StringProperty`
    and defaults to None.
    """

    mipmap = BooleanProperty(False)
    """
    Indicate if you want OpenGL mipmapping to be applied to the texture.
    Read :ref:`mipmap` for more information.

    .. versionadded:: 1.0.0

    :attr:`mipmap` is a :class:`~kivy.properties.BooleanProperty`
    and defaults to `False`.
    """

    _container = ObjectProperty()

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

    def _late_init(self, *args):
        self._container = Container(self.source, self.mipmap)
        self.bind(source=self._container.setter("source"))
        self.add_widget(self._container)

    def reload(self):
        self._container.image.reload()


class Container(Widget):
    source = ObjectProperty()
    image = ObjectProperty()

    def __init__(self, source, mipmap, **kwargs):
        super().__init__(**kwargs)
        self.image = AsyncImage(mipmap=mipmap)
        self.loader_clock = Clock.schedule_interval(
            self.adjust_size, self.image.anim_delay
        )
        self.image.bind(
            on_load=lambda inst: (
                self.adjust_size(),
                self.loader_clock.cancel(),
            )
        )
        self.source = source
        self.bind(size=self.adjust_size, pos=self.adjust_size)

    def on_source(self, instance, value):
        if isinstance(value, str):
            self.image.source = value
        else:
            self.image.texture = value
        self.adjust_size()

    def adjust_size(self, *args):
        if not self.parent or not self.image.texture:
            return

        (par_x, par_y) = self.parent.size

        if par_x == 0 or par_y == 0:
            with self.canvas:
                self.canvas.clear()
            return

        par_scale = par_x / par_y
        (img_x, img_y) = self.image.texture.size
        img_scale = img_x / img_y

        if par_scale > img_scale:
            (img_x_new, img_y_new) = (img_x, img_x / par_scale)
        else:
            (img_x_new, img_y_new) = (img_y * par_scale, img_y)

        crop_pos_x = (img_x - img_x_new) / 2
        crop_pos_y = (img_y - img_y_new) / 2

        subtexture = self.image.texture.get_region(
            crop_pos_x, crop_pos_y, img_x_new, img_y_new
        )

        with self.canvas:
            self.canvas.clear()
            Color(1, 1, 1)
            Rectangle(texture=subtexture, pos=self.pos, size=(par_x, par_y))
