import wx

from clarabot_nano.common.core.message import MSG_TYPE_REQUEST_CONFIG_UPDATE
from clarabot_nano.control.client import WxCoreInterface
from clarabot_nano.control.configure.drive.attach import DriveAttachDialog
from clarabot_nano.control.icon import get_wx_nano_icon
from clarabot_nano.control.utility import Slots
from clarabot_nano.utility.serialize import msgpack_dumps


class DriveConfigManager(wx.Dialog, Slots):
    def __init__(self, core_interface: WxCoreInterface, parent):
        self.core_interface = core_interface
        wx.Dialog.__init__(self, parent, wx.ID_ANY, gettext("Drive configuration manager"))
        Slots.__init__(self)
        self.SetSizeHints(500, 400)
        self.SetIcon(get_wx_nano_icon())
        self._displayed_drives = None

        layout = wx.BoxSizer(wx.VERTICAL)

        buttons_layout = wx.BoxSizer(wx.HORIZONTAL)
        layout.Add(buttons_layout, 0, wx.ALL | wx.EXPAND, 8)

        self.create_drive = wx.Button(self, wx.ID_ANY, gettext("Create drive"))
        buttons_layout.Add(self.create_drive, 0, wx.RIGHT | wx.EXPAND, 8)
        # self.delete_drive = wx.Button(self, wx.ID_ANY, gettext("Delete selected drive"))
        # buttons_layout.Add(self.delete_drive, 0, wx.RIGHT | wx.EXPAND, 8)

        self.tree = wx.TreeCtrl(
            self, wx.ID_ANY, style=wx.TR_NO_BUTTONS | wx.TR_HIDE_ROOT | wx.TR_SINGLE | wx.TR_NO_LINES
        )
        self.tree_root = self.tree.AddRoot("Nano")
        layout.Add(self.tree, 1, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 8)

        self.SetSizer(layout)

        self._delete_drive_id = wx.NewEventType()
        self._detach_room_id = wx.NewEventType()
        self._attach_id = wx.NewEventType()

        self.Bind(wx.EVT_TREE_ITEM_MENU, self._on_item_menu, self.tree)
        self.Bind(wx.EVT_MENU, self._on_delete_drive, id=self._delete_drive_id)
        self.Bind(wx.EVT_MENU, self._on_detach_room, id=self._detach_room_id)
        self.Bind(wx.EVT_MENU, self._on_attach, id=self._attach_id)
        self.Bind(wx.EVT_BUTTON, self._on_create_drive, self.create_drive)

        self.bind_signal(core_interface.config_changed, self._on_config_changed)
        self._on_config_changed(self.core_interface.get_config())

    def _on_config_changed(self, config):
        drives = config["remote"]["drives"]
        if drives != self._displayed_drives:
            self._update_tree(drives)

    def _update_tree(self, drives):
        # NOTE: incremental tree updates are not simple to do, so we'll always rebuild the view... for now
        # save view
        if (selected := self.tree.GetSelection()) and selected.IsOk():
            selected = self.tree.GetItemText(selected)
        else:
            selected = ""
        scroll_thumb = self.tree.GetScrollThumb(wx.VERTICAL)

        def _append_select(parent, text, data):
            item = self.tree.AppendItem(parent, text, data=data)
            if selected and text == selected:
                self.tree.SelectItem(item)
            return item

        # reset view
        self.tree.DeleteChildren(self.tree_root)
        for drive in drives:
            drive_item = _append_select(self.tree_root, drive["path"], 0)
            for room in drive.get("rooms", ()):
                _append_select(drive_item, room, 1)
        # restore view
        self.tree.ExpandAll()
        self.tree.SetScrollPos(wx.VERTICAL, scroll_thumb)
        self._displayed_drives = drives
        self.tree.Update()

    def _on_item_menu(self, event):
        if (item := event.GetItem()) and item.IsOk():
            menu = wx.Menu()
            try:
                if self.tree.GetItemData(item) == 0:  # drive menu
                    menu.Append(self._attach_id, gettext("Attach room to drive"))
                    menu.AppendSeparator()
                    menu.Append(self._delete_drive_id, gettext("Delete drive"))
                    self.tree.PopupMenu(menu, event.GetPoint())
                else:  # room menu
                    menu.Append(self._detach_room_id, gettext("Detach room from drive"))
                    self.tree.PopupMenu(menu, event.GetPoint())
            finally:
                menu.Destroy()

    def _on_delete_drive(self, *args):
        self._delete_drive(self.tree.GetItemText(self.tree.GetSelection()))

    def _on_detach_room(self, *args):
        item = self.tree.GetSelection()
        self._detach_room(self.tree.GetItemText(self.tree.GetItemParent(item)), self.tree.GetItemText(item))

    def _on_attach(self, *args):
        self._attach(self.tree.GetItemText(self.tree.GetSelection()))

    def _delete_drive(self, drive_path):
        self.core_interface.core_request(
            [MSG_TYPE_REQUEST_CONFIG_UPDATE, msgpack_dumps({"_handler_key": "drive:delete", "path": drive_path})]
        )

    def _detach_room(self, drive_path, room_id):
        self.core_interface.core_request(
            [
                MSG_TYPE_REQUEST_CONFIG_UPDATE,
                msgpack_dumps({"_handler_key": "drive:detach-room", "path": drive_path, "room_id": room_id}),
            ]
        )

    def _attach(self, drive_path):
        with DriveAttachDialog(self) as d:
            if d.ShowModal() != wx.ID_OK:
                return
            room_id, block_names = d.get_data()
        self.core_interface.core_request(
            [
                MSG_TYPE_REQUEST_CONFIG_UPDATE,
                msgpack_dumps(
                    {
                        "_handler_key": "drive:attach-room",
                        "path": drive_path,
                        "room_id": room_id,
                        "room_blocks": block_names,
                    }
                ),
            ]
        )

    def _on_create_drive(self, *args):
        if path := wx.DirSelector(
            gettext("Select the root for the new drive"), style=wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST, parent=self
        ):
            self._do_create_drive(path)

    def _do_create_drive(self, path):
        self.core_interface.core_request(
            [MSG_TYPE_REQUEST_CONFIG_UPDATE, msgpack_dumps({"_handler_key": "drive:create", "path": path})]
        )
