/*
** $Id: ldblib.c,v 1.38 2001/08/31 19:46:07 roberto Exp $
** Interface from Lua to its debug API
** See Copyright Notice in lua.h
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LUA_PRIVATE
#include "lua.h"

#include "lauxlib.h"
#include "luadebug.h"
#include "lualib.h"



static void settabss (lua_State *L, const l_char *i, const l_char *v) {
  lua_pushstring(L, i);
  lua_pushstring(L, v);
  lua_settable(L, -3);
}


static void settabsi (lua_State *L, const l_char *i, int v) {
  lua_pushstring(L, i);
  lua_pushnumber(L, v);
  lua_settable(L, -3);
}


static int getinfo (lua_State *L) {
  lua_Debug ar;
  const l_char *options = luaL_opt_string(L, 2, l_s("flnSu"));
  l_char buff[20];
  if (lua_isnumber(L, 1)) {
    if (!lua_getstack(L, (int)(lua_tonumber(L, 1)), &ar)) {
      lua_pushnil(L);  /* level out of range */
      return 1;
    }
  }
  else if (lua_isfunction(L, 1)) {
    lua_pushvalue(L, 1);
    sprintf(buff, l_s(">%.10s"), options);
    options = buff;
  }
  else
    luaL_argerror(L, 1, l_s("function or level expected"));
  if (!lua_getinfo(L, options, &ar))
    luaL_argerror(L, 2, l_s("invalid option"));
  lua_newtable(L);
  for (; *options; options++) {
    switch (*options) {
      case l_c('S'):
        settabss(L, l_s("source"), ar.source);
        if (ar.source)
          settabss(L, l_s("short_src"), ar.short_src);
        settabsi(L, l_s("linedefined"), ar.linedefined);
        settabss(L, l_s("what"), ar.what);
        break;
      case l_c('l'):
        settabsi(L, l_s("currentline"), ar.currentline);
        break;
      case l_c('u'):
        settabsi(L, l_s("nups"), ar.nups);
        break;
      case l_c('n'):
        settabss(L, l_s("name"), ar.name);
        settabss(L, l_s("namewhat"), ar.namewhat);
        break;
      case l_c('f'):
        lua_pushliteral(L, l_s("func"));
        lua_pushvalue(L, -3);
        lua_settable(L, -3);
        break;
    }
  }
  return 1;  /* return table */
}
    

static int getlocal (lua_State *L) {
  lua_Debug ar;
  const l_char *name;
  if (!lua_getstack(L, luaL_check_int(L, 1), &ar))  /* level out of range? */
    luaL_argerror(L, 1, l_s("level out of range"));
  name = lua_getlocal(L, &ar, luaL_check_int(L, 2));
  if (name) {
    lua_pushstring(L, name);
    lua_pushvalue(L, -2);
    return 2;
  }
  else {
    lua_pushnil(L);
    return 1;
  }
}


static int setlocal (lua_State *L) {
  lua_Debug ar;
  if (!lua_getstack(L, luaL_check_int(L, 1), &ar))  /* level out of range? */
    luaL_argerror(L, 1, l_s("level out of range"));
  luaL_checkany(L, 3);
  lua_pushstring(L, lua_setlocal(L, &ar, luaL_check_int(L, 2)));
  return 1;
}



#define KEY_CALLHOOK	l_s("luadblibCallhook")
#define KEY_LINEHOOK	l_s("luadblibLinehook")


static void hookf (lua_State *L, const l_char *key) {
  lua_pushstring(L, key);
  lua_gettable(L, LUA_REGISTRYINDEX);
  if (lua_isfunction(L, -1)) {
    lua_pushvalue(L, -2);  /* original argument (below function) */
    lua_rawcall(L, 1, 0);
  }
  else
    lua_pop(L, 1);  /* pop result from gettable */
}


static void callf (lua_State *L, lua_Debug *ar) {
  lua_pushstring(L, ar->event);
  hookf(L, KEY_CALLHOOK);
}


static void linef (lua_State *L, lua_Debug *ar) {
  lua_pushnumber(L, ar->currentline);
  hookf(L, KEY_LINEHOOK);
}


static void sethook (lua_State *L, const l_char *key, lua_Hook hook,
                     lua_Hook (*sethookf)(lua_State * L, lua_Hook h)) {
  lua_settop(L, 1);
  if (lua_isnil(L, 1))
    (*sethookf)(L, NULL);
  else if (lua_isfunction(L, 1))
    (*sethookf)(L, hook);
  else
    luaL_argerror(L, 1, l_s("function expected"));
  lua_pushstring(L, key);
  lua_gettable(L, LUA_REGISTRYINDEX);   /* get old value */
  lua_pushstring(L, key);
  lua_pushvalue(L, 1);
  lua_settable(L, LUA_REGISTRYINDEX);  /* set new value */
}


static int setcallhook (lua_State *L) {
  sethook(L, KEY_CALLHOOK, callf, lua_setcallhook);
  return 1;
}


static int setlinehook (lua_State *L) {
  sethook(L, KEY_LINEHOOK, linef, lua_setlinehook);
  return 1;
}


static const luaL_reg dblib[] = {
  {l_s("getlocal"), getlocal},
  {l_s("getinfo"), getinfo},
  {l_s("setcallhook"), setcallhook},
  {l_s("setlinehook"), setlinehook},
  {l_s("setlocal"), setlocal}
};


LUALIB_API int lua_dblibopen (lua_State *L) {
  luaL_openl(L, dblib);
  return 0;
}