/* llanglib.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: llanglib.c 3551 2010-03-26 14:43:50Z taco $ $URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.70.1/source/texk/web2c/luatexdir/lua/llanglib.c $";

#define LANG_METATABLE "luatex.lang"

#define check_islang(L,b) (struct tex_language **)luaL_checkudata(L,b,LANG_METATABLE)

static int lang_new(lua_State * L)
{
   struct tex_language **lang;
   if (lua_gettop(L) == 0) {
       lang = lua_newuserdata(L, sizeof(struct tex_language *));
       *lang = new_language(-1);
       if (!*lang) {
           return luaL_error(L, "lang.new(): no room for a new language");
       }
   } else {
       int lualang;
       lang = lua_newuserdata(L, sizeof(struct tex_language *));
       lua_number2int(lualang, lua_tonumber(L, 1));
       *lang = get_language(lualang);
       if (!*lang) {
           return luaL_error(L, "lang.new(%d): undefined language",
                           lua_tonumber(L, 1));
       }
   }
   luaL_getmetatable(L, LANG_METATABLE);
   lua_setmetatable(L, -2);
   return 1;
}

static int lang_id(lua_State * L)
{
   struct tex_language **lang_ptr;
   lang_ptr = check_islang(L, 1);
   lua_pushnumber(L, (*lang_ptr)->id);
   return 1;
}

static int lang_patterns(lua_State * L)
{
   struct tex_language **lang_ptr;
   lang_ptr = check_islang(L, 1);
   if (lua_gettop(L) != 1) {
       if (!lua_isstring(L, 2)) {
           return luaL_error(L, "lang.patterns(): argument should be a string");
       }
       load_patterns(*lang_ptr, (const unsigned char *) lua_tostring(L, 2));
       return 0;
   } else {
       if ((*lang_ptr)->patterns != NULL) {
           lua_pushstring(L, (char *) hnj_serialize((*lang_ptr)->patterns));
       } else {
           lua_pushnil(L);
       }
       return 1;
   }
}

static int lang_clear_patterns(lua_State * L)
{
   struct tex_language **lang_ptr;
   lang_ptr = check_islang(L, 1);
   clear_patterns(*lang_ptr);
   return 0;
}


static int lang_hyphenation(lua_State * L)
{
   struct tex_language **lang_ptr;
   lang_ptr = check_islang(L, 1);
   if (lua_gettop(L) != 1) {
       if (!lua_isstring(L, 2)) {
           return luaL_error(L,
                          "lang.hyphenation(): argument should be a string");
       }
       load_hyphenation(*lang_ptr, (const unsigned char *) lua_tostring(L, 2));
       return 0;
   } else {
       if ((*lang_ptr)->exceptions != 0) {
           lua_pushstring(L, exception_strings(*lang_ptr));
       } else {
           lua_pushnil(L);
       }
       return 1;
   }
}

static int lang_pre_hyphen_char(lua_State * L)
{
   struct tex_language **lang_ptr;
   lang_ptr = check_islang(L, 1);
   if (lua_gettop(L) != 1) {
       if (!lua_isnumber(L, 2)) {
           return luaL_error(L,
                          "lang.prehyphenchar(): argument should be a character number");
       }
       lua_number2int((*lang_ptr)->pre_hyphen_char, lua_tonumber(L, 2));
       return 0;
   } else {
       lua_pushnumber(L, (*lang_ptr)->pre_hyphen_char);
       return 1;
   }
}

static int lang_post_hyphen_char(lua_State * L)
{
   struct tex_language **lang_ptr;
   lang_ptr = check_islang(L, 1);
   if (lua_gettop(L) != 1) {
       if (!lua_isnumber(L, 2)) {
           return luaL_error(L,
                          "lang.posthyphenchar(): argument should be a character number");
       }
       lua_number2int((*lang_ptr)->post_hyphen_char, lua_tonumber(L, 2));
       return 0;
   } else {
       lua_pushnumber(L, (*lang_ptr)->post_hyphen_char);
       return 1;
   }
}


static int lang_pre_exhyphen_char(lua_State * L)
{
   struct tex_language **lang_ptr;
   lang_ptr = check_islang(L, 1);
   if (lua_gettop(L) != 1) {
       if (!lua_isnumber(L, 2)) {
           return luaL_error(L,
                          "lang.preexhyphenchar(): argument should be a character number");
       }
       lua_number2int((*lang_ptr)->pre_exhyphen_char, lua_tonumber(L, 2));
       return 0;
   } else {
       lua_pushnumber(L, (*lang_ptr)->pre_exhyphen_char);
       return 1;
   }
}

static int lang_post_exhyphen_char(lua_State * L)
{
   struct tex_language **lang_ptr;
   lang_ptr = check_islang(L, 1);
   if (lua_gettop(L) != 1) {
       if (!lua_isnumber(L, 2)) {
           return luaL_error(L,
                          "lang.postexhyphenchar(): argument should be a character number");
       }
       lua_number2int((*lang_ptr)->post_exhyphen_char, lua_tonumber(L, 2));
       return 0;
   } else {
       lua_pushnumber(L, (*lang_ptr)->post_exhyphen_char);
       return 1;
   }
}


static int lang_clear_hyphenation(lua_State * L)
{
   struct tex_language **lang_ptr;
   lang_ptr = check_islang(L, 1);
   clear_hyphenation(*lang_ptr);
   return 0;
}


static int do_lang_clean(lua_State * L)
{
   char *cleaned;
   if (!lua_isstring(L, 1)) {
       return luaL_error(L, "lang.clean(): argument should be a string");
   }
   (void) clean_hyphenation(lua_tostring(L, 1), &cleaned);
   lua_pushstring(L, cleaned);
   return 1;
}

static int do_lang_hyphenate(lua_State * L)
{
   halfword *h, *t, tt;
   h = check_isnode(L, 1);
   if (lua_isuserdata(L, 2)) {
       t = check_isnode(L, 2);
       tt = *t;
       lua_pop(L, 1);
   } else {
       tt = *h;
       while (vlink(tt) != null)
           tt = vlink(tt);
   }
   hnj_hyphenation(*h, tt);
   lua_pushboolean(L, 1);
   return 1;
}


static const struct luaL_reg langlib_d[] = {
   /* *INDENT-OFF* */
   {"clear_patterns",    lang_clear_patterns},
   {"clear_hyphenation", lang_clear_hyphenation},
   {"patterns",          lang_patterns},
   {"hyphenation",       lang_hyphenation},
   {"prehyphenchar",     lang_pre_hyphen_char},
   {"posthyphenchar",    lang_post_hyphen_char},
   {"preexhyphenchar",   lang_pre_exhyphen_char},
   {"postexhyphenchar",  lang_post_exhyphen_char},
   {"id",                lang_id},
   /* *INDENT-ON* */
   {NULL, NULL}                /* sentinel */
};


static const struct luaL_reg langlib[] = {
   /* *INDENT-OFF* */
   {"clear_patterns",    lang_clear_patterns},
   {"clear_hyphenation", lang_clear_hyphenation},
   {"patterns",          lang_patterns},
   {"hyphenation",       lang_hyphenation},
   {"prehyphenchar",     lang_pre_hyphen_char},
   {"posthyphenchar",    lang_post_hyphen_char},
   {"preexhyphenchar",   lang_pre_exhyphen_char},
   {"postexhyphenchar",  lang_post_exhyphen_char},
   {"id",                lang_id},
   {"clean",             do_lang_clean},
   {"hyphenate",         do_lang_hyphenate},
   {"new",               lang_new},
   /* *INDENT-ON* */
   {NULL, NULL}                /* sentinel */
};


int luaopen_lang(lua_State * L)
{
   luaL_newmetatable(L, LANG_METATABLE);
   lua_pushvalue(L, -1);       /* push metatable */
   lua_setfield(L, -2, "__index");     /* metatable.__index = metatable */
   luaL_register(L, NULL, langlib_d);  /* dict methods */
   luaL_register(L, "lang", langlib);
   return 1;
}