# SPDX-FileCopyrightText: 2019-2026 Valéry Febvre
# SPDX-License-Identifier: GPL-3.0-or-later
# Author: Valéry Febvre <vfebvre@easter-eggs.com>

from gi.repository import Adw
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Graphene
from gi.repository import Gsk
from gi.repository import Gtk


class SynopsisFading(Gtk.Widget):
    __gtype_name__ = 'SynopsisFading'

    def __init__(self):
        super().__init__()

        self.__max_height = 95
        self.__faded = False

        self.min_height = self.__max_height
        self.current_height = 0
        self.allocated_width = 0

        self.animation = Adw.SpringAnimation(
            widget=self,
            value_from=0,
            value_to=1,
            spring_params=Adw.SpringParams.new(damping_ratio=0.5, mass=1, stiffness=50),
            target=Adw.CallbackAnimationTarget.new(self.animate),
            initial_velocity=1,
            epsilon=0.001,
            clamp=True
        )
        self.child = None

    @GObject.Property(type=bool, default=False)
    def faded(self):
        return self.__faded

    @faded.setter
    def faded(self, value):
        self.__faded = value

    @GObject.Property(type=int, default=95)
    def max_height(self):
        return self.__max_height

    @max_height.setter
    def max_height(self, value):
        if self.max_height == value:
            return

        self.__max_height = value

        if self.child:
            if self.allocated_width > 0:
                width = self.allocated_width
            else:
                _, width, _, _ = self.child.measure(Gtk.Orientation.HORIZONTAL, -1)

            _, child_height, _, _ = self.child.measure(Gtk.Orientation.VERTICAL, width)
            if child_height <= value:
                target_height = child_height
            else:
                target_height = value
        else:
            target_height = value

        self.animation_height_start = self.current_height
        self.animation_height_end = target_height
        self.animation.play()

    def animate(self, value):
        self.current_height = Adw.lerp(self.animation_height_start, self.animation_height_end, value)
        self.queue_resize()

    def do_measure(self, orientation, for_size):
        if not self.child:
            return 0, 0, -1, -1

        if orientation == Gtk.Orientation.HORIZONTAL:
            return self.child.measure(orientation, for_size)

        if for_size == -1:
            if self.allocated_width > 0:
                for_size = self.allocated_width
            else:
                _, for_size, _, _ = self.child.measure(Gtk.Orientation.HORIZONTAL, -1)

        _, child_height, _, _ = self.child.measure(Gtk.Orientation.VERTICAL, for_size)
        if child_height <= self.max_height:
            target_height = child_height
        else:
            target_height = self.max_height

        if self.animation.get_state() == Adw.AnimationState.IDLE:
            self.current_height = target_height

        return self.current_height, self.current_height, -1, -1

    def do_size_allocate(self, width, height, baseline):
        if self.allocated_width != width:
            self.allocated_width = width
            if self.child:
                _, child_height, _, _ = self.child.measure(Gtk.Orientation.VERTICAL, width)

                if self.min_height != self.max_height or child_height < self.min_height:
                    self.current_height = child_height
                GLib.idle_add(self.queue_resize)

        if self.child:
            _, child_height, _, _ = self.child.measure(Gtk.Orientation.VERTICAL, width)
            if child_height > height:
                child_height = child_height
            else:
                child_height = height

            self.child.allocate(width, child_height, baseline, None)

        self.update_faded()

    def do_snapshot(self, snapshot):
        if not self.child:
            return

        width = self.get_width()
        height = self.get_height()
        if height <= 0:
            return

        _, child_height, _, _ = self.child.measure(Gtk.Orientation.VERTICAL, width)
        if child_height <= height:
            self.snapshot_child(self.child, snapshot)
            return

        # Fading
        snapshot.push_mask(Gsk.MaskMode.ALPHA)

        stop_offset = Adw.lerp(0.25, 1, (height - self.min_height) / (child_height - self.min_height))

        cs0 = Gsk.ColorStop()
        cs0.color = Gdk.RGBA(1, 1, 1, 1)
        cs0.offset = stop_offset
        cs1 = Gsk.ColorStop()
        cs1.color = Gdk.RGBA(1, 1, 1, 0)
        cs1.offset = 1

        snapshot.append_linear_gradient(
            Graphene.Rect.init(Graphene.Rect.alloc(), 0, 0, width, height),
            Graphene.Point.init(Graphene.Point.alloc(), 0, 0),
            Graphene.Point.init(Graphene.Point.alloc(), 0, height),
            [cs0, cs1]
        )

        snapshot.pop()

        self.snapshot_child(self.child, snapshot)

        snapshot.pop()

    def get_request_mode(self):
        return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH

    def set_child(self, child):
        if self.child:
            self.child.unparent()

        self.child = child
        self.child.set_parent(self)

    def set_markup(self, text):
        self.__faded = None
        self.__max_height = self.min_height
        self.current_height = self.min_height
        self.allocated_width = 0
        self.child.set_markup(text)

    def set_revealed(self, revealed):
        self.max_height = self.child.get_height() if revealed else self.min_height

    def update_faded(self):
        new_value = False

        if self.child:
            if self.allocated_width > 0:
                width = self.allocated_width
            else:
                _, width, _, _ = self.child.measure(Gtk.Orientation.HORIZONTAL, -1)
            _, child_height, _, _ = self.child.measure(Gtk.Orientation.VERTICAL, width)

            new_value = child_height > self.min_height

        if self.faded != new_value:
            self.faded = new_value
