/* ltokenlib.c

  Copyright 2006-2008 Taco Hoekwater <[email protected]>

  This file is part of LuaTeX.

  LuaTeX is free software; you can redistribute it and/or modify it under
  the terms of the GNU General Public License as published by the Free
  Software Foundation; either version 2 of the License, or (at your
  option) any later version.

  LuaTeX is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
  License for more details.

  You should have received a copy of the GNU General Public License along
  with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */

#include "lua/luatex-api.h"
#include "ptexlib.h"

static const char _svn_version[] =
   "$Id: ltokenlib.c 3404 2010-01-28 11:17:10Z taco $ $URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.70.1/source/texk/web2c/luatexdir/lua/ltokenlib.c $";

#define  is_valid_token(L,i)  (lua_istable(L,i) && lua_objlen(L,i)==3)
#define  get_token_cmd(L,i)  lua_rawgeti(L,i,1)
#define  get_token_chr(L,i)  lua_rawgeti(L,i,2)
#define  get_token_cs(L,i)   lua_rawgeti(L,i,3)
#define  is_active_string(s) (strlen((char *)s)>3 && *s==0xEF && *(s+1)==0xBF && *(s+2)==0xBF)


static unsigned char *get_cs_text(int cs)
{
   if (cs == null_cs)
       return (unsigned char *) xstrdup("\\csname\\endcsname");
   else if ((cs_text(cs) < 0) || (cs_text(cs) >= str_ptr))
       return (unsigned char *) xstrdup("");
   else
       return (unsigned char *) makecstring(cs_text(cs));
}


static int test_expandable(lua_State * L)
{
   int cmd = -1;
   if (is_valid_token(L, -1)) {
       get_token_cmd(L, -1);
       if (lua_isnumber(L, -1)) {
           cmd = (int) lua_tointeger(L, -1);
       } else if (lua_isstring(L, -1)) {
           cmd = get_command_id(lua_tostring(L, -1));
       }
       if (cmd > max_command_cmd) {
           lua_pushboolean(L, 1);
       } else {
           lua_pushboolean(L, 0);
       }
   } else {
       lua_pushnil(L);
   }
   return 1;
}


static int test_protected(lua_State * L)
{
   int chr = -1;
   if (is_valid_token(L, -1)) {
       get_token_chr(L, -1);
       if (lua_isnumber(L, -1)) {
           chr = (int) lua_tointeger(L, -1);
       } else if (lua_isstring(L, -1)) {
           chr = get_command_id(lua_tostring(L, -1));
       }
       if (token_info(token_link(chr)) == protected_token) {
           lua_pushboolean(L, 1);
       } else {
           lua_pushboolean(L, 0);
       }
   } else {
       lua_pushnil(L);
   }
   return 1;
}

static int test_activechar(lua_State * L)
{
   if (is_valid_token(L, -1)) {
       unsigned char *s;
       int cs = 0;
       get_token_cs(L, -1);
       if (lua_isnumber(L, -1)) {
           cs = (int) lua_tointeger(L, -1);
       }
       lua_pop(L, 1);
       if (cs != 0 && ((s = get_cs_text(cs)) != (unsigned char *) NULL)) {
           if (is_active_string(s)) {
               free(s);
               lua_pushboolean(L, 1);
               return 1;
           }
           free(s);
       }
   }
   lua_pushboolean(L, 0);
   return 1;
}


static int run_get_command_name(lua_State * L)
{
   int cs;
   if (is_valid_token(L, -1)) {
       get_token_cmd(L, -1);
       if (lua_isnumber(L, -1)) {
           cs = (int) lua_tointeger(L, -1);
           lua_pushstring(L, command_names[cs].cmd_name);
       } else {
           lua_pushstring(L, "");
       }
   } else {
       lua_pushnil(L);
   }
   return 1;
}


static int run_get_csname_name(lua_State * L)
{
   int cs, cmd;
   unsigned char *s;
   if (is_valid_token(L, -1)) {
       get_token_cmd(L, -1);
       if (lua_isnumber(L, -1)) {
           cmd = (int) lua_tointeger(L, -1);
       }
       lua_pop(L, 1);
       cs = 0;
       get_token_cs(L, -1);
       if (lua_isnumber(L, -1)) {
           cs = (int) lua_tointeger(L, -1);
       }
       lua_pop(L, 1);

       if (cs != 0 && ((s = get_cs_text(cs)) != (unsigned char *) NULL)) {
           if (is_active_string(s))
               lua_pushstring(L, (char *) (s + 3));
           else
               lua_pushstring(L, (char *) s);
       } else {
           lua_pushstring(L, "");
       }
   } else {
       lua_pushnil(L);
   }
   return 1;
}

static int run_get_command_id(lua_State * L)
{
   int cs = -1;
   if (lua_isstring(L, -1)) {
       cs = get_command_id(lua_tostring(L, -1));
   }
   lua_pushnumber(L, cs);
   return 1;
}


static int run_get_csname_id(lua_State * L)
{
   const char *s;
   size_t k, cs = 0;
   if (lua_isstring(L, -1)) {
       s = lua_tolstring(L, -1, &k);
       cs = (size_t) string_lookup(s, k);
   }
   lua_pushnumber(L, (lua_Number) cs);
   return 1;
}


void make_token_table(lua_State * L, int cmd, int chr, int cs)
{
   lua_createtable(L, 3, 0);
   lua_pushnumber(L, cmd);
   lua_rawseti(L, -2, 1);
   lua_pushnumber(L, chr);
   lua_rawseti(L, -2, 2);
   lua_pushnumber(L, cs);
   lua_rawseti(L, -2, 3);
}

static int run_get_next(lua_State * L)
{
   int save_nncs;
   save_nncs = no_new_control_sequence;
   no_new_control_sequence = 0;
   get_next();
   no_new_control_sequence = save_nncs;
   make_token_table(L, cur_cmd, cur_chr, cur_cs);
   return 1;
}

static int run_expand(lua_State * L)
{
   (void) L;
   expand();
   return 0;
}


static int run_lookup(lua_State * L)
{
   const char *s;
   size_t l;
   int cs, cmd, chr;
   int save_nncs;
   if (lua_isstring(L, -1)) {
       s = lua_tolstring(L, -1, &l);
       if (l > 0) {
           save_nncs = no_new_control_sequence;
           no_new_control_sequence = true;
           cs = id_lookup((last + 1), (int) l);        /* cleans up the lookup buffer */
           cs = string_lookup(s, l);
           cmd = eq_type(cs);
           chr = equiv(cs);
           make_token_table(L, cmd, chr, cs);
           no_new_control_sequence = save_nncs;
           return 1;
       }
   }
   lua_newtable(L);
   return 1;
}

static int run_build(lua_State * L)
{
   int cmd, chr, cs;
   if (lua_isnumber(L, 1)) {
       cs = 0;
       chr = (int) lua_tointeger(L, 1);
       cmd = (int) luaL_optinteger(L, 2, get_char_cat_code(chr));
       if (cmd == 0 || cmd == 9 || cmd == 14 || cmd == 15) {
           fprintf(stdout,
                   "\n\nluatex error: not a good token.\nCatcode %i can not be returned, so I replaced it by 12 (other)",
                   (int) cmd);
           error();
           cmd = 12;
       }
       if (cmd == 13) {
           cs = active_to_cs(chr, false);
           cmd = eq_type(cs);
           chr = equiv(cs);
       }
       make_token_table(L, cmd, chr, cs);
       return 1;
   } else {
       return run_lookup(L);
   }
}


static const struct luaL_reg tokenlib[] = {
   {"get_next", run_get_next},
   {"expand", run_expand},
   {"lookup", run_lookup},
   {"create", run_build},
   {"is_expandable", test_expandable},
   {"is_activechar", test_activechar},
   {"is_protected", test_protected},
   {"csname_id", run_get_csname_id},
   {"csname_name", run_get_csname_name},
   {"command_name", run_get_command_name},
   {"command_id", run_get_command_id},
   {NULL, NULL}                /* sentinel */
};

int luaopen_token(lua_State * L)
{
   luaL_register(L, "token", tokenlib);
   return 1;
}