local type = type
local beautiful = require("beautiful")
local theme = require("radical.theme")
local timer = require("gears.timer")
local object = require("radical.object")

local module = {
 -- style  = require("radical.item.style"),
 -- layout = require("radical.item.layout"),
  arrow_type = {
    NONE     = 0,
    PRETTY   = 1,
    CENTERED = 2,
  },
  event      ={
    NEVER    = 0,
    BUTTON1  = 1,
    BUTTON2  = 2,
    BUTTON3  = 3,
    SELECTED = 100,
    HOVER    = 1000,
    LEAVE    = 1001,
  },
  item_flags     = {
    NONE      = 999999 ,
    DISABLED  = 1 , -- Cannot be interacted with
    URGENT    = 2 , -- Need attention
    SELECTED  = 3 , -- Single item selected [[FOCUS]]
    HOVERED   = -1 , -- Mouse hover
    PRESSED   = 4 , -- Mouse pressed
    USED      = 6 , -- Common flag
    CHECKED   = 7 , -- When checkbox isn't enough
    ALTERNATE = 8 ,
    HIGHLIGHT = 9 ,
    HEADER    = 10,

    -- Implementation defined flags
    USR1     = 101,
    USR2     = 102,
    USR3     = 103,
    USR4     = 104,
    USR5     = 105,
    USR6     = 106,
    USR7     = 107,
    USR8     = 108,
    USR9     = 109,
    USR10    = 110,
  },
}

local function load_async(tab,key)
  if key == "style" then
    module.style = require("radical.item.style")
    return module.style
  elseif key == "layout" then
    module.layout = require("radical.item.layout")
    return module.layout
    --TODO handle colors this way
--   elseif key:sub(1,7 ) == "get_fg_" then
--     
--   elseif key:sub(1,7 ) == "get_bg_" then
--     
--   elseif key:sub(1,17) == "get_border_color_" then
--     
  end
  return rawget(module,key)
end

local function hide_sub_menu(item,data)
  if item._tmp_menu then
    item._tmp_menu.visible = false
    item._tmp_menu = nil
    data._tmp_menu = nil
    item:emit_signal("state::changed")
  end
end

local function set_infoshapes(item, shapes)
    if item.widget and item.widget.get_children_by_id then
        local infoshape_widget = item.widget:get_children_by_id("infoshapes")[1]
        if infoshape_widget then
            infoshape_widget:set_infoshapes(shapes)
        end
    end
end

-- local registered_items = {}
-- local is_watching = false
-- local mouse_tracker = object{}
-- local function watch_mouse(a,b,c)
--   print("move",a,b,c)
-- end

-- local function register_mouse_track()
--   if not is_watching then
--     
--   end
-- end

local function new_item(data,args)
  args = args or {}
  local item,private_data = object({
    private_data  = {
      text        = args.text        or ""                                                                  ,
--       height      = args.height      or data.item_height or beautiful.menu_height or 30                     ,
      width       = args.width       or nil                                                                 ,
      icon        = args.icon        or nil                                                                 ,
      prefix      = args.prefix      or ""                                                                  ,
      suffix      = args.suffix      or ""                                                                  ,
      bg          = args.bg          or nil                                                                 ,
      fg          = args.fg          or data.fg                                                             , --TODO don't do this
      border_color= args.border_color or data.item_border_color                                             ,
      border_width= args.border_width or data.item_border_width                                             ,
      bg_prefix   = args.bg_prefix   or data.bg_prefix                                                      ,
      sub_menu_m  = (args.sub_menu   and type(args.sub_menu) == "table" and args.sub_menu.is_menu) and args.sub_menu or nil,
      sub_menu_f  = (args.sub_menu   and type(args.sub_menu) == "function") and args.sub_menu or nil        ,
      checkable   = args.checkable   or (args.checked ~= nil) or false                                      ,
      checked     = args.checked     or false                                                               ,
      tooltip     = args.tooltip     or nil                                                                 ,
      style       = args.style       or data.item_style                                                     ,
      layout      = args.layout      or args.item_layout or nil                                             ,
      infoshapes  = args.infoshapes  or nil                                                                 ,
      shape       = args.shape       or data.item_shape                                                     ,
      overlay_draw= args.overlay_draw or data.overlay_draw                                                  ,
      item_border_color = args.item_border_color or data.item_border_color or nil                           ,
    },
    force_private = {
      visible = true,
      selected = true,
      index    = true,
    },
    autogen_getmap  = true,
    autogen_setmap  = true,
    autogen_signals = true,
  })
  item._private_data = private_data
  item._internal     = args._internal or {}
  theme.setup_item_colors(data,item,args)

  item.get_height = function()
    return args.height or data.item_height or beautiful.menu_height or 30
  end
  item.get_bg = function()
    return data.bg
  end
  item.get_fg = function()
    return data.fg
  end

  item.state         = theme.init_state(item)

  for i=1,10 do
    item["button"..i] = args["button"..i]
  end

  -- Invalidate the filter when there is still room for new items
  -- this is slow, but more reliable than it was before
  if data._internal.visible_item_count
   and data._internal.visible_item_count < (data.max_items or 9999999) then
    data._internal.visible_item_count = nil
  end

  -- Need to be done before painting
  data._internal.items[#data._internal.items+1] = {}
  data._internal.items[#data._internal.items] = item

  -- Getters
  item.get_selected = function(_)
    return item.state[module.item_flags.SELECTED] == true
  end

  -- Setters
  item.set_selected = function(_,value,force)
    private_data.selected = value

    -- Hide the sub-menu
    local current_item = data._current_item
    if current_item and current_item ~= item or force then
      current_item.state[module.item_flags.SELECTED] = nil
      current_item:emit_signal("selected::changed",false)
      hide_sub_menu(current_item,data)
    end

    -- Unselect item
    if value == false then
      item.state[module.item_flags.SELECTED] = nil
      return
    end
    item.state[module.item_flags.SELECTED] = true
    data._current_item = item
  end

  function item:set_hover(value)
    local item_style = item.item_style or data.item_style
    item.state[module.item_flags.HOVERED] = value and true or nil
    item_style(item)
  end

  function item:set_font(f)
    local text_w = item._internal.text_w
    text_w:set_font(f)
  end

  function item:set_text(text)
    local text_w = item._internal.text_w
    if not text_w then return end

    if data.disable_markup then
      text_w:set_text(text)
    else
      text_w:set_markup(text)
    end

--TODO find new largest is item is smaller
--     if data.auto_resize then
--       local fit_w,fit_h = text_w:get_preferred_size()
--       local is_largest = item == data._internal.largest_item_h
--       if not data._internal.largest_item_h_v or data._internal.largest_item_h_v < fit_h then
--         data._internal.largest_item_h =item
--         data._internal.largest_item_h_v = fit_h
--       end
--     end

    item._private_data.text = text
  end

  -- Overlay and underlay
  item.set_infoshapes = set_infoshapes

  item.set_icon = function (_,value)
    local icon_w = item._internal.icon_w

    if not icon_w then return end

    icon_w:set_image(value)
  end

  -- Listen to signals
  item:connect_signal("state::changed",function()
    item:style()

    -- Some item.style need full widget repaint
    if item.style.need_full_repaint then
      --TODO HACK HACK HACK for the repaint, this is really, really stupid
      data._internal.layout:emit_signal("widget::redraw_needed")
      --TODO END HACK
    end
  end)

  -- Add support for long hover and press
  local main_timer,press_timer = nil
  item:connect_signal("mouse::enter",function(_,m,--[[mod,]]geo)
    if not main_timer then
      main_timer = timer{}
      main_timer.timeout = 1.5
      main_timer:connect_signal("timeout",function()
        item._internal._is_long_hover = true
        item:emit_signal("long::hover",item,{},geo)
        data:emit_signal("long::hover",item,{},geo) --TODO mouse::enter should have modifiers
        main_timer:stop()
      end)
    end
    if not main_timer.started then
--       item:connect_signal("mouse::move",test)
      main_timer:start()
    end
  end)
  item:connect_signal("mouse::leave",function()
    if main_timer and main_timer.started then
      main_timer:stop()
--       item:disconnect_signal("mouse::move",test)
    elseif item._internal._is_long_hover then
      item._internal._is_long_hover = nil
      item:emit_signal("long::exit",data,item)
    end
  end)
  item:connect_signal("button::press",function()
    if not press_timer then
      press_timer = timer{}
      press_timer.timeout = 1.5
      press_timer:connect_signal("timeout",function()
        item:emit_signal("long::press",data,item)
--         item:disconnect_signal("mouse::move",test)
        press_timer:stop()
      end)
    end
    if not press_timer.started then
--       item:connect_signal("mouse::move",test)
      press_timer:start()
    end
  end)
  item:connect_signal("button::release",function()
    if press_timer and press_timer.started then
      press_timer:stop()
    end
  end)

  return item
end

return setmetatable(module, { __call = function(_, ...) return new_item(...) end, __index=load_async})
-- kate: space-indent on; indent-width 2; replace-tabs on;
