--------------------------------------
-- This is the bluetooth controller --
--------------------------------------

-- Awesome Libs
local awful = require("awful")
local dpi = require("beautiful").xresources.apply_dpi
local gears = require("gears")
local wibox = require("wibox")

local rubato = require("src.lib.rubato")

local icondir = awful.util.getdir("config") .. "src/assets/icons/bluetooth/"

return function(s)

  local function create_device(device, battery)
    local icon = device.Icon or "bluetooth-on"
    local device_widget = wibox.widget {
      {
        {
          {
            {
              {
                image = gears.color.recolor_image(
                  icondir .. icon .. ".svg", Theme_config.bluetooth_controller.icon_color),
                id = "icon",
                resize = false,
                valign = "center",
                halign = "center",
                forced_width = dpi(24),
                forced_height = dpi(24),
                widget = wibox.widget.imagebox
              },
              id = "icon_container",
              strategy = "max",
              width = dpi(24),
              height = dpi(24),
              widget = wibox.container.constraint
            },
            {
              {
                {
                  text = device.Alias or device.Name,
                  id = "alias",
                  widget = wibox.widget.textbox
                },
                {
                  text = "Connecting...",
                  id = "connecting",
                  visible = false,
                  font = User_config.font.specify .. ", regular 10",
                  widget = wibox.widget.textbox
                },
                id = "alias_container",
                layout = wibox.layout.fixed.horizontal
              },
              width = dpi(260),
              height = dpi(40),
              strategy = "max",
              widget = wibox.container.constraint
            },
            spacing = dpi(10),
            layout = wibox.layout.fixed.horizontal
          },
          { -- Spacing
            forced_width = dpi(10),
            widget = wibox.container.background
          },
          {
            {
              {
                {
                  {
                    id = "con",
                    resize = false,
                    valign = "center",
                    halign = "center",
                    forced_width = dpi(24),
                    forced_height = dpi(24),
                    widget = wibox.widget.imagebox
                  },
                  id = "place",
                  strategy = "max",
                  width = dpi(24),
                  height = dpi(24),
                  widget = wibox.container.constraint
                },
                id = "margin",
                margins = dpi(2),
                widget = wibox.container.margin
              },
              id = "backgr",
              shape = function(cr, width, height)
                gears.shape.rounded_rect(cr, width, height, dpi(4))
              end,
              bg = Theme_config.bluetooth_controller.con_button_color,
              widget = wibox.container.background
            },
            id = "margin0",
            margin = dpi(5),
            widget = wibox.container.margin
          },
          id = "device_layout",
          layout = wibox.layout.align.horizontal
        },
        id = "device_margin",
        margins = dpi(5),
        widget = wibox.container.margin
      },
      bg = Theme_config.bluetooth_controller.device_bg,
      fg = Theme_config.bluetooth_controller.device_fg,
      border_color = Theme_config.bluetooth_controller.device_border_color,
      border_width = Theme_config.bluetooth_controller.device_border_width,
      id = "background",
      shape = function(cr, width, height)
        gears.shape.rounded_rect(cr, width, height, dpi(4))
      end,
      widget = wibox.container.background
    }

    --! using :Connect freezes awesome, either find a solution or switch to console commands
    if device.Connected then
      device_widget:get_children_by_id("con")[1].image = gears.color.recolor_image(icondir .. "link-off.svg",
        Theme_config.bluetooth_controller.icon_color_dark)
      device_widget:connect_signal(
        "button::press",
        function(_, _, _, key)
          if key == 1 then
            device:Disconnect()
            awesome.emit_signal("bluetooth::connect", device)
          end
        end
      )
    else
      device_widget:get_children_by_id("con")[1].image = gears.color.recolor_image(icondir .. "link.svg",
        Theme_config.bluetooth_controller.icon_color_dark)
      device_widget:connect_signal(
        "button::press",
        function(_, _, _, key)
          if key == 1 then
            device:Connect()
            awesome.emit_signal("bluetooth::disconnect", device)
          end
        end
      )
    end
    Hover_signal(device_widget, Theme_config.bluetooth_controller.device_bg_hover,
      Theme_config.bluetooth_controller.device_fg_hover)
    return device_widget
  end

  local connected_devices_list = wibox.widget {
    {
      {
        {
          step = dpi(50),
          spacing = dpi(10),
          layout = wibox.layout.overflow.vertical,
          scrollbar_width = 0,
          id = "connected_device_list"
        },
        id = "margin",
        margins = dpi(10),
        widget = wibox.container.margin
      },
      id = "place",
      height = dpi(200),
      strategy = "max",
      widget = wibox.container.constraint
    },
    id = "connected_device_background",
    border_color = Theme_config.bluetooth_controller.con_device_border_color,
    border_width = Theme_config.bluetooth_controller.con_device_border_width,
    shape = function(cr, width, height)
      gears.shape.partially_rounded_rect(cr, width, height, false, false, true, true, dpi(4))
    end,
    widget = wibox.container.background
  }

  local discovered_devices_list = wibox.widget {
    {
      {
        {
          spacing = dpi(10),
          step = dpi(50),
          layout = wibox.layout.overflow.vertical,
          scrollbar_width = 0,
          id = "discovered_device_list"
        },
        id = "margin",
        margins = dpi(10),
        widget = wibox.container.margin
      },
      id = "place",
      height = dpi(200),
      strategy = "max",
      widget = wibox.container.constraint
    },
    id = "discovered_device_background",
    border_color = Theme_config.bluetooth_controller.con_device_border_color,
    border_width = Theme_config.bluetooth_controller.con_device_border_width,
    shape = function(cr, width, height)
      gears.shape.partially_rounded_rect(cr, width, height, false, false, true, true, dpi(4))
    end,
    widget = wibox.container.background
  }

  local bluetooth_container = wibox.widget {
    {
      {
        {
          {
            {
              {
                {
                  {
                    resize = false,
                    image = gears.color.recolor_image(icondir .. "menu-down.svg",
                      Theme_config.bluetooth_controller.connected_icon_color),
                    widget = wibox.widget.imagebox,
                    valign = "center",
                    halign = "center",
                    id = "icon"
                  },
                  id = "center",
                  halign = "center",
                  valign = "center",
                  widget = wibox.container.place,
                },
                {
                  {
                    text = "Paired Devices",
                    widget = wibox.widget.textbox,
                    id = "device_name"
                  },
                  margins = dpi(5),
                  widget = wibox.container.margin
                },
                id = "connected",
                layout = wibox.layout.fixed.horizontal
              },
              id = "connected_bg",
              bg = Theme_config.bluetooth_controller.connected_bg,
              fg = Theme_config.bluetooth_controller.connected_fg,
              shape = function(cr, width, height)
                gears.shape.rounded_rect(cr, width, height, dpi(4))
              end,
              widget = wibox.container.background
            },
            id = "connected_margin",
            widget = wibox.container.margin
          },
          {
            id = "connected_list",
            widget = connected_devices_list,
            forced_height = 0
          },
          {
            {
              {
                {
                  {
                    resize = false,
                    image = gears.color.recolor_image(icondir .. "menu-down.svg",
                      Theme_config.bluetooth_controller.discovered_icon_color),
                    widget = wibox.widget.imagebox,
                    valign = "center",
                    halign = "center",
                    id = "icon",
                  },
                  id = "center",
                  halign = "center",
                  valign = "center",
                  widget = wibox.container.place,
                },
                {
                  {
                    text = "Nearby Devices",
                    widget = wibox.widget.textbox,
                    id = "device_name"
                  },
                  margins = dpi(5),
                  widget = wibox.container.margin
                },
                id = "discovered",
                layout = wibox.layout.fixed.horizontal
              },
              id = "discovered_bg",
              bg = Theme_config.bluetooth_controller.discovered_bg,
              fg = Theme_config.bluetooth_controller.discovered_fg,
              shape = function(cr, width, height)
                gears.shape.rounded_rect(cr, width, height, dpi(4))
              end,
              widget = wibox.container.background
            },
            id = "discovered_margin",
            top = dpi(10),
            widget = wibox.container.margin
          },
          {
            id = "discovered_list",
            widget = discovered_devices_list,
            forced_height = 0
          },
          id = "layout1",
          layout = wibox.layout.fixed.vertical
        },
        id = "margin",
        margins = dpi(15),
        widget = wibox.container.margin
      },
      shape = function(cr, width, height)
        gears.shape.rounded_rect(cr, width, height, dpi(8))
      end,
      border_color = Theme_config.bluetooth_controller.container_border_color,
      border_width = Theme_config.bluetooth_controller.container_border_width,
      bg = Theme_config.bluetooth_controller.container_bg,
      id = "background",
      widget = wibox.container.background
    },
    width = dpi(400),
    strategy = "exact",
    widget = wibox.container.constraint
  }

  -- Main container
  local bluetooth_controller_container = awful.popup {
    widget = wibox.container.background,
    ontop = true,
    bg = Theme_config.bluetooth_controller.container_bg,
    stretch = false,
    visible = false,
    screen = s,
    placement = function(c) awful.placement.align(c,
        { position = "top_right", margins = { right = dpi(380), top = dpi(60) } })
    end,
    shape = function(cr, width, height)
      gears.shape.rounded_rect(cr, width, height, dpi(12))
    end
  }

  local connected_devices, nearby_devices = {}, {}

  -- function to check if a device is already in the list
  local function is_device_in_list(device)
    for i = 1, #connected_devices do
      if connected_devices[i].Address == device.Address then
        return true
      end
    end
    return false
  end

  awesome.connect_signal(
    "bluetooth::device_changed",
    function(device, battery)
      if not is_device_in_list(device) then
        -- add device and battery to list
        if device.Paired then
          table.insert(connected_devices, device)
        else
          table.insert(nearby_devices, device)
        end
      end

      if (#connected_devices + #nearby_devices) > 0 then
        local cd_list, dd_list = {}, {}
        for _, d in pairs(connected_devices) do
          if d.Paired then
            table.insert(cd_list, create_device(d))
          else
            table.insert(dd_list, create_device(d))
          end
        end
        for _, d in pairs(nearby_devices) do
          if d.Paired then
            table.insert(cd_list, create_device(d, battery))
          else
            table.insert(dd_list, create_device(d, battery))
          end
        end
        connected_devices_list:get_children_by_id("connected_device_list")[1].children = cd_list
        discovered_devices_list:get_children_by_id("discovered_device_list")[1].children = dd_list
      end
    end
  )

  -- Variables for easier access and better readability
  local connected_margin = bluetooth_container:get_children_by_id("connected_margin")[1]
  local connected_list = bluetooth_container:get_children_by_id("connected_list")[1]
  local connected_bg = bluetooth_container:get_children_by_id("connected_bg")[1]
  local connected = bluetooth_container:get_children_by_id("connected")[1].center

  -- Click event for the microphone dropdown
  connected_margin:connect_signal(
    "button::press",
    function()
      local rubato_timer = rubato.timed {
        duration = 0.4,
        intro = 0.1,
        outro = 0.1,
        pos = connected_list.forced_height,
        easing = rubato.linear,
        subscribed = function(v)
          connected_list.forced_height = v
        end
      }
      if connected_list.forced_height == 0 then
        local size = (#connected_devices * 45) + ((#connected_devices - 1) * 10)
        if size < 210 then
          rubato_timer.target = dpi(size)
        else
          rubato_timer.target = dpi(210)
        end
        connected_margin.connected_bg.shape = function(cr, width, height)
          gears.shape.partially_rounded_rect(cr, width, height, true, true, false, false, dpi(4))
        end
        connected.icon:set_image(gears.color.recolor_image(icondir .. "menu-up.svg",
          Theme_config.bluetooth_controller.connected_icon_color))
      else
        rubato_timer.target = 0
        connected_bg.shape = function(cr, width, height)
          gears.shape.rounded_rect(cr, width, height, 4)
        end
        connected.icon:set_image(gears.color.recolor_image(icondir .. "menu-down.svg",
          Theme_config.bluetooth_controller.connected_icon_color))
      end
    end
  )

  -- Variables for easier access and better readability
  local discovered_margin = bluetooth_container:get_children_by_id("discovered_margin")[1]
  local discovered_list = bluetooth_container:get_children_by_id("discovered_list")[1]
  local discovered_bg = bluetooth_container:get_children_by_id("discovered_bg")[1]
  local discovered = bluetooth_container:get_children_by_id("discovered")[1].center

  -- Click event for the microphone dropdown
  discovered_margin:connect_signal(
    "button::press",
    function()
      local rubato_timer = rubato.timed {
        duration = 0.4,
        intro = 0.1,
        outro = 0.1,
        pos = discovered_list.forced_height,
        easing = rubato.linear,
        subscribed = function(v)
          discovered_list.forced_height = v
        end
      }

      if discovered_list.forced_height == 0 then
        local size = (#nearby_devices * dpi(45)) + ((#nearby_devices - 1) * dpi(10))
        if size < 210 then
          rubato_timer.target = dpi(size)
        else
          rubato_timer.target = dpi(20)
        end
        discovered_margin.discovered_bg.shape = function(cr, width, height)
          gears.shape.partially_rounded_rect(cr, width, height, true, true, false, false, dpi(4))
        end
        discovered.icon:set_image(gears.color.recolor_image(icondir .. "menu-up.svg",
          Theme_config.bluetooth_controller.discovered_icon_color))
      else
        rubato_timer.target = 0
        discovered_bg.shape = function(cr, width, height)
          gears.shape.rounded_rect(cr, width, height, 4)
        end
        discovered.icon:set_image(gears.color.recolor_image(icondir .. "menu-down.svg",
          Theme_config.bluetooth_controller.discovered_icon_color))
      end
    end
  )

  -- When the mouse leaves the popup it stops the mousegrabber and hides the popup.
  bluetooth_controller_container:connect_signal(
    "mouse::leave",
    function()
      mousegrabber.run(
        function()
          awesome.emit_signal("bluetooth_controller::toggle", s)
          mousegrabber.stop()
          return true
        end,
        "arrow"
      )
    end
  )

  bluetooth_controller_container:connect_signal(
    "mouse::enter",
    function()
      mousegrabber.stop()
    end
  )

  -- Draw the popup
  bluetooth_controller_container:setup {
    bluetooth_container,
    layout = wibox.layout.fixed.horizontal
  }

  -- Toggle container visibility
  awesome.connect_signal(
    "bluetooth_controller::toggle",
    function(scr)
      if scr == s then
        bluetooth_controller_container.visible = not bluetooth_controller_container.visible
      end
    end
  )

end
