# events.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 dataclasses import dataclass

from gi.repository import GObject
from gi.repository import Gtk

from .. import models
from ..config import FMT_DAY
from ..utils import clean_markup
from ..utils import InterruptibleTimeout
from ..widgets import EventActionRow


@dataclass
class ListBuilderCtx:
    lastobj = None
    listbox = None
    data = []
    guard = None

    def __repr__(self):
        return f"<ListBuilderCtx lastobj={self.lastobj}, listbox={self.listbox}>"


@Gtk.Template(resource_path="/net/kirgroup/confy/pages/events.ui")
class PageEvents(Gtk.Box):
    """Talks list page (called "Events" from pentabarf xml)"""
    __gtype_name__ = "ConfyPageEvents"

    stack = Gtk.Template.Child()

    pagebox = Gtk.Template.Child()

    search_button = Gtk.Template.Child()
    searchbar = Gtk.Template.Child()
    scroll = Gtk.Template.Child()

    filters = None
    group_by = None

    _update_handler_id = None
    _value = None

    timeout = InterruptibleTimeout()

    @GObject.Signal(name="row-activated")
    def row_activated(self):
        ...

    _title = ""
    @GObject.property(type=str, default="")
    def title(self):
        return self._title

    @title.setter
    def title(self, v):
        self._title = v

    _subtitle = ""
    @GObject.property(type=str, default="")
    def subtitle(self):
        return self._subtitle

    @subtitle.setter
    def subtitle(self, v):
        self._subtitle = v

    _model = ""
    @GObject.property(type=str, default="")
    def model(self):
        return self._model

    @model.setter
    def model(self, v):
        self._model = v

    def get_value(self):
        return self._value

    def set_model(self, v):
        self.props.model = v

    def on_litstbox_activated(self, listbox, row):
        self._value = row.obj
        self.emit("row-activated")

    def set_value(self, obj):
        print(f"PageEvents: set_value(obj={obj}) model={self._model}")
        if self._update_handler_id:
            models.dbUpdateWatcher.disconnect(self._update_handler_id)
        self._update_handler_id = None

        self.scroll.get_vadjustment().set_value(0)
        self.search_button.props.visible = True
        self.search_button.props.active = False
        self.searchbar.props.search_mode_enabled = False
        self.searchbar.props.show_close_button = True

        if self._model == "days":
            self.props.title = _("Talks on {}").format(obj.date.strftime(FMT_DAY))
            self.props.subtitle = ""
            self.filters = {'day': obj}
            self.group_by = None

        elif self._model == "tracks":
            subtitle = []
            if len(obj.room) == 1 and obj.room[0].name != "":
                subtitle.append(obj.room[0].name)
            if len(obj.date) == 1:
                subtitle.append(obj.date[0].date.strftime(FMT_DAY))

            self.props.title = _("{} Track").format(obj)
            self.props.subtitle = ", ".join(subtitle)
            self.filters = {'track': obj}
            self.group_by = "date"

        elif self._model == "rooms":
            if obj.name is None:
                title = _("No Room")
            else:
                title = _("Room {}").format(clean_markup(obj.name))

            self.props.title = title
            self.props.subtitle = ""
            self.filters = {'room': obj}
            self.group_by = "date"

        elif self._model == "starred":
            self.props.title = _("Starred")
            self.props.subtitle = ""
            self.filters = {'starred': True}
            self.group_by = "date"  # TODO: or None?
            self._update_handler_id = models.dbUpdateWatcher.connect("update", self.update)

        elif self.model == "search":
            self.props.title = _("Search Talks")
            self.props.subtitle = ""
            self.search_button.props.active = True
            self.search_button.props.visible = False
            self.searchbar.props.show_close_button = False
            self.filters = None
            self.group_by = None


        else:
            print(f"PageEvents: set_value: no valid model set '{self._model}'")
            return

        self.update()

    def update(self, *args):
        count, data = self.get_objects()

        self.timeout.stop()
        # empty box
        w = self.pagebox.get_last_child()
        while w:
            self.pagebox.remove(w)
            w = self.pagebox.get_last_child()

        if count == 0:
            self.show_placeholder()
        else:
            self.stack.set_visible_child_name('list')

        ctx = ListBuilderCtx()
        ctx.data = data
        self.timeout = InterruptibleTimeout(500, self.build_list, ctx)

    def build_list(self, timeout, ctx):
        for idx, obj in enumerate(ctx.data):
            if timeout.must_stop():
                return False

            header = self.build_header(obj, ctx.lastobj)
            if header:
                ctx.listbox = None
                self.pagebox.append(header)

            row = self.build_row(obj)
            if ctx.listbox is None:
                ctx.listbox = self.build_listbox()
                self.pagebox.append(ctx.listbox)
            ctx.listbox.append(row)
            ctx.lastobj = obj
            if idx >  10:
                return True

        return False

    def build_listbox(self):
        lb = Gtk.ListBox(margin_bottom=20)
        lb.add_css_class("boxed-list")
        lb.connect("row-activated", self.on_litstbox_activated)
        return lb

    def build_header(self, current, before):
        """
        Yes, this is just like a `GtkListBoxUpdateHeaderFunc`
        But GtkListBox headers aren't gtk-adwaita enough...

        `current` is current data to be added, `before` is data
        added before.
        return a widget for a new header, or None.
        """
        _title = None
        if self.group_by == "date":
            if before is None or current.date.strftime(FMT_DAY) != before.date.strftime(FMT_DAY):
                _title = current.date.strftime(FMT_DAY)
        elif self.group_by == "track":
             if before is None or current.track != before.track:
                _title = current.track
        elif self.group_by == "room":
            if before is None or current.room != before.room:
                _title = current.room
        else:
            return None

        if _title:
            label = Gtk.Label(label=_title, halign=Gtk.Align.START)
            label.add_css_class("heading")
            return label

        return None

    @Gtk.Template.Callback()
    def on_searchbar_entry_changed(self, e):
        self.update()

    @Gtk.Template.Callback()
    def on_search_mode_changed(self, *_):
        self.update()

    def get_objects(self):
        if self.model == "search":
            term = self.searchbar.entry.get_text()
            is_starred = self.searchbar.is_starred.get_active()
            _filters = {}
            if is_starred:
                _filters['starred'] = True

            g = models.Event.search(term, **_filters)
            return next(g), g
        if self.filters is None:
            return 0, []
        if self.searchbar.is_searching():
            term = self.searchbar.entry.get_text()
            is_starred = self.searchbar.is_starred.get_active()
            _filters = self.filters.copy()
            if is_starred:
                _filters['starred'] = True
            g =  models.Event.search(term, order_by=self.group_by, **_filters)
            return next(g), g
        g = models.Event.filter(**self.filters)
        return next(g), g

    def build_row(self, obj):
        row = EventActionRow(obj)
        return row

    def show_placeholder(self):
        if self._model == "search" or self.searchbar.is_searching():
            self.stack.set_visible_child_name('no_match')
        elif self._model == "starred":
            self.stack.set_visible_child_name('empty_star')
        else:
            self.stack.set_visible_child_name('list')
