# self.py
#
# Copyright 2020 Fabio
#
# 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/>.

import datetime
import re

from gi.repository import GLib
from gi.repository import Gtk
from gi.repository import GObject
from gi.repository import GdkPixbuf
from gi.repository import Gio
from gi.repository import Handy
from gi.repository import Notify

from .config import APP_NAME, APP_ID, APP_VERSION
from . import pages
from . import local
from . import models
from .preferences import PreferencesWindow
from .widgets import LoadingWidget, ErrorWidget, PlaceholderWidget
from .widgets import NotificationOverlay, LoadingOverlay, SearchBarOverlay
from .widgets import InputDialog, ConferenceEditDialog


class NetworkConnectionMonitor(GObject.Object):
    def __init__(self):
        super().__init__()
        self._isconnected = False
        self.nm = Gio.NetworkMonitor.get_default()
        self.nm.connect("network-changed", self.on_network_changed)
        self.on_network_changed()

    def on_network_changed(self, *args):
        _ic = self._isconnected
        self._isconnected = self.nm.get_connectivity() == Gio.NetworkConnectivity.FULL
        if (self._isconnected != _ic):
            #print("NetworkConnectionMonitor.isconnected", self._isconnected)
            self.notify("isconnected")

    @GObject.Property
    def isconnected(self):
        """The system can reach the global netowrk. Bool, read-only"""
        #print("NetworkConnectionMonitor.isconnected getter:", self._isconnected)
        return self._isconnected


class StartView(pages.BaseListPage):
    """
    window
        overlay
            notification
            loading
        title_bar
            open_button
            search_button
            update_button

        box
            search
            column
                box <VBox>
                    titlebox <VBox> (if title or subtitle is set)
                        title       <Label>
                        subtitle    <Label>
                frame
                    listbox
    """

    def __init__(self, window, **kwargs):
        self.window = window
        self.nm = NetworkConnectionMonitor()
        self.data = []

        self.empty_placeholder = PlaceholderWidget(
            APP_NAME,
            _("No conferences yet.") + "\n" + \
            _("Add a conference with the add button in title bar."))
        self.empty_placeholder.show_all()

        self.nomatch_placeholder = PlaceholderWidget(
            text = _("No matches found"),
            icon_name = 'system-search'
        )
        self.nomatch_placeholder.show_all()

        super().__init__(**kwargs)

        self.get_style_context().add_class("start-view")
        self.get_style_context().add_class("clean-list")

        self.column.set_maximum_size(500)
        title_bar = Handy.HeaderBar(
            centering_policy = Handy.CenteringPolicy.STRICT,
            show_close_button = True,
            hexpand = True,
            title = APP_NAME
        )

        self.listbox.set_placeholder(self.empty_placeholder)

        fetcher = local.open_menu(self.nm.props.isconnected)
        if fetcher is not None:
            self.window.loading.show(fetcher)
            fetcher.connect("done", self.update_list)
            fetcher.connect("error", lambda s,e: self.window.notification.show(_("<b>Error loading events list:</b>\n{}").format(e)))
        else:
            self.update_list()

        # app menu
        appmenumodel = Gio.Menu()
        self.window.pop_menu(appmenumodel)

        self.start_button = Gtk.Button.new_from_icon_name('open-menu-symbolic', 1)
        self.start_button.set_tooltip_text(_("Show window menu"))
        appmenu = Gtk.Popover.new_from_model(self.start_button, appmenumodel)
        self.start_button.connect('clicked',  lambda _: appmenu.popup())
        title_bar.pack_end(self.start_button)

        self.update_button = Gtk.Button.new_from_icon_name('view-refresh-symbolic', 1)
        self.update_button.set_tooltip_text(_("Refresh events list"))
        self.update_button.set_sensitive(self.nm.props.isconnected)
        self.update_button.connect('clicked', self.update_schedules)
        title_bar.pack_end(self.update_button)

        self.open_button = Gtk.Button.new_from_icon_name('list-add-symbolic', 1)
        self.open_button.set_tooltip_text(_("Add event from URL"))
        self.open_button.set_sensitive(self.nm.props.isconnected)
        self.open_button.connect('clicked', self.open_custom_url)
        title_bar.pack_start(self.open_button)

        _search_button_icon = Gtk.Image.new_from_pixbuf(
            Gtk.IconTheme.get_default().load_icon(
                'system-search-symbolic', 16, 0))
        self.search_button = Gtk.ToggleButton.new()
        self.search_button.set_image(_search_button_icon)
        self.search_button.set_tooltip_text(_("Find event"))
        self.search_button.bind_property("active",
            self.window.searchbar, "search-mode-enabled",
            GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE )
        title_bar.pack_start(self.search_button)

        # search event
        self.window.searchbar.entry.connect('notify::text', self.on_do_search)
        self.window.searchbar.connect('notify::search-mode-enabled', self.on_do_search)

        # can't do this because: Warning: ../glib/gobject/gbinding.c:271: Unable to convert a value of type PyObject to a value of type gboolean
        #self.nm.bind_property("isconnected", update_action, "enabled", 0 )
        self.nm.connect("notify",self.on_networkconnectionmonitor_notify)

        window.set_titlebar(title_bar)
        title_bar.show_all()
        self.show_all()

    def on_do_search(self, *args):
        self.update_list()

    def on_networkconnectionmonitor_notify(self, *args):
        is_connected = self.nm.props.isconnected
        self.update_button.set_sensitive(is_connected)
        self.open_button.set_sensitive(is_connected)

    def update_schedules(self, *args):
        fetcher = local.update_menu()
        def _done(s, f):
            self.update_list()
            self.window.notification.show(_("<b>Events list updated</b>"))

        def _error(s, e):
            self.window.notification.show(_("<b>Error loading events list:</b>\n{}").format(e))

        self.window.loading.show(fetcher)
        fetcher.connect("done", _done)
        fetcher.connect("error", _error)

    def edit_meta(self, url):
        conf = models.Conference.by_url(url)
        if conf is None:
            return
        d = ConferenceEditDialog(self.window, conf)
        ret = d.run()
        if ret == ConferenceEditDialog.OK:
            conf = d.get_conf()
            local.update_user_menu(conf.to_json())
            with conf.get_meta() as m:
                m.title = conf.title
                m.start = conf.start
                m.end = conf.end
            self.update_list()
        d.destroy()
        return conf

    def open_custom_url(self, *args):
        def _cbk(meta):
            conf = self.edit_meta(meta.url)
            self.update_list()
            self.window.notification.show(_("<b>New event:</b>\n{}").format(conf.title))

        def _error(s, e):
            self.window.notification.show(_("<b>Error:</b>\n{}").format(e))

        dialog = InputDialog(self.window)
        res = dialog.run()
        if res == InputDialog.OK:
            url = dialog.get_text()
            try:
                fetcher = local.add_user_menu(url, _cbk)
                if fetcher is not None:
                    fetcher.connect("error", _error)
            except local.MenuItemAlreadyExistsException as e:
                # show existing obj
                conf = models.Conference()._init(e.obj)
                self.window.show_main(conf)
            except Exception as e:
                import traceback;  traceback.print_exc()
                self.window.notification.show(_("<b>Error:</b>\n{}").format(e))
            else:
                if fetcher is not None:
                    self.window.loading.show(fetcher)

        dialog.destroy()

    def get_objects(self):
        confs = self.data
        sb = self.window.searchbar
        try:
            confs = models.Conference.all()

            # if searchbar is visible and search text is not "", filter confs by title and url
            if (sb.get_search_mode() and sb.entry.get_text() != ""):
                self.listbox.set_placeholder(self.nomatch_placeholder)
                search = sb.entry.get_text().lower()
                confs = [ conf for conf in confs
                            if search in conf.title.lower() or search in conf.url.lower() ]
            else:
                self.listbox.set_placeholder(self.empty_placeholder)

        except Exception as e:
            self.window.notification.show(_("<b>Error loading events list:</b>\n{}").format(e))
        return confs


    def group_by(self, obj):
        if obj.end < datetime.date.today():
            return _("Past")
        else:
            return _("Coming up")

    def build_row(self, obj):
        row = Handy.ActionRow(activatable=True, selectable=False)
        row.set_title(obj.title)
        return row

    def on_activate(self, listbox, actionrow):
        idx = actionrow.get_index()
        obj = self.data[idx]
        self.window.show_main(obj)


class MainView(Gtk.VBox):
    """
    window
        overlay
            notification
            loading
        title_bar
            back_button
            search_button
            'squeezer'
                top_switcher
                title_label
            update_button
            start_button
        box
            search
            box (self)
                mainstack
                    leaflet ("schedule")
                        main_menu
                        Gtk.Separator
                        stack_schedule <Handy.Deck>
                            [pages]
                    stack_starred ("starred") <Handy.Deck>
                        starred
                        [pages]

                    map ("map")
                bottom_switcher

    TODO: this view is handling too much things!
    """

    def __init__(self, window, conf):
        super().__init__()

        title_bar = Handy.HeaderBar(
            centering_policy = Handy.CenteringPolicy.STRICT,
            show_close_button = True,
            hexpand = True
        )
        window.set_titlebar(title_bar)
        self.window = window
        self.conf = conf

        self.notifications = {} # keep track of desktop notifications

        self.nm = NetworkConnectionMonitor()


        # app menu, it's always usefull
        appmenumodel = Gio.Menu()
        if conf.user:
            appmenumodel.append(_("Edit info"), "win.edit-meta")
        appmenumodel.append(_("Return to events list"), "win.return-to-events")
        self.window.pop_menu(appmenumodel)

        self.start_button = Gtk.Button.new_from_icon_name('open-menu-symbolic', 1)
        self.start_button.set_tooltip_text(_("Show window menu"))

        appmenu = Gtk.Popover.new_from_model(self.start_button, appmenumodel)
        self.start_button.connect('clicked',  lambda _: appmenu.popup())
        title_bar.pack_end(self.start_button)



        self.url = conf.url
        # open db update should be triggered by network state and data age
        try:
            fetcher = local.openconf(conf, is_online=self.nm.props.isconnected)
        except Exception as e:
            e = ErrorWidget(_("Error loading event"), e)
            self.pack_start(e, True, True, 0)
            title_bar.show_all()
            self.show_all()
        else:
            if fetcher is not None:
                fetcher.connect("done", self.on_fetcher_done)
                fetcher.connect("error", self.on_fetcher_error)

                self.pack_start(LoadingWidget(fetcher), True, True, 0)
                title_bar.show_all()
                self.show_all()
            else:
                self.build_gui()

    def build_gui(self):
        title_bar = self.window.get_titlebar()

        # title bar buttons:
        self.update_button = Gtk.Button.new_from_icon_name('view-refresh-symbolic', 1)
        self.update_button.set_tooltip_text("Refresh event")
        self.update_button.set_sensitive(self.nm.props.isconnected)
        self.update_button.connect('clicked', self.update_event)
        title_bar.pack_end(self.update_button)

        self.nm.connect("notify",self.on_networkconnectionmonitor_notify)

        # back button
        self.back_button = Gtk.Button.new_from_icon_name(
                'go-previous-symbolic', 1)
        self.back_button.set_tooltip_text(_("Go back"))
        self.back_button.connect('clicked', self.goBack)
        title_bar.pack_start(self.back_button)

        # search button
        _search_button_icon = Gtk.Image.new_from_pixbuf(
            Gtk.IconTheme.get_default().load_icon(
                'system-search-symbolic', 16, 0))
        self.search_button = Gtk.ToggleButton.new()
        self.search_button.set_image(_search_button_icon)
        self.search_button.set_tooltip_text(_("Find talk"))
        self.search_button.bind_property("active",
            self.window.searchbar, "search-mode-enabled",
            GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE )
        title_bar.pack_start(self.search_button)

        # search event
        self.window.searchbar.entry.connect('notify::text', self.on_do_search)
        self.window.searchbar.connect('notify::search-mode-enabled', self.on_search_mode_changed)

        # mainstack
        self.mainstack = Gtk.Stack(
            transition_type=Gtk.StackTransitionType.NONE,
            vexpand_set=True,
            vexpand=True)

        self.leaflet = Handy.Leaflet(
            can_swipe_back=True,
            hhomogeneous_folded=True,
            hhomogeneous_unfolded=False)

        self.leaflet.connect('notify::folded', self.on_leaflet_notify)
        self.leaflet.connect('notify::visible-child', self.on_leaflet_notify)
        self.main_menu = pages.MainMenuPage(pageStack=self, conf=self.conf)

        self.stack_schedule = Handy.Deck(
            can_swipe_back=True,
            can_swipe_forward=False)
        self.stack_schedule.connect("notify::transition-running", self.on_stack_changed)

        # add main menu and stack to leaflet
        self.leaflet.add(self.main_menu)
        sep = Gtk.Separator()
        self.leaflet.add(sep)
        self.leaflet.child_set_property(sep, 'navigatable', False)
        self.leaflet.add(self.stack_schedule)

        # add first page to stack_schedule
        self.stack_schedule.add(pages.ConferencePage(self.conf))
        self.stack_schedule.count = 1


        # starred
        self.stack_starred = Handy.Deck(
            can_swipe_back=True,
            can_swipe_forward=False)
        self.stack_starred.connect("notify::transition-running", self.on_stack_changed)

        self.starred = pages.StarredPage(searchbar=self.window.searchbar)
        self.starred._set_pagestack(self)

        # add first page to stack_starred
        self.stack_starred.add(self.starred)
        self.stack_starred.count = 1

        # set current stack
        self.stack = self.stack_schedule


        # main stack content

        self.mainstack.add_titled(self.leaflet, "schedule", _("Schedule"))
        self.mainstack.child_set_property(self.leaflet, "icon-name", "x-office-calendar-symbolic")


        self.mainstack.add_titled(self.stack_starred, "starred", _("Starred"))
        self.mainstack.child_set_property(self.stack_starred, "icon-name", "starred-symbolic")


        if len(self.conf.get_map_links()) > 0:
            self.map = pages.MapPage(self.conf)
            self.mainstack.add_titled(self.map, "map", _("Map"))
            self.mainstack.child_set_property(self.map, "icon-name", "mark-location-symbolic")

        # main stack switchers
        self.top_switcher = Handy.ViewSwitcher(
            policy=Handy.ViewSwitcherPolicy.WIDE,
            stack=self.mainstack)
        self.bottom_switcher = Handy.ViewSwitcherBar(
            stack=self.mainstack)

        #mainstack event
        self.mainstack.connect("notify::visible-child", self.on_mainstack_changed)

        squeezer = Handy.Squeezer()
        squeezer.add(self.top_switcher)
        title_label = Gtk.Label()
        #title_label.set_markup("<b>{}</b>".format(APP_NAME))
        squeezer.add(title_label)
        title_bar.set_custom_title(squeezer)
        squeezer.connect("notify::visible-child",self.on_headerbar_squeezer_notify)

        # main box contanier

        self.pack_start(self.mainstack, True, True, 0)
        self.pack_start(self.bottom_switcher, False, False, 0)

        title_bar.show_all()
        self.show_all()

        # Check if there are notifications to send
        Gio.Application.get_default().connect(
            'tick', self.check_event_notification
        )
        self.check_event_notification()

        self.update_titlebar_buttons()

        # set start mainstack page
        self.mainstack.set_visible_child(self.leaflet)

    def on_networkconnectionmonitor_notify(self, *args):
        is_connected = self.nm.props.isconnected
        self.update_button.set_sensitive(is_connected)

    def on_mainstack_changed(self, *args):
        if (self.mainstack.get_visible_child() == self.stack_starred):
            self.stack = self.stack_starred
            # get back to starred list when switching main stack
            # would be better not, but as we can't update EventDetailPage
            # without rebuild it, if use is in a starred EventDetailPage
            # then get back to schedule and change something related to that
            # event, EventDetailPage objects in stack_starred will be out
            # of sync (eg, a new starred event which overlaps)
            self.starred.update_list()
            self.stack.set_visible_child(self.starred)
            self.stack.count = 1
            for w in self.stack.get_children()[1:]:
                w.destroy()
        elif (self.mainstack.get_visible_child() == self.leaflet):
            self.stack = self.stack_schedule
        self.update_titlebar_buttons()

    def on_headerbar_squeezer_notify(self, squeezer, event):
        child = squeezer.get_visible_child()
        self.bottom_switcher.set_reveal(child != self.top_switcher)

    def on_leaflet_notify(self, sender, prop):
        self.update_titlebar_buttons()

    def on_fetcher_done(self, *args):
        self.foreach(lambda obj: obj.destroy())
        self.build_gui()

    def on_fetcher_error(self, s, e):
        self.foreach(lambda obj: obj.destroy())
        e = ErrorWidget(_("Error loading event"), e)
        self.pack_start(e, True, True, 0)
        e.show_all()

    def on_do_search(self, *args):
        if self.mainstack.get_visible_child_name() == "schedule":
            searchpage = None
            if self.stack.count > 0:
                searchpage = self.stack.get_children()[0]

            if searchpage.__class__ != pages.SearchEventsPage:
                searchpage = pages.SearchEventsPage(searchbar = self.window.searchbar)
                self.replaceWithPage(searchpage)

            if self.stack.count > 1:
                self.stack.set_visible_child(searchpage)
                self.stack.count = 1
                for w in self.stack.get_children()[1:]:
                    w.destroy()
            searchpage.update_list()

        elif self.mainstack.get_visible_child_name() == "starred":
            if self.stack.count > 1:
                # reset stack to first page
                self.stack.set_visible_child(self.starred)
                self.stack.count = 1
                for w in self.stack.get_children()[1:]:
                    w.destroy()
            self.starred.update_list()

    def on_search_mode_changed(self, *args):
        if self.mainstack.get_visible_child_name() == "schedule":
            searchmode = self.window.searchbar.get_search_mode()
            currentfirstpage = self.stack.get_children()[0]
            if not searchmode and currentfirstpage.__class__ == pages.SearchEventsPage:
                # search closed, reset to ConferencePage
                self.main_menu.select_conference_page()

    def edit_meta(self):
        d = ConferenceEditDialog(
            self.window, self.conf, can_cancel=True, can_delete=True)
        ret = d.run()
        self.conf = d.get_conf()
        d.destroy()
        if ret == ConferenceEditDialog.OK:
            local.update_user_menu(self.conf.to_json())
            with self.conf.get_meta() as m:
                m.title = self.conf.title
                m.start = self.conf.start
                m.end = self.conf.end
            self._update_ui()
            self.window.notification.show(_("<b>Updated event:</b>\n{}").format(self.conf.title))
        if ret == ConferenceEditDialog.DELETE:
            local.delete_user_menu(self.conf.to_json())
            self.window.notification.show(_("<b>Deleted event:</b>\n{}").format(self.conf.title))
            # we could delete cache db here...
            # get back to conferences list
            self.window.show_start()

    def _update_ui(self):
        """update ui when database change. Reset view to ConferencePage"""
        self.main_menu.update()
        self.mainstack.set_visible_child_name("schedule")
        self.main_menu.select_conference_page()

    def update_event(self, *args):
        """update cached event data"""
        def _done(*args):
            self.window.loading.close()
            self.window.notification.show(_("<b>Event schedule updated</b>"))
            self._update_ui()

        def _error(s, e):
            self.window.loading.close()
            self.window.notification.show(_("<b>Error:</b>\n{}").format(e))

        fetcher = local.updatedb(self.url)
        self.window.loading.show(fetcher)
        fetcher.connect("done", _done)
        fetcher.connect("error", _error)
        #TODO UPDATE GUI!!! ! !

    def check_event_notification(self, *args):
        nextup = list(models.Event.nextup(5))
        for e in nextup:
            # fast and stupid clean html
            cleanr = re.compile('<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});')
            title = re.sub(cleanr, '', e.title)
            print("nextup:", e.start_in_tz().strftime("%H:%M"), title, "@", e.room)
            self._send_desktop_notification(
                _("Next up: {}").format(title),
                _("at {} in {}").format(
                    e.start_in_tz().strftime("%H:%M"),
                    e.room
                ),
                f"nextup-{e.id}")
            e.set_notified(True)

    def _send_desktop_notification(self, title, body, nid=None):
        if nid in self.notifications:
            return
        n = Notify.Notification.new(title, body, "net.kirgroup.confy")
        n.set_timeout(Notify.EXPIRES_NEVER)
        #n.add_action("view", "View", on_notification_action, nid)
        n.show()
        self.notifications[nid] = n

    """
    def _gio_send_desktop_notification(self, title, body, nid=None):
        n = Gio.Notification.new(title)
        n.set_body(body)
        n.set_icon(Gio.ThemedIcon.new("net.kirgroup.confy"))
        n.set_priority(Gio.NotificationPriority.HIGH)
        print(Gio.Application.get_default(), n)
        self.notifications[nid] = n
        Gio.Application.get_default().send_notification(nid, n)
    """

    def update_titlebar_buttons(self):
        search_show = True
        back_show = False
        if self.mainstack.get_visible_child_name() == "schedule":
            folded = self.leaflet.get_property('folded')
            firstfold = self.leaflet.get_visible_child() == self.main_menu
            back_show = (folded and not firstfold) or self.stack.count > 1
        elif self.mainstack.get_visible_child_name() == "starred":
            back_show = self.stack.count > 1

        self.back_button.set_visible(back_show)
        self.search_button.set_visible(search_show)

    def on_stack_changed(self, *args):
        if self.stack.get_transition_running():
            # wait for transition end
            return
        stack_children = self.stack.get_children()
        lastid = len(stack_children) - 1
        currentid = stack_children.index(self.stack.get_visible_child())
        if currentid == (lastid-1):
            # we got back one page, destroy last page
            stack_children[lastid].destroy()
            self.stack.count = self.stack.count - 1
        self.update_titlebar_buttons()

    def goBack(self, *args):
        if (self.stack.count > 1):
            self.stack.navigate(Handy.NavigationDirection.BACK)
            """
            #self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_RIGHT)
            current = self.stack.get_children()[-1]
            previous = self.stack.get_children()[-2]
            self.stack.set_visible_child(previous)
            self.stack.count = self.stack.count - 1

            folded = self.leaflet.get_property('folded')
            while not folded and self.leaflet.get_child_transition_running():
                pass
            current.destroy()
            """
        else:
            self.leaflet.set_visible_child(self.main_menu)
        self.update_titlebar_buttons()

    def pushPage(self, page):
        #self.mainstack.set_visible_child(self.leaflet)
        page._set_pagestack(self)
        self.stack.add(page)
        self.stack.count = self.stack.count + 1
        page.show_all()
        self.leaflet.set_visible_child(self.stack)
        self.stack.set_visible_child(page)

        self.update_titlebar_buttons()

    def replaceWithPage(self, page):
        #self.mainstack.set_visible_child(self.leaflet)
        page._set_pagestack(self)
        self.stack.add(page)
        page.show_all()
        self.leaflet.set_visible_child(self.stack)
        self.stack.set_visible_child(page)
        self.stack.count = 1

        #folded = self.leaflet.get_property('folded')
        #while not folded and self.leaflet.get_child_transition_running():
        #    pass

        for w in self.stack.get_children()[:-1]:
            w.destroy()

        self.update_titlebar_buttons()


class ConfyWindow(Gtk.ApplicationWindow):
    """
        ConfyWindow
            overlay
                notification    <NotificationOverlay>
                loading         <LoadingOverlay>
                box             <VBox>
                    searchbar       <SearchBarOverlay>
                    view            <MainView>|<StartView>
    """

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.set_icon_name(APP_ID)
        self.set_default_size(800,600)

        self.overlay = Gtk.Overlay.new()
        self.add(self.overlay)

        self.notification = NotificationOverlay()
        self.overlay.add_overlay(self.notification)

        self.loading = LoadingOverlay()
        self.overlay.add_overlay(self.loading)

        self.box = Gtk.VBox()
        self.searchbar = SearchBarOverlay()
        self.box.pack_start(self.searchbar, False, False, 0)
        self.overlay.add(self.box)


        # win actions
        edit_meta_action = Gio.SimpleAction.new("edit-meta", None)
        edit_meta_action.connect("activate", self.edit_meta)
        self.add_action(edit_meta_action)

        return_to_events_action = Gio.SimpleAction.new("return-to-events", None)
        return_to_events_action.connect("activate", lambda *_: self.show_start() )
        self.add_action(return_to_events_action)

        show_preferences_action = Gio.SimpleAction.new("show-preferences", None)
        show_preferences_action.connect("activate", self.show_preferences )
        self.add_action(show_preferences_action)

        search_action = Gio.SimpleAction.new("search", None)
        search_action.connect("activate", lambda *_: self.searchbar.set_search_mode(True) )
        self.add_action(search_action)

        about_action = Gio.SimpleAction.new("about", None)
        about_action.connect("activate", self.show_about)
        self.add_action(about_action)

        self.view = None
        self.show_all()

        self.show_start()

    def show_preferences(self, *args):
        pref = PreferencesWindow(transient_for=self)
        pref.show_all()

    def edit_meta(self, *args):
        if isinstance(self.view, MainView):
            self.view.edit_meta()

    def pop_menu(self, menu):
        s = Gio.Menu()
        s.append(_("Preferences"), "win.show-preferences")
        s.append(_("Keyboard shortcuts"), "app.shortcuts")
        s.append(_("About {}").format(APP_NAME), "win.about")
        menu.append_section(None, s)

    def show_about(self, *args):
        Gtk.AboutDialog(
            program_name=APP_NAME,
            logo_icon_name=APP_ID,
            version=APP_VERSION,
            copyright="© 2020 Fabio Comuni",
            website="https://confy.kirgroup.net/",
            authors=["Fabio Comuni", "Evangelos Ribeiro Tzaras", "Sebastian Crane"],
            comments=_("Conference schedules viewer"),
            license_type=Gtk.License.GPL_3_0
        ).show()

    def show_start(self):
        """start page, list conferences"""
        if self.view is not None:
            self.view.destroy()
        self.searchbar.set_search_mode(False)
        self.view = StartView(self)
        self.box.pack_start(self.view, True, True, 0)
        local.close()

    def show_main(self, conf):
        """start page, list conferences"""
        if self.view is not None:
            self.view.destroy()
        self.searchbar.set_search_mode(False)
        self.view = MainView(self, conf)
        self.box.pack_start(self.view, True, True, 0)


