# widgets/eventactionrow.py
#
# Copyright 2020-2023 Fabio Comuni, et al.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
from typing import Optional
import datetime
import math

from dateutil.tz import UTC

from gi.repository import Gio
from gi.repository import Gtk
from gi.repository import Adw
from gi.repository import Graphene

from ..utils import clean_markup
from ..models import dbUpdateWatcher, Event

from .starbutton import StarButton


class EventActionRow(Adw.ActionRow):
    _hid: Optional[int] = None
    obj: Optional[Event] = None

    def __init__(self, obj):
        super().__init__(activatable=True, selectable=False)
        self.obj = obj

        self._hid = dbUpdateWatcher.connect('update', self.on_update)

        self.toggle_btn = StarButton(is_starred=obj.starred)
        self.toggle_btn.connect("clicked", self.toggle_starred)
        self.add_suffix(self.toggle_btn)

        self.on_update()

        Gio.Application.get_default().connect(
            "tick", lambda *_: self.queue_draw()
        )

    def __del__(self):
        if self._hid:
            dbUpdateWatcher.disconnect(self.hid)

    def _is_first(self):
        return self.get_index() == 0

    def _is_last(self):
        p = self.get_parent()
        if p is None:
            return True
        return p.get_last_child() == self

    def do_snapshot(self, snapshot: Gtk.Snapshot):
        x = -2
        y = -2
        w = 10
        h = self.get_allocated_height()

        rect = Graphene.Rect().init(x, y, w, h)
        cr = snapshot.append_cairo(rect)
        self._draw(cr, rect)
        first_child = self.get_first_child()
        if first_child is not None:
            self.snapshot_child(first_child, snapshot)

    def _roundedclippath(self, cr, x, y, w, h, rtop, rbottom):
        cr.arc(x + rtop, y + rtop, rtop, math.pi, 3 * math.pi / 2)
        cr.line_to(x + w, y)
        cr.line_to(x + w, y + h)
        cr.line_to(x + w, y + h)
        # cr.arc(x+w-rtop, y+rtop, rtop, 3*math.pi/2, 0) # top-right round corner
        # cr.arc(x+w-rbottom, y+h-rbottom, rbottom, 0, math.pi/2) # bottom-right
        cr.arc(x + rbottom, y + h - rbottom, rbottom, math.pi / 2, math.pi)
        cr.close_path()
        cr.clip()

    def _draw(self, cr, rect):
        """
        Draw event progress indicator in widget background.
        It's a tick line on left border from top whose lenght is relative to the
        percentage of event time elapsed.

        We need to handle list round corners. Gtk doesn't clip element like HTML
        when rendering with "border-radius" value, so our line will overflow the
        corner.

        I use the clip path method. It get a top rounded corner if `self` is the first
        row in the list, and a bottom rounded corner if it's the last.
        Round radio is defined in code, as I can't find a way to get current widget CSS
        values.
        Then a line of required lenght, color and width is drawn from top.
        Color of the line is widget foreground color with alpha 0.2. Using foreground color
        make the line work on light and dark theme.
        """
        now = datetime.datetime.now(UTC).replace(tzinfo=None)
        # now = datetime.datetime(2023, 2, 5, 10, 50, tzinfo=UTC).replace(tzinfo=None)
        obj = self.obj

        dur = (obj.end.timestamp() - obj.start.timestamp())
        pos = (now.timestamp() - obj.start.timestamp())
        if dur > 0:
            prc = min(max(pos / dur, 0.0), 1.0)
        else:
            # not a valid event duration
            # set prc to 0 if the event is in the future or 1 if in the past
            prc = int(pos >= 0)

        if prc == 0:
            return

        maxh = rect.get_height()
        x = rect.get_x()
        y = rect.get_y()
        h = maxh * prc
        w = rect.get_width()

        # clip at rounded corners
        rtop = 12 if self._is_first() and self.get_header() is None else 0
        rbottom = 12 if self._is_last() else 0

        # color
        # >= gtk 4.10 : self.get_color()
        color = self.get_style_context().get_color()
        color.alpha = 0.2

        cr.save()

        # rounded clip path
        self._roundedclippath(cr, x, y - 1, w, maxh + 2, rtop, rbottom)

        # draw line
        cr.set_source_rgba(
            color.red, color.green, color.blue, color.alpha
        )
        cr.set_line_width(w)
        cr.move_to(x, y)
        cr.line_to(x, h)
        cr.stroke()
        cr.restore()

    def on_update(self, *args):
        obj = self.obj

        subtitle = "{}-{}".format(
            obj.start_in_tz().strftime("%a %H:%M"),
            obj.end_in_tz().strftime("%H:%M"))

        if obj.room:
            subtitle = "{} ({})".format(
                subtitle,
                obj.room)

        if obj.track:
            subtitle = "{} '{}'".format(
                subtitle,
                obj.track)

        ps = list(obj.persons())
        if len(ps) > 0:
            subtitle = "{} - {}".format(
                subtitle,
                ", ".join([str(p) for p in ps]))

        self.set_title(clean_markup(obj.title))
        self.set_subtitle(clean_markup(subtitle))

        self.toggle_btn.props.is_starred = self.obj.starred

    def toggle_starred(self, button):
        self.obj.set_star(not self.obj.starred)
