# =================================================================
# =                 Author: Cameron Percival                      =
# =================================================================


# ============Functions============
import Functions as fn
from ui.AppFrameGUI import AppFrameGUI
from multiprocessing import cpu_count
from queue import Queue
from threading import Thread

base_dir = fn.os.path.abspath(fn.os.path.join(fn.os.path.dirname(__file__), ".."))
# base_dir = fn.os.path.dirname(fn.os.path.realpath(__file__))


class GUI_Worker(Thread):
    def __init__(self, queue):
        Thread.__init__(self)
        self.queue = queue

    def run(self):
        while True:
            # pull what we need from the queue so we can process properly.
            items = self.queue.get()

            try:
                # make sure we have the required number of items on the queue
                if items is not None:
                    # self, Gtk, vboxStack1, category, package_file = items

                    self, Gtk, vbox_stack, category, packages = items

                    AppFrameGUI.build_ui_frame(
                        self,
                        Gtk,
                        vbox_stack,
                        category,
                        packages,
                    )

            except Exception as e:
                fn.logger.error("Exception in GUI_Worker(): %s" % e)
            finally:
                if items is None:
                    fn.logger.debug("Stopping GUI Worker thread")
                    self.queue.task_done()
                    return False
                self.queue.task_done()


class GUI:
    def setup_gui_search(
        self,
        Gtk,
        Gdk,
        GdkPixbuf,
        base_dir,
        os,
        Pango,
        search_results,
        search_term,
        settings,
    ):
        try:
            # remove previous vbox
            if self.search_activated == False:
                self.remove(self.vbox)
            else:
                self.remove(self.vbox_search)

            # lets quickly create the latest installed list.
            fn.get_current_installed()

            # =======================================================
            #                       HeaderBar
            # =======================================================

            setup_headerbar(self, Gtk, settings)

            # =======================================================
            #                       App Notifications
            # =======================================================

            hbox0 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)

            self.notification_revealer = Gtk.Revealer()
            self.notification_revealer.set_reveal_child(False)

            self.notification_label = Gtk.Label()

            pb_panel = GdkPixbuf.Pixbuf().new_from_file(base_dir + "/images/panel.png")
            panel = Gtk.Image().new_from_pixbuf(pb_panel)

            overlay_frame = Gtk.Overlay()
            overlay_frame.add(panel)
            overlay_frame.add_overlay(self.notification_label)

            self.notification_revealer.add(overlay_frame)

            hbox0.pack_start(self.notification_revealer, True, False, 0)

            # ==========================================================
            #                       CONTAINER
            # ==========================================================

            self.vbox_search = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
            vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
            hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)

            self.vbox_search.pack_start(hbox, True, True, 0)
            self.add(self.vbox_search)

            # ==========================================================
            #                    PREP WORK
            # ==========================================================

            # This section sets up the tabs, and the array for dealing with the tab content

            # ==========================================================
            #                       GENERATE STACK
            # ==========================================================
            stack = Gtk.Stack()
            # stack.set_transition_type(Gtk.StackTransitionType.SLIDE_UP_DOWN)
            stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
            stack.set_transition_duration(350)

            vbox_stack = []
            stack_item = 0

            # Max Threads
            """
                Fatal Python error: Segmentation fault
                This error happens randomly, due to the for loop iteration on the cpu_count
                old code: for x in range(cpu_count()):
            """

            # spawn only 1 GUI_Worker threads, as any number greater causes a Segmentation fault

            search_worker = GUI_Worker(self.queue)
            search_worker.name = "thread_GUI_search_worker"
            # Set the worker to be True to allow processing, and avoid Blocking
            # search_worker.daemon = True
            search_worker.start()

            # This code section might look a little weird. It is because it was
            # derived from another function before this version was required.

            for category in search_results:
                # NOTE: IF the yaml file name standard changes, be sure to update this, or weirdness will follow.

                # subcategory = search_results[category][0].subcategory
                vbox_stack.append(
                    Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
                )
                stack.add_titled(
                    vbox_stack[stack_item],
                    str("stack" + str(len(vbox_stack))),
                    category,
                )

                # subcategory_desc = search_results[category][0].subcategory_description
                search_res_lst = search_results[category]

                # Multithreading!

                self.queue.put(
                    (
                        self,
                        Gtk,
                        vbox_stack[stack_item],
                        category,
                        search_res_lst,
                    )
                )

                stack_item += 1

            # send a signal that no further items are to be put on the queue
            self.queue.put(None)
            # safety to ensure that we finish threading before we continue on.
            self.queue.join()
            fn.logger.debug("GUI Worker thread completed")

            stack_switcher = Gtk.StackSidebar()
            stack_switcher.set_name("sidebar")
            stack_switcher.set_stack(stack)

            # =====================================================
            #                       LOGO
            # =====================================================

            ivbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
            pixbuf = GdkPixbuf.Pixbuf().new_from_file_at_size(
                os.path.join(base_dir, "images/sofirem.png"), 45, 45
            )
            image = Gtk.Image().new_from_pixbuf(pixbuf)

            # remove the focus on startup from search entry
            ivbox.set_property("can-focus", True)
            Gtk.Window.grab_focus(ivbox)

            # =====================================================
            #               RECACHE BUTTON
            # =====================================================

            btn_recache = Gtk.Button(label="Recache Applications")
            btn_recache.connect("clicked", self.recache_clicked)
            # btnReCache.set_property("has-tooltip", True)
            # btnReCache.connect("query-tooltip", self.tooltip_callback,
            #           "Refresh the application cache")

            # =====================================================
            #                   REPOS
            # =====================================================

            # if not (
            #     fn.check_package_installed("arcolinux-keyring")
            #     or fn.check_package_installed("arcolinux-mirrorlist-git")
            # ):
            #     self.btnRepos = Gtk.Button(label="Add ArcoLinux Repo")
            #     self.btnRepos._value = 1
            # else:
            #     self.btnRepos = Gtk.Button(label="Remove ArcoLinux Repo")
            #     self.btnRepos._value = 2
            #
            # self.btnRepos.set_size_request(100, 30)
            # self.btnRepos.connect("clicked", self.on_repos_clicked)

            # =====================================================
            #               QUIT BUTTON
            # =====================================================

            btn_quit_app = Gtk.Button(label="Quit")
            btn_quit_app.set_size_request(100, 30)
            btn_quit_app.connect("clicked", self.on_close, "delete-event")
            btn_context = btn_quit_app.get_style_context()
            btn_context.add_class("destructive-action")

            # =====================================================
            #               SEARCH BOX
            # =====================================================

            self.searchentry = Gtk.SearchEntry()
            self.searchentry.set_text(search_term)
            self.searchentry.connect("activate", self.on_search_activated)
            self.searchentry.connect("icon-release", self.on_search_cleared)

            iv_searchbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)

            # =====================================================
            #                      PACKS
            # =====================================================

            # hbox1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=2)
            # hbox2 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=2)
            # hbox3 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=2)

            # hbox3.pack_start(btnReCache, False, False, 0)

            iv_searchbox.pack_start(self.searchentry, False, False, 0)

            ivbox.pack_start(image, False, False, 0)
            ivbox.pack_start(iv_searchbox, False, False, 0)
            ivbox.pack_start(stack_switcher, True, True, 0)

            ivbox.pack_start(btn_quit_app, False, False, 0)

            vbox1.pack_start(hbox0, False, False, 0)
            vbox1.pack_start(stack, True, True, 0)

            hbox.pack_start(ivbox, False, True, 0)
            hbox.pack_start(vbox1, True, True, 0)

            stack.set_hhomogeneous(False)
            stack.set_vhomogeneous(False)

            self.show_all()

        except Exception as err:
            fn.logger.error("Exception in GUISearch(): %s" % err)

    def setup_gui(self, Gtk, Gdk, GdkPixbuf, base_dir, os, Pango, settings):  # noqa
        try:
            # reset back to main box
            if self.search_activated:
                # remove the search vbox
                self.remove(self.vbox_search)
                self.show_all()

            # lets quickly create the latest installed list.
            fn.get_current_installed()

            # =======================================================
            #                       HeaderBar
            # =======================================================

            setup_headerbar(self, Gtk, settings)

            # =======================================================
            #                       App Notifications
            # =======================================================

            hbox0 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)

            self.notification_revealer = Gtk.Revealer()
            self.notification_revealer.set_reveal_child(False)

            self.notification_label = Gtk.Label()

            pb_panel = GdkPixbuf.Pixbuf().new_from_file(base_dir + "/images/panel.png")
            panel = Gtk.Image().new_from_pixbuf(pb_panel)

            overlay_frame = Gtk.Overlay()
            overlay_frame.add(panel)
            overlay_frame.add_overlay(self.notification_label)

            self.notification_revealer.add(overlay_frame)

            hbox0.pack_start(self.notification_revealer, True, False, 0)

            # ==========================================================
            #                       CONTAINER
            # ==========================================================

            self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
            vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
            hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)

            self.vbox.pack_start(hbox, True, True, 0)
            self.add(self.vbox)

            # ==========================================================
            #                    PREP WORK
            # ==========================================================

            # This section sets up the tabs, and the array for dealing with the tab content
            """
            yaml_files_unsorted = []
            path = base_dir + "/yaml/"
            for file in os.listdir(path):
                if file.endswith(".yaml"):
                    yaml_files_unsorted.append(file)
                else:
                    print(
                        "Unsupported configuration file type. Please contact Arcolinux Support."
                    )
            # Need to sort the list (Or do we? I choose to)
            yaml_files = sorted(yaml_files_unsorted)
            """

            # Check github for updated files
            # fn.check_github(yaml_files)
            # ==========================================================
            #                       GENERATE STACK
            # ==========================================================
            stack = Gtk.Stack()
            # stack.set_transition_type(Gtk.StackTransitionType.SLIDE_UP_DOWN)
            stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
            stack.set_transition_duration(350)

            vbox_stack = []
            stack_item = 0

            # Max Threads
            """
                Fatal Python error: Segmentation fault
                This error happens randomly, due to the for loop iteration on the cpu_count
                old code: for x in range(cpu_count()):
            """

            # spawn only 1 GUI_Worker threads, as any number greater causes a Segmentation fault

            worker = GUI_Worker(self.queue)
            worker.name = "thread_GUI_Worker"
            # Set the worker to be True to allow processing, and avoid Blocking
            # worker.daemon = True
            worker.start()

            for category in self.packages:
                # NOTE: IF the yaml file name standard changes, be sure to update this, or weirdness will follow.

                # this is the side stack listing all categories
                vbox_stack.append(
                    Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
                )
                stack.add_titled(
                    vbox_stack[stack_item],
                    str("stack" + str(len(vbox_stack))),
                    category,
                )

                packages_lst = self.packages[category]

                # Multithreading!
                self.queue.put(
                    (
                        self,
                        Gtk,
                        vbox_stack[stack_item],
                        category,
                        packages_lst,
                    )
                )
                stack_item += 1

            # send a signal that no further items are to be put on the queue
            self.queue.put(None)
            # safety to ensure that we finish threading before we continue on.

            self.queue.join()
            fn.logger.debug("GUI Worker thread completed")

            stack_switcher = Gtk.StackSidebar()
            stack_switcher.set_name("sidebar")
            stack_switcher.set_stack(stack)

            # =====================================================
            #                       LOGO
            # =====================================================

            ivbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
            pixbuf = GdkPixbuf.Pixbuf().new_from_file_at_size(
                os.path.join(base_dir, "images/sofirem.png"), 45, 45
            )
            image = Gtk.Image().new_from_pixbuf(pixbuf)

            # remove the focus on startup from search entry
            ivbox.set_property("can-focus", True)
            Gtk.Window.grab_focus(ivbox)

            # =====================================================
            #               RECACHE BUTTON
            # =====================================================

            # btnReCache = Gtk.Button(label="Recache Applications")
            # btnReCache.connect("clicked", self.recache_clicked)
            # btnReCache.set_property("has-tooltip", True)
            # btnReCache.connect("query-tooltip", self.tooltip_callback,
            #           "Refresh the application cache")

            # =====================================================
            #                   REPOS
            # =====================================================

            # =====================================================
            #               QUIT BUTTON
            # =====================================================
            btn_quit_app = Gtk.Button(label="Quit")
            btn_quit_app.set_size_request(100, 30)
            btn_quit_app.connect("clicked", self.on_close, "delete-event")
            btn_context = btn_quit_app.get_style_context()
            btn_context.add_class("destructive-action")
            # =====================================================
            #               SEARCH BOX
            # =====================================================
            self.searchentry = Gtk.SearchEntry()
            self.searchentry.set_placeholder_text("Search...")
            self.searchentry.connect("activate", self.on_search_activated)
            self.searchentry.connect("icon-release", self.on_search_cleared)

            ivsearchbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)

            ivsearchbox.pack_start(self.searchentry, False, False, 0)

            ivbox.pack_start(image, False, False, 0)
            ivbox.pack_start(ivsearchbox, False, False, 0)
            ivbox.pack_start(stack_switcher, True, True, 0)
            ivbox.pack_start(btn_quit_app, False, False, 0)

            vbox1.pack_start(hbox0, False, False, 0)
            vbox1.pack_start(stack, True, True, 0)

            hbox.pack_start(ivbox, False, True, 0)
            hbox.pack_start(vbox1, True, True, 0)

            stack.set_hhomogeneous(False)
            stack.set_vhomogeneous(False)

            if self.search_activated:
                self.show_all()

        except Exception as e:
            fn.logger.error("Exception in GUI(): %s" % e)


# setup headerbar including popover settings
def setup_headerbar(self, Gtk, settings):
    try:
        header_bar_title = "Sofirem"
        headerbar = Gtk.HeaderBar()
        headerbar.set_title(header_bar_title)
        headerbar.set_show_close_button(True)

        self.set_titlebar(headerbar)

        toolbutton = Gtk.ToolButton()
        # icon-name open-menu-symbolic / open-menu-symbolic.symbolic
        toolbutton.set_icon_name("open-menu-symbolic")

        toolbutton.connect("clicked", self.on_settings_clicked)

        headerbar.pack_end(toolbutton)

        self.popover = Gtk.Popover()
        self.popover.set_relative_to(toolbutton)

        vbox = Gtk.Box(spacing=0, orientation=Gtk.Orientation.VERTICAL)
        vbox.set_border_width(15)

        # switches

        # switch to display package versions
        self.switch_package_version = Gtk.Switch()

        if settings != None:
            if settings["Display Package Versions"]:
                self.display_versions = settings["Display Package Versions"]

        if self.display_versions == True:
            self.switch_package_version.set_active(True)
        else:
            self.switch_package_version.set_active(False)

        self.switch_package_version.connect("notify::active", self.version_toggle)

        # switch to import arcolinux keyring
        self.switch_arco_keyring = Gtk.Switch()

        if (
            fn.check_package_installed("arcolinux-keyring") is False
            or fn.verify_arco_pacman_conf() is False
        ):
            self.switch_arco_keyring.set_state(False)

        else:
            self.switch_arco_keyring.set_state(True)

        self.switch_arco_keyring.connect("state-set", self.arco_keyring_toggle)

        # switch to import arcolinix mirrorlist
        self.switch_arco_mirrorlist = Gtk.Switch()

        if (
            fn.check_package_installed("arcolinux-mirrorlist-git") is False
            or fn.verify_arco_pacman_conf() is False
        ):
            self.switch_arco_mirrorlist.set_state(False)

        else:
            self.switch_arco_mirrorlist.set_state(True)

        self.switch_arco_mirrorlist.connect("state-set", self.arco_mirrorlist_toggle)

        # switch to display package progress window
        self.switch_package_progress = Gtk.Switch()

        if settings != None:
            if settings["Display Package Progress"]:
                self.display_package_progress = settings["Display Package Progress"]

        if self.display_package_progress == True:
            self.switch_package_progress.set_active(True)
        else:
            self.switch_package_progress.set_active(False)
        self.switch_package_progress.connect(
            "notify::active", self.package_progress_toggle
        )

        # modalbuttons

        # button to open the pacman log monitoring dialog
        self.modelbtn_pacmanlog = Gtk.ModelButton()
        self.modelbtn_pacmanlog.connect("clicked", self.on_pacman_log_clicked)
        self.modelbtn_pacmanlog.set_name("modelbtn_popover")
        self.modelbtn_pacmanlog.props.centered = False
        self.modelbtn_pacmanlog.props.text = "Open Pacman Log File"

        # button to display installed packages window
        modelbtn_packages_export = Gtk.ModelButton()
        modelbtn_packages_export.connect("clicked", self.on_packages_export_clicked)
        modelbtn_packages_export.set_name("modelbtn_popover")
        modelbtn_packages_export.props.centered = False
        modelbtn_packages_export.props.text = "Show Installed Packages"

        # button to display import packages window
        modelbtn_packages_import = Gtk.ModelButton()
        modelbtn_packages_import.connect("clicked", self.on_packages_import_clicked)
        modelbtn_packages_import.set_name("modelbtn_popover")
        modelbtn_packages_import.props.centered = False
        modelbtn_packages_import.props.text = "Import Packages"

        # button to show about dialog
        modelbtn_about_app = Gtk.ModelButton()
        modelbtn_about_app.connect("clicked", self.on_about_app_clicked)
        modelbtn_about_app.set_name("modelbtn_popover")
        modelbtn_about_app.props.centered = False
        modelbtn_about_app.props.text = "About Sofirem"

        # button to show iso package lists window
        modelbtn_iso_packages_list = Gtk.ModelButton()
        modelbtn_iso_packages_list.connect(
            "clicked", self.on_arcolinux_iso_packages_clicked
        )
        modelbtn_iso_packages_list.set_name("modelbtn_popover")
        modelbtn_iso_packages_list.props.centered = False
        modelbtn_iso_packages_list.props.text = "Explore ArcoLinux ISO Packages"

        # button to show package search window
        modelbtn_package_search = Gtk.ModelButton()
        modelbtn_package_search.connect("clicked", self.on_package_search_clicked)
        modelbtn_package_search.set_name("modelbtn_popover")
        modelbtn_package_search.props.centered = False
        modelbtn_package_search.props.text = "Open Package Search"

        # grid for the switch options
        grid_switches = Gtk.Grid()
        grid_switches.set_row_homogeneous(True)

        lbl_package_version = Gtk.Label(xalign=0)
        lbl_package_version.set_text("Display Package Versions")

        lbl_package_version_padding = Gtk.Label(xalign=0)
        lbl_package_version_padding.set_text("  ")

        lbl_package_progress = Gtk.Label(xalign=0)
        lbl_package_progress.set_text("Display Package Progress")

        lbl_package_progress_padding = Gtk.Label(xalign=0)
        lbl_package_progress_padding.set_text("  ")

        lbl_arco_keyring = Gtk.Label(xalign=0)
        lbl_arco_keyring.set_text("Import ArcoLinux Keyring")

        lbl_arco_keyring_padding = Gtk.Label(xalign=0)
        lbl_arco_keyring_padding.set_text("  ")

        lbl_arco_mirrorlist = Gtk.Label(xalign=0)
        lbl_arco_mirrorlist.set_text("Import ArcoLinux Mirrorlist")

        lbl_arco_mirrorlist_padding = Gtk.Label(xalign=0)
        lbl_arco_mirrorlist_padding.set_text("  ")

        grid_switches.attach(lbl_package_version, 0, 1, 1, 1)
        grid_switches.attach_next_to(
            lbl_package_version_padding,
            lbl_package_version,
            Gtk.PositionType.RIGHT,
            1,
            1,
        )

        grid_switches.attach_next_to(
            self.switch_package_version,
            lbl_package_version_padding,
            Gtk.PositionType.RIGHT,
            1,
            1,
        )

        grid_switches.attach(lbl_package_progress, 0, 2, 1, 1)
        grid_switches.attach_next_to(
            lbl_package_progress_padding,
            lbl_package_progress,
            Gtk.PositionType.RIGHT,
            1,
            1,
        )

        grid_switches.attach_next_to(
            self.switch_package_progress,
            lbl_package_progress_padding,
            Gtk.PositionType.RIGHT,
            1,
            1,
        )

        grid_switches.attach(lbl_arco_keyring, 0, 3, 1, 1)
        grid_switches.attach_next_to(
            lbl_arco_keyring_padding,
            lbl_arco_keyring,
            Gtk.PositionType.RIGHT,
            1,
            1,
        )

        grid_switches.attach_next_to(
            self.switch_arco_keyring,
            lbl_arco_keyring_padding,
            Gtk.PositionType.RIGHT,
            1,
            1,
        )

        grid_switches.attach(lbl_arco_mirrorlist, 0, 4, 1, 1)
        grid_switches.attach_next_to(
            lbl_arco_mirrorlist_padding,
            lbl_arco_mirrorlist,
            Gtk.PositionType.RIGHT,
            1,
            1,
        )

        grid_switches.attach_next_to(
            self.switch_arco_mirrorlist,
            lbl_arco_mirrorlist_padding,
            Gtk.PositionType.RIGHT,
            1,
            1,
        )

        vbox_buttons = Gtk.Box(spacing=1, orientation=Gtk.Orientation.VERTICAL)
        vbox_buttons.pack_start(self.modelbtn_pacmanlog, False, True, 0)
        vbox_buttons.pack_start(modelbtn_packages_export, False, True, 0)
        vbox_buttons.pack_start(modelbtn_packages_import, False, True, 0)
        vbox_buttons.pack_start(modelbtn_iso_packages_list, False, True, 0)
        vbox_buttons.pack_start(modelbtn_package_search, False, True, 0)
        vbox_buttons.pack_start(modelbtn_about_app, False, True, 0)

        vbox.pack_start(grid_switches, False, False, 0)
        vbox.pack_start(vbox_buttons, False, False, 0)

        self.popover.add(vbox)
        self.popover.set_position(Gtk.PositionType.BOTTOM)
    except Exception as e:
        fn.logger.error("Exception in setup_headerbar(): %s" % e)
