
------------------------------------------------------------------------------
-- Callback handler  
------------------------------------------------------------------------------

iup.callbacks = {} -- storage for the C callbacks

function iup.CallMethod(name, ...)
  local ih = ... -- the first argument is always the iupHandle
  local lua_func = ih[name] -- use "gettable" to retrieve the Lua callback
  if (not lua_func) then
    return
  end
  
  if type(lua_func) == "function" then
    return lua_func(...)
  elseif type(lua_func) == "string" then  
    local temp = self
    self = ih
    local result = iup.dostring(lua_func)
    self = temp
    return result
  else
    return iup.ERROR
  end
end

function iup.RegisterCallback(name, c_func, type)
  -- Store a C callback for future use
  if not iup.callbacks[name] then iup.callbacks[name] = {} end
  local cb = iup.callbacks[name]
  if type then
    cb[type] = c_func
  else
    cb[1] = c_func
  end
end

function iup.CallGlobalMethod(name, ...)
  local INDEX = string.upper(name)
  local cb = iup.callbacks[INDEX]
  if (not cb) then 
    return
  end

  local lua_func = cb[2]
  if type(lua_func) == "function" then
    return lua_func(...)
  elseif type(lua_func) == "string" then  
    return iup.dostring(lua_func)
  else
    return iup.ERROR
  end
end

function iup.SetGlobalCallback(name, lua_func)
  local INDEX = string.upper(name)
  local cb = iup.callbacks[INDEX]
  if (cb) then -- if a callback name
    if (lua_func) then
      local c_func = cb[1]
      iup.SetFunction(INDEX, c_func) -- set the pre-defined C callback
    else
      iup.SetFunction(INDEX, nil)
    end
    cb[2] = lua_func -- store also the Lua callback
  end
end


------------------------------------------------------------------------------
-- Meta Methods 
------------------------------------------------------------------------------

------------------------------------------------------------------------------
-- this is a Lua table for control construction, see iup.WIDGET and iup.BOX
-- not used by applications

-- implements class inheritance for iupWidget
local widget_gettable = function(widget, index)
  local p = widget
  local v
  while p do
    v = rawget(p, index)
    if v then 
      return v 
    end

    p = rawget(p, "parent")
  end
  return nil
end

iup.NewClass("iupWidget")
iup.SetMethod("iupWidget", "__index", widget_gettable)


------------------------------------------------------------------------------
-- this is an Ihandle* with enhancements

local ihandle_gettable = function(ih, index)
  local INDEX = string.upper(index)
  if (iup.callbacks[INDEX]) then 
    local widget = iup.GetWidget(ih)
    if (not widget or type(widget)~="table") then error("invalid IUP handle") end
    return widget[index]
  else
    local value = iup.GetAttribute(ih, INDEX)
    if (not value) then
      local widget = iup.GetWidget(ih)
      if (not widget or type(widget)~="table") then error("invalid IUP handle") end
      return widget[index]
    elseif type(value)== "number" or type(value) == "string" then
      local ih = iup.GetHandle(value)
      if ih then 
        return ih
      else 
        return value 
      end
    else
      return value 
    end
  end
end

local ihandle_settable = function(ih, index, value)
  local ti = type(index)
  local tv = type(value)
  local widget = iup.GetWidget(ih)
  if (not widget or type(widget)~="table") then error("invalid IUP handle") end
  if ti == "number" or ti == "string" then -- check if a valid C name
    local INDEX = string.upper(index)
    local cb = iup.callbacks[INDEX]
    if (cb) then -- if a callback name
      local c_func = cb[1]
      if (not c_func) then
        c_func = cb[iup.GetClassName(ih)]
      end
      iup.SetCallback(ih, INDEX, c_func, value) -- set the pre-defined C callback
      widget[index] = value -- store also in Lua
    elseif iup.GetClass(value) == "iupHandle" then -- if an iupHandle
      local name = iup.SetHandleName(value)
      iup.SetAttribute(ih, INDEX, name)
      widget[index] = nil -- if there was something in Lua remove it
    elseif tv == "string" or tv == "number" or tv == "nil" then -- if a common value
      iup.SetAttribute(ih, INDEX, value)
      widget[index] = nil -- if there was something in Lua remove it
    else
      widget[index] = value -- store only in Lua
    end
  else
    widget[index] = value -- store only in Lua
  end
end

iup.NewClass("iupHandle")
iup.SetMethod("iupHandle", "__index", ihandle_gettable)
iup.SetMethod("iupHandle", "__newindex", ihandle_settable)
iup.SetMethod("iupHandle", "__tostring", iup.ihandle_tostring) -- implemented in C
iup.SetMethod("iupHandle", "__eq", iup.ihandle_compare) -- implemented in C


------------------------------------------------------------------------------
-- Utilities 
------------------------------------------------------------------------------

function iup.SetHandleName(v)  -- used also by radio and zbox
  local name = iup.GetName(v)
  if not name then
    local autoname = string.format("_IUPLUA_NAME(%s)", tostring(v))
    iup.SetHandle(autoname, v)
    return autoname
  end
  return name
end

function iup.RegisterWidget(ctrl) -- called by all the controls initialization functions
  iup[ctrl.nick] = function(param)
    if (not ctrl.constructor) then 
      error("IUP constructor missing for:" .. ctrl.nick) 
    end
    return ctrl:constructor(param)
  end
end

-- Called from C when an Ihandle created in C is pushed to Lua
function iup.RegisterHandle(ih, typename)

  iup.SetClass(ih, "iupHandle")
  
  local widget = iup.GetWidget(ih)
  if not widget then
    local class = iup[string.upper(typename)]
    if not class then
      if (iup.IsContainer(ih)) then
        class = iup.BOX
      else
        class = iup.WIDGET
      end
    end

    local widget = { 
      parent = class, 
      ihandle = ih 
      }

    iup.SetClass(widget, "iupWidget")
    iup.SetWidget(ih, widget)
  end
  
  return ih
end

------------------------------------------------------------------------------
-- Widget class (top class) 
------------------------------------------------------------------------------

iup.WIDGET = {
  callback = {}
}

function iup.WIDGET.constructor(class, param)
  local ih = class:createElement(param)  -- all classes must define createElement
  local widget = { 
    parent = class,
    ihandle = ih
  }
  iup.SetClass(ih, "iupHandle")
  iup.SetClass(widget, "iupWidget")
  iup.SetWidget(ih, widget)
  widget:setAttributes(param)
  return ih
end

function iup.WIDGET.setAttributes(widget, param)
  local ih = widget.ihandle
  for i,v in pairs(param) do 
    if type(i) == "number" and iup.GetClass(v) == "iupHandle" then
      -- We should not set this or other elements (such as iuptext)
      -- will erroneosly inherit it
      rawset(widget, i, v)
    else
      -- this will call settable metamethod
      ih[i] = v
    end
  end
end

function iup.WIDGET.show(ih)
  iup.Show(ih)
end

function iup.WIDGET.hide(ih)
  iup.Hide(ih)
end

function iup.WIDGET.map(ih)
  iup.Map(ih)
end

function iup.WIDGET.unmap(ih)
  iup.Unmap(ih)
end

function iup.WIDGET.detach(ih)
  iup.Detach(ih)
end

function iup.WIDGET.destroy(ih)
  iup.Destroy(ih)
end

-- all the objects in the hierarchy must be "iupWidget"
-- Must repeat this call for every new widget
iup.SetClass(iup.WIDGET, "iupWidget")


------------------------------------------------------------------------------
-- Box class (inherits from WIDGET) 
------------------------------------------------------------------------------

iup.BOX = {
  parent = iup.WIDGET
}

function iup.BOX.setAttributes(widget, param)
  -- iup.Append will be automatically called after createElement
  -- no need to pass elements in the constructor of boxes
  local ih = widget.ihandle
  local n = #param
  for i = 1, n do
    if iup.GetClass(param[i]) == "iupHandle" then 
      iup.Append(ih, param[i]) 
    end
  end
  iup.WIDGET.setAttributes(widget, param)
end

function iup.BOX.append(ih, child)
  return iup.Append(ih, child)
end

function iup.BOX.insert(ih, ref_child, child)
  return iup.Insert(ih, ref_child, child)
end

iup.SetClass(iup.BOX, "iupWidget")

------------------------------------------------------------------------------
-- Compatibility functions.
------------------------------------------------------------------------------

function iup.RGB(r, g, b)
  return string.format("%d %d %d", 255*r, 255*g, 255*b)
end

-- This will allow both names to be used in the same application
-- also will allow static linking to work with require for the main library (only)
if _G.package then
  _G.package.loaded["iuplua"] = iup
  iup._M = iup
  iup._PACKAGE = "iuplua"
end

function iup.layoutdialog(obj)
  return iup.LayoutDialog(obj[1])
end
