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

#include <memory.h>

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

#include "iup.h"
#include "iupcontrols.h"
#include "iupcbs.h"
#include <cd.h>
#include <cdlua.h>

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

#include "iup_attrib.h"
#include "iup_object.h"
#include "iup_assert.h"
#include "iup_predialogs.h"
#include "iup_str.h"



typedef int(*IFnL)(Ihandle*, lua_State *L);


static int math_sum(lua_State *L)
{
  int n = lua_gettop(L);  /* number of arguments */
  lua_Number dsum = luaL_checknumber(L, 1);
  int i;
  for (i = 2; i <= n; i++)
  {
    lua_Number d = luaL_checknumber(L, i);
    dsum += d;
  }
  lua_pushnumber(L, dsum);
  return 1;
}

static int math_average(lua_State *L)
{
  int n = lua_gettop(L);  /* number of arguments */
  lua_Number dsum = luaL_checknumber(L, 1);
  int i;
  for (i = 2; i <= n; i++)
  {
    lua_Number d = luaL_checknumber(L, i);
    dsum += d;
  }
  dsum /= (double)n;
  lua_pushnumber(L, dsum);
  return 1;
}

static int luamatrix_pushvalue(lua_State *L, const char* value, int only_number)
{
  double num;

  if (!value || value[0] == 0)
  {
    if (only_number)
      return 0;

    lua_pushnil(L);
    return 1;
  }

  if (sscanf(value, "%lf", &num) == 1)
    lua_pushnumber(L, num);
  else
  {
    if (only_number)
      return 0;

    lua_pushstring(L, value);
  }

  return 1;
}

static char* get_cell_value_safe(lua_State *L, Ihandle* ih, int lin, int col)
{
  char* value;

  if (iupAttribGetId2(ih, "_IUPMATRIX_GETCELL", lin, col))
    luaL_error(L, "recursion detected for cell(%d,%d)", lin, col);

  iupAttribSetStrId2(ih, "_IUPMATRIX_GETCELL", lin, col, "1");
  value = IupGetAttributeId2(ih, "CELL", lin, col);  /* display value */
  iupAttribSetId2(ih, "_IUPMATRIX_GETCELL", lin, col, NULL);

  return value;
}

static int formula_range(lua_State *L)
{
  Ihandle *ih;
  int lin1 = luaL_checkinteger(L, 1);
  int col1 = luaL_checkinteger(L, 2);
  int lin2 = luaL_checkinteger(L, 3);
  int col2 = luaL_checkinteger(L, 4);
  int only_number = 0;
  int lin, col, count;

  if (lua_isboolean(L, 5))
    only_number = lua_toboolean(L, 5);

  lua_getglobal(L, "matrix");
  ih = (Ihandle*)lua_touserdata(L, -1);

  count = 0;

  for (lin = lin1; lin <= lin2; lin++)
  {
    for (col = col1; col <= col2; col++)
    {
      char* value = get_cell_value_safe(L, ih, lin, col);

      if (luamatrix_pushvalue(L, value, only_number))
        count++;
    }
  }

  return count;
}

static int formula_cell(lua_State *L)
{
  Ihandle *ih;
  char* value;

  int lin = luaL_checkinteger(L, 1);
  int col = luaL_checkinteger(L, 2);

  lua_getglobal(L, "matrix");
  ih = (Ihandle*)lua_touserdata(L, -1);

  value = get_cell_value_safe(L, ih, lin, col);
  return luamatrix_pushvalue(L, value, 0);
}

static int formula_ifelse(lua_State *L)
{
  lua_toboolean(L, 1) ? lua_pushvalue(L, 2) : lua_pushvalue(L, 3);
  return 1;
}

static void show_formula_error(lua_State *L, Ihandle* ih, const char* str_message)
{
  const char* error = lua_tostring(L, -1);
  char msg[1024];
  sprintf(msg, "%s\n  Lua error: %s", str_message, error);
  IupMessageError(IupGetDialog(ih), msg);
}

static void register_math_global(lua_State *L)
{
  const char* register_global =
    "function openpackage(ns)\n"
    "  for n, v in pairs(ns) do _G[n] = v end\n"
    "end\n"
    "openpackage(math)\n";

  luaL_dostring(L, register_global);
}

#define iup_isupper(_c) (_c>='A' && _c<='Z')

static int matrix_get_global_name(const char* key, size_t len, int *lin, int *col)
{
  int i;

  if (len < 2)
    return 0;

  if (key[0] == 'L' && iup_isdigit(key[1]) && len > 3)
  {
    i = 2;
    while (iup_isdigit(key[i]) && i < len) 
      i++;

    if (key[i] == 'C' && iup_isdigit(key[i+1]))
    {
      int start_col = i+1;
      i += 2;

      while (iup_isdigit(key[i]) && i < len)
        i++;

      if (i == len) /* found L123C123 notation */
      {
        char str_lin[50];
        int str_lin_len = start_col-2;
        if (str_lin_len > 50)
          return 0;

        memcpy(str_lin, key + 1, str_lin_len);
        str_lin[str_lin_len] = 0;
        iupStrToInt(str_lin, lin);

        iupStrToInt(key + start_col, col);
        return 1;
      }
    }
  }

  return 0;
}

static int excel_get_global_name(const char* key, size_t len, int *lin, int *col)
{
  int i;

  if (len < 2)
    return 0;

  if (iup_isupper(key[0]) && len > 1)
  {
    i = 1;
    while (iup_isupper(key[i]) && i < len)
      i++;

    if (iup_isdigit(key[i]))
    {
      int start_lin = i;
      i++;

      while (iup_isdigit(key[i]) && i < len)
        i++;

      if (i == len) /* found ABC123 (col|lin) notation (same as Excel) */
      {
        *col = 0;
        i = 0;
        while (i < start_lin)
        {
          (*col) = 26 * (*col) + (key[i] - 'A' + 1);
          i++;
        }

        iupStrToInt(key + start_lin, lin);
        return 1;
      }
    }
  }

  return 0;
}

static int matrix_global_index(lua_State *L)
{
  size_t len;
  const char* key = lua_tolstring(L, 2, &len);
  int lin, col;

  if (matrix_get_global_name(key, len, &lin, &col))
  {
    Ihandle *ih;
    char* value;

    lua_getglobal(L, "matrix");
    ih = (Ihandle*)lua_touserdata(L, -1);

    value = get_cell_value_safe(L, ih, lin, col);
    luamatrix_pushvalue(L, value, 0);
  }
  else 
  {
    /* get raw method */
    lua_getmetatable(L, 1);
    lua_pushvalue(L, 2);
    lua_rawget(L, -2);
  }

  return 1;
}

static int excel_global_index(lua_State *L)
{
  size_t len;
  const char* key = lua_tolstring(L, 2, &len);
  int lin, col;

  if (excel_get_global_name(key, len, &lin, &col))
  {
    Ihandle *ih;
    char* value;

    lua_getglobal(L, "matrix");
    ih = (Ihandle*)lua_touserdata(L, -1);

    value = get_cell_value_safe(L, ih, lin, col);
    luamatrix_pushvalue(L, value, 0);
  }
  else
  {
    /* get raw method */
    lua_getmetatable(L, 1);
    lua_pushvalue(L, 2);
    lua_rawget(L, -2);
  }

  return 1;
}

static lua_State* iMatrixInitFormula(Ihandle* ih, const char* init)
{
  lua_State *L;
  IFnL init_cb;
  char* cell_names;

  iupASSERT(iupObjectCheck(ih));
  if (!iupObjectCheck(ih))
    return NULL;

  /* must be an IupMatrix */
  if (ih->iclass->nativetype != IUP_TYPECANVAS ||
      !IupClassMatch(ih, "matrix"))
    return NULL;

  L = (lua_State*)iupAttribGet(ih, "_IUPMATRIX_LUASTATE");  /* Used only by SetDynamic */
  if (L)
    lua_close(L);

  L = luaL_newstate();
  luaL_openlibs(L);

  register_math_global(L);

  cell_names = iupAttribGet(ih, "CELLNAMES");
  if (iupStrEqualNoCase(cell_names, "EXCEL"))
  {
    lua_register(L, "global_index", excel_global_index);
    luaL_dostring(L, "setmetatable(_G, {__index=global_index})");
  }
  else if (iupStrEqualNoCase(cell_names, "MATRIX"))
  {
    lua_register(L, "global_index", matrix_global_index);
    luaL_dostring(L, "setmetatable(_G, {__index=global_index})");
  }

  lua_register(L, "sum", math_sum);
  lua_register(L, "average", math_average);
  lua_register(L, "range", formula_range);
  lua_register(L, "cell", formula_cell);
  lua_register(L, "ifelse", formula_ifelse);

  if (init)
    luaL_dostring(L, init);

  init_cb = (IFnL)IupGetCallback(ih, "FORMULAINIT_CB");
  if (init_cb)
    init_cb(ih, L);

  lua_pushlightuserdata(L, ih);
  lua_setglobal(L, "matrix");

  return L;
}

static int iMatrixLoadFormula(lua_State *L, const char* formula)
{
  char formula_func[1024];

  sprintf(formula_func, "function matrix_formula(lin, col)\n"
          "  return %s\n"
          "end\n", formula);

  if (luaL_dostring(L, formula_func) != 0)
    return 0;

  return 1;
}

static int iMatrixExecFormula(lua_State *L, int lin, int col)
{
  lua_getglobal(L, "matrix_formula");
  lua_pushinteger(L, lin);
  lua_pushinteger(L, col);

  if (lua_pcall(L, 2, 1, 0) != 0)
    return 0;

  return 1;
}

void IupMatrixSetFormula(Ihandle* ih, int col, const char* formula, const char* init)
{
  int lin, numlin;

  lua_State *L = iMatrixInitFormula(ih, init);
  if (!L)
    return;

  if (!iMatrixLoadFormula(L, formula))
  {
    const char* str_message = IupGetLanguageString("IUP_ERRORINVALIDFORMULA");
    show_formula_error(L, ih, str_message);
    lua_close(L);
    return;
  }

  numlin = IupGetInt(ih, "NUMLIN");

  for (lin = 1; lin <= numlin; lin++)
  {
    if (!iMatrixExecFormula(L, lin, col))
    {
      const char* str_message = IupGetLanguageString("IUP_ERRORINVALIDFORMULA");
      show_formula_error(L, ih, str_message);
      lua_close(L);
      return;
    }

    /* get the result */

    if (lua_isnumber(L, -1))
    {
      double num = lua_tonumber(L, -1);
      IupSetDoubleId2(ih, "", lin, col, num);
    }
    else if (lua_isnil(L, -1))
      IupSetAttributeId2(ih, "", lin, col, NULL);
    else if (lua_isboolean(L, -1))
    {
      int num = lua_toboolean(L, -1);
      IupSetIntId2(ih, "", lin, col, num);
    }
    else
    {
      const char* value = lua_tostring(L, -1);
      IupSetStrAttributeId2(ih, "", lin, col, value);
    }

    lua_pop(L, 1);  /* remove the result from the stack */
  }

  lua_close(L);
}

static char* iMatrixDynamicTranslateValue_CB(Ihandle* ih, int lin, int col, char* value)
{
  if (value && value[0] == '=' && !iupAttribGet(ih, "EDITVALUE"))
  {
    lua_State* L = (lua_State*)iupAttribGet(ih, "_IUPMATRIX_LUASTATE");

    if (!iMatrixLoadFormula(L, value + 1))
    {
      const char* str_message = IupGetLanguageString("IUP_ERRORINVALIDFORMULA");
      show_formula_error(L, ih, str_message);
      return (char*)str_message;
    }

    if (!iMatrixExecFormula(L, lin, col))
    {
      const char* str_message = IupGetLanguageString("IUP_ERRORINVALIDFORMULA");
      show_formula_error(L, ih, str_message);
      return (char*)str_message;
    }

    /* get the result */

    if (lua_isnumber(L, -1))
    {
      double num = lua_tonumber(L, -1);
      iupAttribSetDouble(ih, "FORMULA_RETURN", num);
    }
    else
    {
      const char* str = lua_tostring(L, -1);
      iupAttribSetStr(ih, "FORMULA_RETURN", str);
    }

    lua_pop(L, 1);  /* remove the result from the stack */
    return iupAttribGet(ih, "FORMULA_RETURN");
  }

  return value;
}

/* Samples: 
      A = 1
      Z = 26
      BC = 55
      AD = 30
*/
static void iMatrixDynamicColName(char* col_name, int col)
{
  int n = 1;
  int cc = col;
  while (cc > 26)
  {
    cc /= 26;
    n++;
  }

  col_name[n] = 0;

  while (n)
  {
    int rem = col % 26;
    int c = rem + 'A' - 1;
    col_name[n-1] = (char)c;
    col /= 26;
    n--;
  }
}

static void iMatrixDynamicInsertCellReference(Ihandle* ih, int lin, int col)
{
  char* old_value = iupAttribGet(ih, "_IUPMATRIX_EDITVALUE");
  if (old_value)
  {
    char* cell_names;
    char* old_caret = iupAttribGet(ih, "_IUPMATRIX_EDITCARET");

    IupSetStrAttribute(ih, "VALUE", old_value);
    IupSetStrAttribute(ih, "CARET", old_caret);

    cell_names = iupAttribGet(ih, "CELLNAMES");
    if (iupStrEqualNoCase(cell_names, "EXCEL"))
    {
      char col_name[30];
      iMatrixDynamicColName(col_name, col);
      IupSetfAttribute(ih, "INSERT", "%s%d", col_name, lin);
    }
    else if (iupStrEqualNoCase(cell_names, "MATRIX"))
      IupSetfAttribute(ih, "INSERT", "L%dC%d", lin, col);
    else
      IupSetfAttribute(ih, "INSERT", "cell(%d,%d)", lin, col);
  }
}

static void iMatrixDynamicInsertRange(Ihandle* ih, int start_lin, int start_col, int lin, int col)
{
  char* old_value = iupAttribGet(ih, "_IUPMATRIX_EDITVALUE");
  if (old_value)
  {
    char* old_caret = iupAttribGet(ih, "_IUPMATRIX_EDITCARET");

    IupSetStrAttribute(ih, "VALUE", old_value);
    IupSetStrAttribute(ih, "CARET", old_caret);
    IupSetfAttribute(ih, "INSERT", "range(%d,%d,%d,%d)", start_lin, start_col, lin, col);
  }
}

static int iMatrixDynamicEditKillFocus_CB(Ihandle* ih)
{
  if (IupGetInt(ih, "EDITTEXT"))
  {
    char* value = IupGetAttribute(ih, "VALUE");  /* edited value */
    char* caret = IupGetAttribute(ih, "CARET");
    iupAttribSetStr(ih, "_IUPMATRIX_EDITVALUE", value);
    iupAttribSetStr(ih, "_IUPMATRIX_EDITCARET", caret);
  }

  return IUP_DEFAULT;
}

static int iMatrixDynamicEditClick_CB(Ihandle* ih, int lin, int col, char* status)
{
  if (iup_isbutton1(status) && IupGetInt(ih, "EDITTEXT"))
  {
    char* value = IupGetAttribute(ih, "VALUE");  /* edited value */
    if (value && value[0] == '=')
    {
      iMatrixDynamicInsertCellReference(ih, lin, col);
      iupAttribSetStrf(ih, "_IUPMATRIX_EDITINSERT", "%d:%d", lin, col);
    }
  }

  return IUP_DEFAULT;
}

static int iMatrixDynamicEditRelease_CB(Ihandle* ih, int lin, int col, char* status)
{
  (void)lin;
  (void)col;
  (void)status;

  if (iupAttribGet(ih, "_IUPMATRIX_EDITINSERT"))
    iupAttribSet(ih, "_IUPMATRIX_EDITINSERT", NULL);

  return IUP_DEFAULT;
}

static int iMatrixDynamicEditMouseMove_CB(Ihandle* ih, int lin, int col)
{
  char* value = iupAttribGet(ih, "_IUPMATRIX_EDITINSERT");
  if (value)
  {
    int start_lin, start_col;
    iupStrToIntInt(value, &start_lin, &start_col, ':');
    iMatrixDynamicInsertRange(ih, start_lin, start_col, lin, col);
  }
  return IUP_DEFAULT;
}

static int iMatrixDynamicLDestroy_CB(Ihandle* ih)
{
  Icallback cb = IupGetCallback(ih, "OLD_LDESTROY_CB");
  lua_State* L = (lua_State*)iupAttribGet(ih, "_IUPMATRIX_LUASTATE");
  iupAttribSet(ih, "_IUPMATRIX_LUASTATE", NULL);
  IupSetCallback(ih, "TRANSLATEVALUE_CB", NULL);
  lua_close(L);
  if (cb)
    cb(ih);
  return IUP_DEFAULT;
}

void IupMatrixSetDynamic(Ihandle* ih, const char* init)
{
  Icallback cb;

  lua_State *L = iMatrixInitFormula(ih, init);
  if (!L)
    return;

  iupAttribSet(ih, "_IUPMATRIX_LUASTATE", (char*)L);

  cb = IupGetCallback(ih, "LDESTROY_CB");
  if (cb)
    IupSetCallback(ih, "OLD_LDESTROY_CB", cb);
  IupSetCallback(ih, "LDESTROY_CB", iMatrixDynamicLDestroy_CB);

  IupSetCallback(ih, "TRANSLATEVALUE_CB", (Icallback)iMatrixDynamicTranslateValue_CB);
  IupSetCallback(ih, "EDITCLICK_CB", (Icallback)iMatrixDynamicEditClick_CB);
  IupSetCallback(ih, "EDITRELEASE_CB", (Icallback)iMatrixDynamicEditRelease_CB);
  IupSetCallback(ih, "EDITMOUSEMOVE_CB", (Icallback)iMatrixDynamicEditMouseMove_CB);
  IupSetCallback(ih, "EDITKILLFOCUS_CB", (Icallback)iMatrixDynamicEditKillFocus_CB);
}

static int MatrixSetFormula(lua_State *L)
{
  Ihandle *ih = iuplua_checkihandle(L, 1);
  IupMatrixSetFormula(ih, (int)luaL_checkinteger(L, 2), luaL_checkstring(L, 3), luaL_optstring(L, 4, NULL));
  return 0;
}

static int MatrixSetDynamic(lua_State *L)
{
  Ihandle *ih = iuplua_checkihandle(L, 1);
  IupMatrixSetDynamic(ih, luaL_optstring(L, 2, NULL));
  return 0;
}

static int matrix_draw_cb(Ihandle *self, int p0, int p1, int p2, int p3, int p4, int p5, cdCanvas* cnv)
{
  lua_State *L = iuplua_call_start(self, "draw_cb");
  lua_pushinteger(L, p0);
  lua_pushinteger(L, p1);
  lua_pushinteger(L, p2);
  lua_pushinteger(L, p3);
  lua_pushinteger(L, p4);
  lua_pushinteger(L, p5);
  cdlua_pushcanvas(L, cnv);
  return iuplua_call(L, 7);
}

static int matrix_color_cb(Ihandle *self, int p0, int p1, int *p2, int *p3, int *p4, const char* name)
{
  int status;
  lua_State *L = iuplua_call_start(self, name);
  int top = lua_gettop(L) - 3; /* 3 is the number of pushed values in iuplua_call_start */
  /* don't have control over the number of returned values because of LUA_MULTRET,
     so must restore stack manually */

  lua_pushinteger(L, p0);
  lua_pushinteger(L, p1);

  /* similar to iuplua_call */
  status = iuplua_call_raw(L, 2 + 2, LUA_MULTRET);   /* 2 args + 2 args(errormsg, ih), variable number of returns */

  if (status != LUA_OK)
    return IUP_DEFAULT;
  else
  {
    /* can do:
       return r, g, b, iup.DEFAULT
       return r, g, b, iup.IGNORE
       return iup.IGNORE
       return                       -- same as iup.IGNORE
    */
    int tmp = (int)lua_isnil(L, -1)? IUP_IGNORE: (int)lua_tointeger(L, -1);
    if (tmp == IUP_IGNORE)
    {
      lua_settop(L, top);  /* remove the results */
      return IUP_IGNORE;
    }

    *p2 = (int)lua_tointeger(L, -4);  /* R */
    *p3 = (int)lua_tointeger(L, -3);  /* G */
    *p4 = (int)lua_tointeger(L, -2);  /* B */
    lua_settop(L, top);  /* remove the results */
    return IUP_DEFAULT;  
  }
}

static int matrix_bgcolor_cb(Ihandle *self, int p0, int p1, int *p2, int *p3, int *p4)
{
  return matrix_color_cb(self, p0, p1, p2, p3, p4, "bgcolor_cb");
}

static int matrix_fgcolor_cb(Ihandle *self, int p0, int p1, int *p2, int *p3, int *p4)
{
  return matrix_color_cb(self, p0, p1, p2, p3, p4, "fgcolor_cb");
}

static int MatGetAttribute(lua_State *L)
{
  Ihandle *ih = iuplua_checkihandle(L,1);
  const char *name = luaL_checkstring(L,2);
  int lin = (int)luaL_checkinteger(L,3);
  int col = (int)luaL_checkinteger(L, 4);
  const char *value = IupGetAttributeId2(ih, name, lin, col);
  if (!value || iupATTRIB_ISINTERNAL(name))
    lua_pushnil(L);
  else
  {
    if (iupAttribIsNotString(ih, name))
    {
      if (iupAttribIsIhandle(ih, name))
        iuplua_pushihandle(L, (Ihandle*)value);
      else
        lua_pushlightuserdata(L, (void*)value);
    }
    else
      lua_pushstring(L,value);
  }
  return 1;
}

static int MatStoreAttribute(lua_State *L)
{
  Ihandle *ih = iuplua_checkihandle(L,1);
  const char *a = luaL_checkstring(L,2);
  int lin = luaL_checkinteger(L,3);
  int col = luaL_checkinteger(L,4);

  if (lua_isnil(L,5)) 
    IupSetAttributeId2(ih,a,lin, col,NULL);
  else 
  {
    const char *v;
    if(lua_isuserdata(L,5)) 
    {
      v = lua_touserdata(L,5);
      IupSetAttributeId2(ih,a,lin, col,v);
    }
    else 
    {
      v = luaL_checkstring(L,5);
      IupStoreAttributeId2(ih,a,lin, col,v);
    }
  }
  return 0;
}

static IFnii iMatrixOriginalKeyPress_CB = NULL;
static IFniiiis iMatrixOriginalButton_CB = NULL;
static IFniis iMatrixOriginalMotion_CB = NULL;

static int MatButtonCB(lua_State *L)
{
  Ihandle *ih = iuplua_checkihandle(L, 1);
  int p0 = luaL_checkinteger(L, 2);
  int p1 = luaL_checkinteger(L, 3);
  int p2 = luaL_checkinteger(L, 4);
  int p3 = luaL_checkinteger(L, 5);
  const char* p4 = luaL_checkstring(L, 6);
  int ret = iMatrixOriginalButton_CB(ih, p0, p1, p2, p3, (char*)p4);
  lua_pushinteger(L, ret);
  return 1;
}

static int MatMotionCB(lua_State *L)
{
  Ihandle *ih = iuplua_checkihandle(L, 1);
  int p0 = luaL_checkinteger(L, 2);
  int p1 = luaL_checkinteger(L, 3);
  const char* p2 = luaL_checkstring(L, 4);
  int ret = iMatrixOriginalMotion_CB(ih, p0, p1, (char*)p2);
  lua_pushinteger(L, ret);
  return 1;
}

static int MatKeyPressCB(lua_State *L)
{
  Ihandle *ih = iuplua_checkihandle(L, 1);
  int p0 = luaL_checkinteger(L, 2);
  int p1 = luaL_checkinteger(L, 3);
  int ret = iMatrixOriginalKeyPress_CB(ih, p0, p1);
  lua_pushinteger(L, ret);
  return 1;
}

void iuplua_matrixfuncs_open(lua_State *L)
{
  iuplua_register(L, MatrixSetFormula, "MatrixSetFormula");
  iuplua_register(L, MatrixSetDynamic, "MatrixSetDynamic");

  /* DEPRECATED backward compatibility */
  iuplua_register(L, MatGetAttribute, "MatGetAttribute");
  iuplua_register(L, MatStoreAttribute, "MatStoreAttribute");
  iuplua_register(L, MatStoreAttribute, "MatSetAttribute");

  /* Original Callback Export */
  iuplua_register(L, MatButtonCB, "MatButtonCb");
  iuplua_register(L, MatMotionCB, "MatMotionCb");
  iuplua_register(L, MatKeyPressCB, "MatKeyPressCb");

  {
    Ihandle* mat = IupMatrix(NULL);
    iMatrixOriginalKeyPress_CB = (IFnii)IupGetCallback(mat, "KEYPRESS_CB");
    iMatrixOriginalButton_CB = (IFniiiis)IupGetCallback(mat, "BUTTON_CB");
    iMatrixOriginalMotion_CB = (IFniis)IupGetCallback(mat, "MOTION_CB");
    IupDestroy(mat);
  }

  iuplua_register_cb(L, "BGCOLOR_CB", (lua_CFunction)matrix_bgcolor_cb, NULL);
  iuplua_register_cb(L, "FGCOLOR_CB", (lua_CFunction)matrix_fgcolor_cb, NULL);
  iuplua_register_cb(L, "DRAW_CB", (lua_CFunction)matrix_draw_cb, NULL);
  iuplua_register_cb(L, "LISTDRAW_CB", (lua_CFunction)matrix_draw_cb, NULL);
}
