/** \file
 * \brief iuptree binding for Lua 5.
 *
 * See Copyright Notice in "iup.h"
 */

#include <lua.h>
#include <lauxlib.h>

#include "iup.h"

#include "iuplua.h"
#include "il.h"

/* 
   The REGISTRY is used to store references to the associated Lua objects.
   Given an ID, to retrieve a Lua object is quite simple.

   But given a user_id to obtain the ID is more complicated.
   The iup.TREEREFTABLE is used to do this mapping.
   We use the object as the index to this table.
*/

/* iup.TREEREFTABLE[object at pos] = ref */
static void tree_settableref(lua_State *L, int pos, int ref)
{
  iuplua_push_name(L, "TREEREFTABLE");

  lua_pushvalue(L, pos);
  if(ref == LUA_NOREF)
    lua_pushnil(L);
  else
    lua_pushinteger(L, ref);
  lua_settable(L, -3);
  lua_pop(L, 1);
}

/* ref = iup.TREEREFTABLE[object at pos] */
static int tree_gettableref(lua_State *L, int pos)
{
  iuplua_push_name(L, "TREEREFTABLE");

  lua_pushvalue(L, pos);
  lua_gettable(L, -2);
  if (lua_isnil(L, -1))
  {
    lua_pop(L, 1);
    return LUA_NOREF;
  }
  else
  {
    int ref = (int)lua_tointeger(L, -1);
    lua_pop(L, 1);
    return ref;
  }
}

static void tree_push_userid(lua_State *L, void* userid)
{
  int ref = (int)userid;
  if (ref == 0) /* userid is actually NULL */
    lua_pushnil(L);
  else 
  {
    if (ref > 0) ref--; /* only positive references are shifted */
    lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
  }
}

/*****************************************************************************
 * Userdata/Table <-> id functions 
 ****************************************************************************/

static int TreeGetId(lua_State *L)
{
  Ihandle *ih = iuplua_checkihandle(L,1);
  int ref = tree_gettableref(L, 2);
  if (ref == LUA_NOREF)
    lua_pushnil(L);
  else
  {
    int id;
    if (ref >= 0) ref++;  /* only positive references are shifted */
    id = IupTreeGetId(ih, (void*)ref);
    if (id == -1)
      lua_pushnil(L);
    else
      lua_pushinteger(L, id);
  }
  return 1;        
}

static int TreeGetUserId(lua_State *L)
{  
  Ihandle *ih = iuplua_checkihandle(L,1);
  int id = (int)luaL_checkinteger(L,2);
  tree_push_userid(L, IupTreeGetUserId(ih, id));
  return 1;
}

static int TreeSetUserId(lua_State *L)
{  
  Ihandle *ih = iuplua_checkihandle(L,1);
  int id = (int)luaL_checkinteger(L, 2);
  int ref = (int)IupTreeGetUserId(ih, id);
  if (ref != 0) /* userid is not NULL */
  {
    if (ref > 0) ref--; /* only positive references are shifted */

    /* release the previous object referenced there */
    lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
    tree_settableref(L, 4, LUA_NOREF);
    luaL_unref(L, LUA_REGISTRYINDEX, ref);
    lua_pop(L, 1);
  }

  if (lua_isnil(L, 3))
    IupTreeSetUserId(ih, id, NULL);
  else
  {
    /* add a new reference */
    lua_pushvalue(L, 3);
    ref = luaL_ref(L, LUA_REGISTRYINDEX);
    tree_settableref(L, 3, ref);

    if (ref >= 0) ref++;  /* only positive references are shifted */
    IupTreeSetUserId(ih, id, (void*)ref);
  }

  return 0;
}

static int tree_multiselection_cb(Ihandle *ih, int* ids, int p1)
{
  int i;
  lua_State *L = iuplua_call_start(ih, "multiselection_cb");
  lua_createtable(L, p1, 0);
  for (i = 0; i < p1; i++)
  {
    lua_pushinteger(L,i+1);
    lua_pushinteger(L,ids[i]);
    lua_settable(L,-3);
  }
  lua_pushinteger(L, p1);
  return iuplua_call(L, 2);
}

static int tree_multiunselection_cb(Ihandle *ih, int* ids, int p1)
{
  int i;
  lua_State *L = iuplua_call_start(ih, "multiunselection_cb");
  lua_createtable(L, p1, 0);
  for (i = 0; i < p1; i++)
  {
    lua_pushinteger(L,i+1);
    lua_pushinteger(L,ids[i]);
    lua_settable(L,-3);
  }
  lua_pushinteger(L, p1);
  return iuplua_call(L, 2);
}

static int tree_noderemoved_cb(Ihandle *ih, void* p1)
{
  lua_State *L = iuplua_call_start(ih, "noderemoved_cb");
  tree_push_userid(L, p1);
  return iuplua_call(L, 1);
}

void iuplua_treefuncs_open (lua_State *L)
{
  iuplua_register_cb(L, "MULTISELECTION_CB", (lua_CFunction)tree_multiselection_cb, NULL);
  iuplua_register_cb(L, "MULTIUNSELECTION_CB", (lua_CFunction)tree_multiunselection_cb, NULL);
  iuplua_register_cb(L, "NODEREMOVED_CB", (lua_CFunction)tree_noderemoved_cb, NULL);

  /* Table/Userdata <-> id */
  iuplua_register(L, TreeGetId, "TreeGetId");
  iuplua_register(L, TreeGetUserId, "TreeGetUserId");
  iuplua_register(L, TreeSetUserId, "TreeSetUserId");

  /* OLD names for backward compatibility */
  iuplua_register(L, TreeGetId, "TreeGetTableId");
  iuplua_register(L, TreeGetUserId, "TreeGetTable");
  iuplua_register(L, TreeSetUserId, "TreeSetTableId");
}
