local awful = require("awful")

local helpers = require(tostring(...):match(".*bling") .. ".helpers")

local Scratchpad = {}
local in_anim = false

--- Creates a new scratchpad object based on the argument
--
-- @param info A table of possible arguments
-- @return The new scratchpad object
function Scratchpad:new(info)
  info = info or {}
  info.awestore = info.awestore or {}
  setmetatable(info, self)
  self.__index = self
  return info
end

--- Find all clients that satisfy the the rule
--
-- @return A list of all clients that satisfy the rule
function Scratchpad:find() return helpers.client.find(self.rule) end

--- Applies the objects scratchpad properties to a given client
--
-- @param c A client to which to apply the properties
function Scratchpad:apply(c)
  if not c or not c.valid then return end
  c.floating = self.floating
  c.sticky = self.sticky
  c:geometry({
    x = self.geometry.x + awful.screen.focused().geometry.x,
    y = self.geometry.y + awful.screen.focused().geometry.y,
    width = self.geometry.width,
    height = self.geometry.height,
  })
  if self.autoclose then
    c:connect_signal("unfocus", function(c)
      c.sticky = false -- client won't turn off if sticky
      helpers.client.turn_off(c)
    end)
  end
end

--- Turns the scratchpad on
function Scratchpad:turn_on()
  local matches = self:find()
  if matches[1] and not in_anim then
    -- if a client was found, turn it on
    c = matches[1]
    if self.reapply then self:apply(c) end
    -- c.sticky was set to false in turn_off so it has to be reapplied anyway
    c.sticky = self.sticky
    local new_y = c.y
    local new_x = c.x

    -- Get the tweens
    local anim_x = self.awestore.x
    local anim_y = self.awestore.y

    -- Subscribe
    if anim_x then
      anim_x:subscribe(function(x)
        if c and c.valid then c.x = x end
        in_anim = true
      end)
    end
    if anim_y then
      anim_y:subscribe(function(y)
        if c and c.valid then c.y = y end
        in_anim = true
      end)
    end

    helpers.client.turn_on(c)

    -- Unsubscribe
    if anim_x then
      anim_x:set(new_x)
      local unsub_x
      unsub_x = anim_x.ended:subscribe(function()
        in_anim = false
        unsub_x()
      end)
    end
    if anim_y then
      anim_y:set(new_y)
      local unsub_y
      unsub_y = anim_y.ended:subscribe(function()
        in_anim = false
        unsub_y()
      end)
    end
    return
  else
    -- if no client was found, spawn one, find the corresponding window,
    --  apply the properties only once (until the next closing)
    local pid = awful.spawn.with_shell(self.command)
    local function inital_apply(c)
      if helpers.client.is_child_of(c, pid) then self:apply(c) end
      client.disconnect_signal("manage", inital_apply)
    end
    client.connect_signal("manage", inital_apply)
    return
  end
end

--- Turns the scratchpad off
function Scratchpad:turn_off()
  local matches = self:find()
  local c = matches[1]
  if c and not in_anim then
    c.sticky = false

    -- Get the tweens
    local anim_x = self.awestore.x
    local anim_y = self.awestore.y

    -- Subscribe
    if anim_x then
      anim_x:subscribe(function(x)
        if c and c.valid then c.x = x end
        in_anim = true
      end)
    end
    if anim_y then
      anim_y:subscribe(function(y)
        if c and c.valid then c.y = y end
        in_anim = true
      end)
    end

    -- Unsubscribe
    if anim_x then
      anim_x:set(anim_x:initial())
      local unsub
      unsub = anim_x.ended:subscribe(function()
        in_anim = false
        helpers.client.turn_off(c)
        unsub()
      end)
    end
    if anim_y then
      anim_y:set(anim_y:initial())

      local unsub
      unsub = anim_y.ended:subscribe(function()
        in_anim = false
        helpers.client.turn_off(c)
        unsub()
      end)
    end

    if not anim_x and not anim_y then helpers.client.turn_off(c) end
  end
end

--- Turns the scratchpad off if it is focused otherwise it raises the scratchpad
function Scratchpad:toggle()
  local is_turn_off = false
  local matches = self:find()
  if self.dont_focus_before_close then
    if matches[1] then
      local current_tag = matches[1].screen.selected_tag
      for k, tag in pairs(matches[1]:tags()) do
        if tag == current_tag then
          is_turn_off = true
          break
        else
          is_turn_off = false
        end
      end
    end
  else
    is_turn_off = client.focus and awful.rules.match(client.focus, self.rule)
  end

  if is_turn_off then
    if self.disable_floating_on_close and matches[1].floating == true then matches[1].floating = false end
    self:turn_off()
  else
    self:turn_on()
  end
end

return Scratchpad
