"""
Display number of scratchpad windows and urgency hints.

Configuration parameters:
    cache_timeout: refresh interval for i3-msg or swaymsg (default 5)
    format: display format for this module
        (default "\u232b [\\?color=scratchpad {scratchpad}]")
    thresholds: specify color thresholds to use
        (default [(0, "darkgray"), (1, "violet")])

Format placeholders:
    {scratchpad} number of scratchpads
    {urgent} number of urgent scratchpads

Color thresholds:
    xxx: print a color based on the value of `xxx` placeholder

Optional:
    i3ipc: an improved python library to control i3wm and sway

Examples:
```
# hide zero scratchpad
scratchpad {
    format = '[\\?not_zero \u232b [\\?color=scratchpad {scratchpad}]]'
}

# hide non-urgent scratchpad
scratchpad {
    format = '[\\?not_zero \u232b {urgent}]'
}

# bring up scratchpads on clicks
scratchpad {
    on_click 1 = 'scratchpad show'
}

# add more colors
scratchpad {
    thresholds = [
       (0, "darkgray"), (1, "violet"), (2, "deepskyblue"), (3, "lime"),
       (4, "yellow"), (5, "orange"), (6, "red"), (7, "tomato"),
    ]
}
```

@author shadowprince (counter), cornerman (async)
@license Eclipse Public License (counter), BSD (async)

SAMPLE OUTPUT
[{'full_text': '\u232b '}, {'full_text': u'0', 'color': '#a9a9a9'}]

violet
[{'full_text': '\u232b '}, {'full_text': u'5', 'color': '#ee82ee'}]

urgent
[{'full_text': '\u232b URGENT 1', 'urgent': True}]
"""

STRING_ERROR = "invalid ipc `{}`"


class Ipc:
    """ """

    def __init__(self, parent):
        self.parent = parent
        self.setup(parent)


class I3ipc(Ipc):
    """
    i3ipc - an improved python library to control i3wm and sway
    """

    def setup(self, parent):
        from threading import Thread

        self.parent.cache_timeout = self.parent.py3.CACHE_FOREVER
        self.scratchpad_data = {"scratchpad": 0, "urgent": 0}

        t = Thread(target=self.start)
        t.daemon = True
        t.start()

    def start(self):
        from i3ipc import Connection

        i3 = Connection()
        self.update(i3)
        for event in ["window::move", "window::urgent"]:
            i3.on(event, self.update)
        i3.main()

    def update(self, i3, event=None):
        scratchpad = i3.get_tree().scratchpad()
        if not scratchpad:
            return

        # Workaround for I3ipc 2.2.1 not finding leaves() in sway. Fixing: #2038
        leaves = getattr(scratchpad, "floating_nodes", [])

        temporary = {
            "ipc": self.parent.ipc,
            "scratchpad": len(leaves),
            "urgent": sum(window.urgent for window in leaves),
        }
        if self.scratchpad_data != temporary:
            self.scratchpad_data = temporary
            self.parent.py3.update()

    def get_scratchpad_data(self):
        return self.scratchpad_data


class Msg(Ipc):
    """
    i3-msg - send messages to i3 window manager
    swaymsg - send messages to sway window manager
    """

    def setup(self, parent):
        from json import loads

        self.json_loads = loads
        wm_msg = {"i3msg": "i3-msg"}.get(parent.ipc, parent.ipc)
        self.tree_command = [wm_msg, "-t", "get_tree"]

    def get_scratchpad_data(self):
        tree = self.json_loads(self.parent.py3.command_output(self.tree_command))
        leaves = self.find_scratchpad(tree).get("floating_nodes", [])
        return {
            "ipc": self.parent.ipc,
            "scratchpad": len(leaves),
            "urgent": sum(window["urgent"] for window in leaves),
        }

    def find_scratchpad(self, tree):
        if tree.get("name") == "__i3_scratch":
            return tree
        for x in tree.get("nodes", []):
            result = self.find_scratchpad(x)
            if result:
                return result
        return {}


class Py3status:
    """ """

    # available configuration parameters
    cache_timeout = 5
    format = "\u232b [\\?color=scratchpad {scratchpad}]"
    thresholds = [(0, "darkgray"), (1, "violet")]

    def post_config_hook(self):
        # ipc: specify i3ipc, i3-msg, or swaymsg, otherwise auto
        self.ipc = getattr(self, "ipc", "")
        if self.ipc in ["", "i3ipc"]:
            try:
                from i3ipc import Connection  # noqa f401

                self.ipc = "i3ipc"
            except Exception:
                if self.ipc:
                    raise  # module not found

        self.ipc = (self.ipc or self.py3.get_wm_msg()).replace("-", "")
        if self.ipc in ["i3ipc"]:
            self.backend = I3ipc(self)
        elif self.ipc in ["i3msg", "swaymsg"]:
            self.backend = Msg(self)
        else:
            raise Exception(STRING_ERROR.format(self.ipc))

        self.thresholds_init = self.py3.get_color_names_list(self.format)

    def scratchpad(self):
        scratchpad_data = self.backend.get_scratchpad_data()

        for x in self.thresholds_init:
            if x in scratchpad_data:
                self.py3.threshold_get_color(scratchpad_data[x], x)

        response = {
            "cached_until": self.py3.time_in(self.cache_timeout),
            "full_text": self.py3.safe_format(self.format, scratchpad_data),
        }
        if scratchpad_data["urgent"]:
            response["urgent"] = True
        return response


if __name__ == "__main__":
    """
    Run module in test mode.
    """
    from py3status.module_test import module_test

    config = {"format": r"\[{ipc}\] [\?color=scratchpad {scratchpad}]"}
    module_test(Py3status, config=config)
