--
-- KeyBinding Dialog Widget.
-- @copyright Jefferson Gonzalez
-- @license MIT
--

local keymap = require "core.keymap"
local style = require "core.style"
local Button = require "widget.button"
local Dialog = require "widget.dialog"
local Label = require "widget.label"
local Line = require "widget.line"
local ListBox = require "widget.listbox"
local MessageBox = require "widget.messagebox"

---@type widget.keybinddialog
local current_dialog = nil

---@class widget.keybinddialog : widget.dialog
---@overload fun():widget.keybinddialog
---@field super widget.dialog
---@field selected integer
---@field shortcuts widget.listbox
---@field add widget.button
---@field remove widget.button
---@field line widget.line
---@field message widget.label
---@field binding widget.label
---@field mouse_intercept widget.label
---@field save widget.button
---@field reset widget.button
---@field cancel widget.button
local KeybindDialog = Dialog:extend()

---Constructor
function KeybindDialog:new()
  KeybindDialog.super.new(self, "Keybinding Selector")

  self.type_name = "widget.keybinddialog"

  self.selected = nil

  local this = self

  self.shortcuts = ListBox(self.panel)
  self.shortcuts:set_size(100, 100)
  function self.shortcuts:on_row_click(idx, data)
    this.selected = idx
  end

  self.add = Button(self.panel, "Add")
  self.add:set_icon("]")
  function self.add:on_click()
    this.shortcuts:add_row({"none"})
    this.shortcuts:set_selected(#this.shortcuts.rows)
    this.shortcuts:set_visible_rows()
    this.selected = #this.shortcuts.rows
  end

  self.remove = Button(self.panel, "Remove")
  self.remove:set_icon("C")
  function self.remove:on_click()
    local selected = this.shortcuts:get_selected()
    if selected then
      this.shortcuts:remove_row(selected)
      this.shortcuts:set_selected(nil)
      this.selected = nil
    else
      MessageBox.error("No shortcut selected", "Please select an shortcut to remove")
    end
  end

  self.line = Line(self.panel)

  self.message = Label(self.panel, "Press a key combination to change selected")

  self.mouse_intercept = Label(self.panel, "Grab mouse events here")
  self.mouse_intercept.border.width = 1
  self.mouse_intercept.clickable = true
  self.mouse_intercept:set_size(100, 100)
  function self.mouse_intercept:on_mouse_pressed(button, x, y, clicks)
    keymap.on_mouse_pressed(button, x, y, clicks)
    return true
  end
  function self.mouse_intercept:on_mouse_wheel(y)
    keymap.on_mouse_wheel(y)
    return true
  end
  function self.mouse_intercept:on_mouse_enter(...)
    Label.super.on_mouse_enter(self, ...)
    self.border.color = style.caret
  end
  function self.mouse_intercept:on_mouse_leave(...)
    Label.super.on_mouse_leave(self, ...)
    self.border.color = style.text
  end

  self.save = Button(self.panel, "Save")
  self.save:set_icon("S")
  function self.save:on_click()
    this:on_save(this:get_bindings())
    this:on_close()
  end

  self.reset = Button(self.panel, "Reset")
  function self.reset:on_click()
    this:on_reset()
    this:on_close()
  end

  self.cancel = Button(self.panel, "Cancel")
  self.cancel:set_icon("C")
  function self.cancel:on_click()
    this:on_close()
  end
end

---@param bindings table<integer, string>
function KeybindDialog:set_bindings(bindings)
  self.shortcuts:clear()
  for _, binding in ipairs(bindings) do
    self.shortcuts:add_row({binding})
  end
  if #bindings > 0 then
    self.shortcuts:set_selected(1)
    self.selected = 1
  end
  self.shortcuts:set_visible_rows()
end

---@return table<integer, string>
function KeybindDialog:get_bindings()
  local bindings = {}
  for idx=1, #self.shortcuts.rows, 1 do
    table.insert(bindings, self.shortcuts:get_row_text(idx))
  end
  return bindings
end

---Show the dialog and enable key interceptions
function KeybindDialog:show()
  current_dialog = self
  KeybindDialog.super.show(self)
end

---Hide the dialog and disable key interceptions
function KeybindDialog:hide()
  current_dialog = nil
  KeybindDialog.super.hide(self)
end

---Called when the user clicks on save
---@param bindings string
function KeybindDialog:on_save(bindings) end

---Called when the user clicks on reset
function KeybindDialog:on_reset() end

function KeybindDialog:update_size_position()
  KeybindDialog.super.update_size_position(self)

  self.shortcuts:set_position(style.padding.x/2, 0)

  self.add:set_position(
    style.padding.x/2,
    self.shortcuts:get_bottom() + style.padding.y
  )

  self.remove:set_position(
    self.add:get_right() + (style.padding.x/2),
    self.shortcuts:get_bottom() + style.padding.y
  )

  self.line:set_position(
    0,
    self.remove:get_bottom() + style.padding.y
  )

  self.message:set_position(
    style.padding.x/2,
    self.line:get_bottom() + style.padding.y
  )
  self.mouse_intercept:set_position(
    style.padding.x/2,
    self.message:get_bottom() + style.padding.y
  )

  self.save:set_position(
    style.padding.x/2,
    self.mouse_intercept:get_bottom() + style.padding.y
  )
  self.reset:set_position(
    self.save:get_right() + style.padding.x,
    self.mouse_intercept:get_bottom() + style.padding.y
  )
  self.cancel:set_position(
    self.reset:get_right() + style.padding.x,
    self.mouse_intercept:get_bottom() + style.padding.y
  )

  local size = self:get_size()

  self.panel.size.x = self.panel:get_real_width() + style.padding.x
  self.panel.size.y = self.panel:get_real_height()
  size.x = self:get_real_width() - (style.padding.x / 2)
  size.y = self:get_real_height() + (style.padding.y / 2)

  self.shortcuts:set_size(size.x - style.padding.x)

  self.line:set_width(size.x - style.padding.x)

  self.mouse_intercept:set_size(size.x - style.padding.x)

  self.close:set_position(
    size.x - self.close.size.x - (style.padding.x / 2),
    style.padding.y / 2
  )
end

--------------------------------------------------------------------------------
-- Intercept keymap events
--------------------------------------------------------------------------------

-- Same declarations as in core.keymap because modkey_map is not public
local macos = PLATFORM == "Mac OS X"
local modkeys_os = require("core.modkeys-" .. (macos and "macos" or "generic"))
local modkey_map = modkeys_os.map
local modkeys = modkeys_os.keys

---Copied from core.keymap because it is not public
local function key_to_stroke(k)
  local stroke = ""
  for _, mk in ipairs(modkeys) do
    if keymap.modkeys[mk] then
      stroke = stroke .. mk .. "+"
    end
  end
  return stroke .. k
end

local keymap_on_key_pressed = keymap.on_key_pressed
function keymap.on_key_pressed(k, ...)
  if current_dialog and current_dialog.selected then
    local mk = modkey_map[k]
    if mk then
      keymap.modkeys[mk] = true
      -- work-around for windows where `altgr` is treated as `ctrl+alt`
      if mk == "altgr" then
        keymap.modkeys["ctrl"] = false
      end
      current_dialog.shortcuts:set_row(current_dialog.selected, {key_to_stroke("")})
    else
      current_dialog.shortcuts:set_row(current_dialog.selected, {key_to_stroke(k)})
    end
    return true
  else
    return keymap_on_key_pressed(k, ...)
  end
end


return KeybindDialog
