#!/usr/bin/env python3

from gi.repository import Gtk, Gdk, GLib
from i3ipc import Event

import nwg_panel.common
from nwg_panel.tools import check_key, get_icon_name, update_image, update_image_fallback_desktop, load_autotiling


class SwayWorkspaces(Gtk.Box):
    def __init__(self, settings, i3, icons_path):
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
        self.settings = settings
        self.i3 = i3
        self.num_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
        self.num_box.set_property("name", "sway-workspaces")
        self.ws_num2box = {}
        self.ws_num2lbl = {}
        self.name_label = Gtk.Label()
        self.name_label.set_property("name", "sway-workspaces-name")
        self.win_id = ""
        self.win_pid = None
        self.icon = Gtk.Image()
        self.icon.set_property("name", "sway-workspaces-icon")
        self.layout_icon = Gtk.Image()
        self.icons_path = icons_path
        self.autotiling = load_autotiling()
        self.build_box()
        self.refresh()
        self.subscribe()

    def subscribe(self):
        self.i3.on(Event.WINDOW, self.on_i3ipc_event)
        self.i3.on(Event.WORKSPACE, self.on_i3ipc_event)

    def build_box(self):
        check_key(self.settings, "numbers", [])
        check_key(self.settings, "custom-labels", [])
        check_key(self.settings, "focused-labels", [])
        check_key(self.settings, "show-icon", True)
        check_key(self.settings, "image-size", 16)
        check_key(self.settings, "show-name", True)
        check_key(self.settings, "name-length", 40)
        check_key(self.settings, "mark-autotiling", True)
        check_key(self.settings, "mark-content", True)
        check_key(self.settings, "hide-empty", False)
        check_key(self.settings, "show-layout", True)
        check_key(self.settings, "angle", 0.0)
        if self.settings["angle"] != 0.0:
            self.set_orientation(Gtk.Orientation.VERTICAL)
            self.num_box.set_orientation(Gtk.Orientation.VERTICAL)

        # prevent from #142
        ws_num = -1
        if self.i3.get_tree().find_focused():
            ws_num, win_name, win_id, non_empty, win_layout, numbers = self.find_details()

        if len(self.settings["custom-labels"]) == 1:
            self.settings["custom-labels"] *= len(self.settings["numbers"])
        elif len(self.settings["custom-labels"]) != len(self.settings["numbers"]):
            self.settings["custom-labels"] = []

        if len(self.settings["focused-labels"]) == 1:
            self.settings["focused-labels"] *= len(self.settings["numbers"])
        elif len(self.settings["focused-labels"]) != len(self.settings["numbers"]):
            self.settings["focused-labels"] = []

        self.pack_start(self.num_box, False, False, 0)

        for idx, num in enumerate(self.settings["numbers"]):
            if num == str(ws_num) and self.settings["focused-labels"]:
                label = self.settings["focused-labels"][idx]
            elif self.settings["custom-labels"]:
                label = self.settings["custom-labels"][idx]
            else:
                label = str(num)

            eb, lbl = self.build_number(num, label)
            self.num_box.pack_start(eb, False, False, 0)

            if num == str(ws_num):
                eb.set_property("name", "task-box-focused")
            else:
                eb.set_property("name", "task-box")

        if self.settings["show-icon"]:
            self.pack_start(self.icon, False, False, 6)

        if self.settings["show-name"]:
            self.pack_start(self.name_label, False, False, 0)

        if self.settings["show-layout"]:
            self.pack_start(self.layout_icon, False, False, 6)

    def build_number(self, num, label):
        eb = Gtk.EventBox()
        eb.connect("enter_notify_event", self.on_enter_notify_event)
        eb.connect("leave_notify_event", self.on_leave_notify_event)
        eb.connect("button-release-event", self.on_click, num)
        eb.add_events(Gdk.EventMask.SCROLL_MASK)
        eb.connect('scroll-event', self.on_scroll)

        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
        box.set_property("name", "sway-workspaces-item")
        if self.settings["angle"] != 0.0:
            box.set_orientation(Gtk.Orientation.VERTICAL)
        eb.add(box)

        if self.settings["mark-autotiling"]:
            try:
                at = int(num) in self.autotiling
            except:
                at = False
            autotiling = "a" if at in self.autotiling else ""
            lbl = Gtk.Label("{}{}".format(autotiling, label))
        else:
            lbl = Gtk.Label("{}".format(label))
        lbl.set_use_markup(True)
        if self.settings["angle"] != 0.0:
            lbl.set_angle(self.settings["angle"])
            self.name_label.set_angle(self.settings["angle"])

        self.ws_num2box[num] = eb
        self.ws_num2lbl[num] = lbl

        box.pack_start(lbl, False, False, 6)

        return eb, lbl

    def on_i3ipc_event(self, i3conn, event):
        GLib.idle_add(self.refresh, priority=GLib.PRIORITY_HIGH)

    def refresh(self):
        if self.i3.get_tree().find_focused():
            ws_num, win_name, win_id, non_empty, win_layout, numbers = self.find_details()

            if len(self.settings["numbers"]) > 0:
                numbers = self.settings["numbers"]

            if ws_num > 0:
                for num in self.ws_num2lbl:
                    self.ws_num2lbl[num].hide()

                for _idx, num in enumerate(numbers):
                    idx = None
                    if num in self.settings["numbers"]:
                        idx = self.settings["numbers"].index(num)
                    try:
                        int_num = int(num)
                    except:
                        int_num = 0

                    if idx is None:
                        text = str(num)
                    elif num == str(ws_num) and self.settings["focused-labels"]:
                        text = self.settings["focused-labels"][idx]
                    elif self.settings["custom-labels"]:
                        text = self.settings["custom-labels"][idx]
                    else:
                        text = str(num)

                    if num not in self.ws_num2lbl:
                        eb, lbl = self.build_number(num, text)
                        self.num_box.pack_start(eb, False, False, 0)
                        eb.show_all()

                    lbl = self.ws_num2lbl[num]

                    if not self.settings["hide-empty"] or int_num in non_empty or num == str(ws_num):
                        lbl.show()
                    else:
                        lbl.hide()

                    # mark non-empty WS with CSS ID
                    if int_num in non_empty:
                        lbl.set_property("name", "workspace-occupied")

                    # mark non-empty WS with a dot
                    if self.settings["mark-content"]:
                        if int_num in non_empty:
                            if not text.endswith("."):
                                text += "."
                        else:
                            if text.endswith("."):
                                text = text[0:-1]

                    lbl.set_markup(text)

                    if num == str(ws_num):
                        self.ws_num2box[num].set_property("name", "task-box-focused")
                    else:
                        self.ws_num2box[num].set_property("name", "task-box")

                if self.settings["show-icon"] and win_id != self.win_id:
                    self.update_icon(win_id, win_name)
                    self.win_id = win_id

            if self.settings["show-name"]:
                self.name_label.set_text(win_name)

            if self.settings["show-layout"]:
                if win_name:
                    if win_layout == "splith":
                        update_image(self.layout_icon, "go-next-symbolic", self.settings["image-size"], self.icons_path)
                    elif win_layout == "splitv":
                        update_image(self.layout_icon, "go-down-symbolic", self.settings["image-size"], self.icons_path)
                    elif win_layout == "tabbed":
                        update_image(self.layout_icon, "view-dual-symbolic", self.settings["image-size"],
                                     self.icons_path)
                    elif win_layout == "stacked":
                        update_image(self.layout_icon, "view-paged-symbolic", self.settings["image-size"],
                                     self.icons_path)
                    else:
                        update_image(self.layout_icon, "window-pop-out-symbolic", self.settings["image-size"],
                                     self.icons_path)

                    if not self.layout_icon.get_visible():
                        self.layout_icon.show()
                else:
                    if self.layout_icon.get_visible():
                        self.layout_icon.hide()

    def update_icon(self, win_id, win_name):
        loaded_icon = False
        if win_id and win_name:
            try:
                update_image_fallback_desktop(self.icon,
                                              win_id,
                                              self.settings["image-size"],
                                              self.icons_path,
                                              fallback=False)
                loaded_icon = True
                if not self.icon.get_visible():
                    self.icon.show()
            except:
                pass

        if not loaded_icon and self.icon.get_visible():
            self.icon.hide()

    def find_details(self):
        tree = self.i3.get_tree()
        workspaces = self.i3.get_workspaces()
        ws_num = -1
        win_name = ""
        win_id = ""  # app_id if available, else window_class
        layout = None
        numbers = []

        for ws in workspaces:
            numbers.append(str(ws.num))
            if ws.focused:
                ws_num = ws.num

        non_empty = []
        if self.settings["show-name"] or self.settings["show-icon"]:
            f = self.i3.get_tree().find_focused()
            if f.type == "con" and f.name and str(f.parent.workspace().num) in self.settings["numbers"]:
                win_name = f.name[:self.settings["name-length"]]

                if f.app_id:
                    win_id = f.app_id
                elif f.window_class:
                    win_id = f.window_class

            for item in tree.descendants():
                if item.type == "workspace":
                    # find non-empty workspaces
                    # if self.settings["mark-content"] or self.settings["hide-empty"]:
                    tasks_num = 0
                    for d in item.descendants():
                        if d.type == "con" and d.name:
                            tasks_num += 1
                    if tasks_num > 0:
                        non_empty.append(item.num)

                    for node in item.floating_nodes:
                        if str(node.workspace().num) in self.settings["numbers"]:
                            if node.focused and node.name:
                                win_name = node.name[:self.settings["name-length"] - 1]

                                if node.app_id:
                                    win_id = node.app_id
                                elif node.window_class:
                                    win_id = node.window_class
                                layout = "floating"

                            non_empty.append(node.workspace().num)

            if not layout:
                layout = f.parent.layout

        return ws_num, win_name, win_id, non_empty, layout, numbers

    def on_click(self, event_box, event_button, num):
        nwg_panel.common.i3.command("workspace number {}".format(num))

    def on_scroll(self, event_box, event):
        if event.direction == Gdk.ScrollDirection.UP:
            nwg_panel.common.i3.command("workspace prev")
        elif event.direction == Gdk.ScrollDirection.DOWN:
            nwg_panel.common.i3.command("workspace next")

    def on_enter_notify_event(self, widget, event):
        widget.set_state_flags(Gtk.StateFlags.DROP_ACTIVE, clear=False)
        widget.set_state_flags(Gtk.StateFlags.SELECTED, clear=False)

    def on_leave_notify_event(self, widget, event):
        widget.unset_state_flags(Gtk.StateFlags.DROP_ACTIVE)
        widget.unset_state_flags(Gtk.StateFlags.SELECTED)
