/* -*- C++ -*-
 *
 *  LUAHandler.cpp - LUA handler for ONScripter
 *
 *  Copyright (c) 2001-2019 Ogapee. All rights reserved.
 *
 *  ogapee@aqua.dti2.ne.jp
 *
 *  This program 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.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "LUAHandler.h"
#include "ONScripter.h"
#include "ScriptHandler.h"

#define ONS_LUA_HANDLER_PTR "ONS_LUA_HANDLER_PTR"
#define INIT_SCRIPT "system.lua"

static char cmd_buf[256];
static char *conv_buf = NULL;
static size_t conv_buf_len = 0;

int NL_dofile(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    const char *str = luaL_checkstring( state, 1 );
    
    unsigned long length = lh->sh->cBR->getFileLength(str);
    if (length == 0){
        printf("cannot open %s\n", str);
        return 0;
    }

    unsigned char *buffer = new unsigned char[length+1];
    int location;
    lh->sh->cBR->getFile(str, buffer, &location);
    buffer[length] = 0;

    unsigned char *buffer2 = new unsigned char[length*3/2];
    unsigned char *p = buffer;
    unsigned char *p2 = buffer2;
    while(*p){
        int n = lh->sh->enc.getBytes(*p);
        for (int i=0 ; i<n; i++){
            if (i > 0 && *p == '\\') *p2++ = '\\';
            *p2++ = *p++;
        }
    }

    if (luaL_loadbuffer(state, (const char*)buffer2, p2 - buffer2, str) || 
        lua_pcall(state, 0, 0, 0)){
        printf("cannot parse %s %s\n", str, lua_tostring(state,-1));
    }

    delete[] buffer;
    delete[] buffer2;

    return 0;
}

int NSCheckComma(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int val = lh->sh->getEndStatus();
    if (val & ScriptHandler::END_COMMA && !(val & ScriptHandler::END_COMMA_READ))
        lua_pushboolean( state, 1 );
    else
        lua_pushboolean( state, 0 );
        
    return 1;
}

int NSDDelete(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int no = luaL_checkinteger( state, 1 );

    lh->ons->NSDDeleteCommand(no);
        
    return 0;
}

int NSDCall(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int num = luaL_checkinteger( state, 1 );
    const char *str1 = luaL_checkstring( state, 2 );
    int proc = luaL_checkinteger( state, 3 );
    const char *str2 = luaL_checkstring( state, 4 );

    lh->ons->NSDCallCommand(num, str1, proc, str2);

    return 0;
}

int NSDDLL(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );

    const char *str1 = luaL_checkstring( state, 1 );
    const char *str2 = luaL_checkstring( state, 2 );

    struct DllFuncTable{
        const char *name1, *name2;
    } dll_func_table[] = {
        {"deffontd.dll", "Font"},
        {NULL, NULL},
    };

    int idx=0;
    while(dll_func_table[idx].name1){
        size_t len = strlen(dll_func_table[idx].name1);
        if (strlen(str1) >= len &&
            strncmp(str1+strlen(str1)-len, dll_func_table[idx].name1, len) == 0 &&
            strcmp(str2, dll_func_table[idx].name2) == 0)
            break;
        idx++;
    }

    if (dll_func_table[idx].name1)
        idx++;
    else
        idx = 0;

    lua_pushinteger( state, idx );

    return 1;
}

int NSDLoad(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int no = luaL_checkinteger( state, 1 );
    const char *str = luaL_checkstring( state, 2 );

    lh->ons->NSDLoadCommand(no, str);
        
    return 0;
}

int NSDPresentRect(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int x1 = luaL_checkinteger( state, 1 );
    int y1 = luaL_checkinteger( state, 2 );
    int x2 = luaL_checkinteger( state, 3 );
    int y2 = luaL_checkinteger( state, 4 );

    lh->ons->NSDPresentRectCommand(x1, y1, x2, y2);
        
    return 0;
}

int NSDSp2(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int num = luaL_checkinteger( state, 1 );
    int dcx = luaL_checkinteger( state, 2 );
    int dcy = luaL_checkinteger( state, 3 );
    int sx = luaL_checkinteger( state, 4 );
    int sy = luaL_checkinteger( state, 5 );
    int w = luaL_checkinteger( state, 6 );
    int h = luaL_checkinteger( state, 7 );
    float xs = luaL_checknumber( state, 8 );
    float xy = luaL_checknumber( state, 9 );
    float rot = luaL_checknumber( state, 10 );
    int alpha = luaL_checkinteger( state, 11 );

    lh->ons->NSDSp2Command(num, dcx, dcy, sx, sy, w, h,
                           (int)(xs*100.0), (int)(xy*100.0), (int)rot, alpha);
        
    return 0;
}

int NSDSetSprite(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int spnum = luaL_checkinteger( state, 1 );
    int texnum = luaL_checkinteger( state, 2 );
    const char *str = NULL;
    if (lua_isstring( state, 3 ))
        str = luaL_checkstring( state, 3 );

    lh->ons->NSDSetSpriteCommand(spnum, texnum, str);
        
    return 0;
}

int NSDoEvents(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);
    
    sprintf(cmd_buf, "_wait 0");
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    lua_pushboolean( state, false );

    return 1;
}

int NSEnd(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);
    
    sprintf(cmd_buf, "_end");
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSExec(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);
    
    lh->sh->enterExternalScript((char*)lua_tostring(state, 1));
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSExecAnimation(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    lh->ons->waitEventSub(0);

    return 0;
}

int NSGosub(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);
    
    const char *str = luaL_checkstring( state, 1 );
    lh->ons->gosubReal( str+1, lh->sh->getNext() );

    return 0;
}

int NSGoto(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);
    
    const char *str = luaL_checkstring( state, 1 );
    lh->ons->setCurrentLabel( str+1 );

    return 0;
}

int NSGetClick(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    ONScripter::ButtonState &bs = lh->ons->getCurrentButtonState();

    if (bs.event_type == SDL_MOUSEBUTTONUP && bs.event_button == SDL_BUTTON_LEFT)
        lua_pushboolean( state, true );
    else
        lua_pushboolean( state, false );

    if (bs.event_type == SDL_MOUSEBUTTONUP && bs.event_button == SDL_BUTTON_RIGHT)
        lua_pushboolean( state, true );
    else
        lua_pushboolean( state, false );

#if SDL_VERSION_ATLEAST(1, 2, 5)
    if (bs.event_button == SDL_BUTTON_WHEELUP)
        lua_pushinteger( state, 1 );
    else if (bs.event_button == SDL_BUTTON_WHEELDOWN)
        lua_pushinteger( state, -1 );
    else
#endif
        lua_pushinteger( state, 0 );

    if (bs.event_type == SDL_MOUSEBUTTONDOWN && bs.event_button == SDL_BUTTON_LEFT)
        lua_pushboolean( state, true );
    else
        lua_pushboolean( state, false );

    if (bs.event_type == SDL_MOUSEBUTTONDOWN && bs.event_button == SDL_BUTTON_RIGHT)
        lua_pushboolean( state, true );
    else
        lua_pushboolean( state, false );

    bs.event_type = 0;
    bs.event_button = 0;

    return 5;
}

int NSGetIntValue(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int no = luaL_checkinteger( state, 1 );
    lua_pushnumber( state, lh->sh->getVariableData(no).num );
    
    return 1;
}

int NSGetKey(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    const char *str = luaL_checkstring( state, 1 );
    ONScripter::ButtonState bs = lh->ons->getCurrentButtonState();
    
    if ( strcmp(str, bs.str) == 0 || 
        (strcmp(str, "ESC") == 0 && strcmp(bs.str, "RCLICK") == 0))
        lua_pushboolean( state, 1 );
    else
        lua_pushboolean( state, 0 );

    return 1;
}

int NSGetMouse(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    ONScripter::ButtonState bs = lh->ons->getCurrentButtonState();
    
    if (bs.x == lh->ons->getWidth() && bs.y == lh->ons->getHeight()){
        lua_pushinteger( state, -1 );
        lua_pushinteger( state, -1 );
    }
    else{
        lua_pushinteger( state, bs.x*lh->screen_ratio2/lh->screen_ratio1 );
        lua_pushinteger( state, bs.y*lh->screen_ratio2/lh->screen_ratio1 );
    }

    return 2;
}

int NSGetSkip(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);
    
    lua_pushinteger( state, lh->ons->getSkip() );

    return 1;
}

int NSGetStrValue(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int no = luaL_checkinteger( state, 1 );
    
    lua_pushstring( state, lh->sh->getVariableData(no).str );
    
    return 1;
}

int NSGetWindowSize(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    lua_pushinteger( state, lh->ons->getWidth() );
    lua_pushinteger( state, lh->ons->getHeight() );

    return 2;
}

int NSLuaAnimationInterval(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int val = lua_tointeger(state, 1);
    
    lh->duration_time = val;

    return 0;
}

int NSLuaAnimationMode(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int val = lua_toboolean(state, 1);
    
    lh->is_animatable = (val==1);
    if (lh->is_animatable) lh->next_time = SDL_GetTicks() + lh->duration_time;

    return 0;
}

int NSOggClose(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );

    sprintf(cmd_buf, "_dwavestop %d", no);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

// Not implemented correctly, fix me later.
int NSOggFade(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );
    int startvol= luaL_checkinteger( state, 2 );
    int endvol= luaL_checkinteger( state, 3 );
    int dur = luaL_checkinteger( state, 4 );
    int flag = lua_toboolean(state, 5);

    if (flag)
        sprintf(cmd_buf, "_dwavestop %d", no);
    else
        sprintf(cmd_buf, "_chvol %d %d", no, (endvol+10000)/100);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSOggLoad(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );
    const char *str = luaL_checkstring( state, 2 );

    sprintf(cmd_buf, "_dwaveload %d \"%s\"", no, str);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSOggPlay(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );
    int val = lua_toboolean(state, 2);

    if (val)
        sprintf(cmd_buf, "_dwaveplayloop %d", no);
    else
        sprintf(cmd_buf, "_dwaveplay %d", no);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSOggVolume(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );
    int val = luaL_checkinteger( state, 2 );

    sprintf(cmd_buf, "_chvol %d %d", no, (val+10000)/100);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSPopInt(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int val = lh->sh->getEndStatus();
    if (val & ScriptHandler::END_COMMA && !(val & ScriptHandler::END_COMMA_READ)){
        lua_pushstring( state, "LUAHandler::NSPopInt() no integer." );
        lua_error( state );
    }

    lua_pushnumber( state, lh->sh->readInt() );

    return 1;
}

int NSPopIntRef(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int val = lh->sh->getEndStatus();
    if (val & ScriptHandler::END_COMMA && !(val & ScriptHandler::END_COMMA_READ)){
        lua_pushstring( state, "LUAHandler::NSPopIntRef() no integer variable." );
        lua_error( state );
    }

    lh->sh->readVariable();
    if (lh->sh->current_variable.type != ScriptHandler::VAR_INT){
        lua_pushstring( state, "LUAHandler::NSPopIntRef() no integer variable." );
        lua_error( state );
    }
        
    lua_pushnumber( state, lh->sh->current_variable.var_no );

    return 1;
}

int NSPopStr(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int val = lh->sh->getEndStatus();
    if (val & ScriptHandler::END_COMMA && !(val & ScriptHandler::END_COMMA_READ)){
        lua_pushstring( state, "LUAHandler::NSPopStr() no string." );
        lua_error( state );
    }

    lua_pushstring( state, lh->sh->readStr() );

    return 1;
}

int NSPopStrRef(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int val = lh->sh->getEndStatus();
    if (val & ScriptHandler::END_COMMA && !(val & ScriptHandler::END_COMMA_READ)){
        lua_pushstring( state, "LUAHandler::NSPopStrRef() no string variable." );
        lua_error( state );
    }

    lh->sh->readVariable();
    if (lh->sh->current_variable.type != ScriptHandler::VAR_STR){
        lua_pushstring( state, "LUAHandler::NSPopStrRef() no string variable." );
        lua_error( state );
    }
        
    lua_pushnumber( state, lh->sh->current_variable.var_no );

    return 1;
}

int NSPopLabel(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int val = lh->sh->getEndStatus();
    if (val & ScriptHandler::END_COMMA && !(val & ScriptHandler::END_COMMA_READ)){
        lua_pushstring( state, "LUAHandler::NSPopLabel() no label." );
        lua_error( state );
    }

    const char *str = lh->sh->readLabel();
    if (str[0] != '*'){
        lua_pushstring( state, "LUAHandler::NSPopLabel() no label." );
        lua_error( state );
    }
        
    lua_pushstring( state, str+1 );

    return 1;
}

int NSPopID(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int val = lh->sh->getEndStatus();
    if (val & ScriptHandler::END_COMMA && !(val & ScriptHandler::END_COMMA_READ)){
        lua_pushstring( state, "LUAHandler::NSPopID() no ID." );
        lua_error( state );
    }

    lua_pushstring( state, lh->sh->readLabel() );

    return 1;
}

int NSPopComma(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int val = lh->sh->getEndStatus();
    if (!(val & ScriptHandler::END_COMMA) || val & ScriptHandler::END_COMMA_READ){
        lua_pushstring( state, "LUAHandler::NSPopComma() no comma." );
        lua_error( state );
    }
    
    lh->sh->setEndStatus( ScriptHandler::END_COMMA_READ );
    
    return 0;
}

int NSReturn(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);
    
    lh->ons->returnCommand();

    return 0;
}

int NSSetIntValue(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int no  = luaL_checkinteger( state, 1 );
    int val = luaL_checkinteger( state, 2 );
    
    lh->sh->setNumVariable( no, val );
    
    return 0;
}

int NSSetStrValue(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );
    LUAHandler *lh = (LUAHandler*)lua_topointer( state, -1 );

    int no = luaL_checkinteger( state, 1 );
    const char *str = NULL;
    if (lua_isstring( state, 2 ))
        str = luaL_checkstring( state, 2 );
    
    if (lh->sh->getVariableData(no).str)
        delete[] lh->sh->getVariableData(no).str;
    lh->sh->getVariableData(no).str = NULL;
    
    if (str){
        lh->sh->getVariableData(no).str = new char[strlen(str) + 1];
        strcpy(lh->sh->getVariableData(no).str, str);
    }
    
    return 0;
}

int NSSleep(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int val = luaL_checkinteger( state, 1 );

    sprintf(cmd_buf, "_wait %d", val);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSSp2Clear(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );

    sprintf(cmd_buf, "_csp2 %d", no);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSSp2GetInfo(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );

    AnimationInfo *ai = lh->ons->getSprite2Info(no);

    lua_pushinteger( state, ai->orig_pos.w / ai->num_of_cells );
    lua_pushinteger( state, ai->orig_pos.h );
    lua_pushinteger( state, ai->num_of_cells );

    return 3;
}

int NSSp2GetPos(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );

    AnimationInfo *ai = lh->ons->getSprite2Info(no);

    lua_pushinteger( state, ai->orig_pos.x );
    lua_pushinteger( state, ai->orig_pos.y );
    lua_pushinteger( state, ai->scale_x );
    lua_pushinteger( state, ai->scale_y );
    lua_pushinteger( state, ai->rot );
    lua_pushinteger( state, ai->trans );
    lua_pushinteger( state, ai->blending_mode );

    return 7;
}

int NSSp2Load(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );
    const char *str = luaL_checkstring( state, 2 );

    sprintf(cmd_buf, "_lsp2 %d, \"%s\", %d, 0, 100, 100, 0", no, str, lh->ons->getWidth()*2);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSSp2Move(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );
    int x  = luaL_checkinteger( state, 2 );
    int y  = luaL_checkinteger( state, 3 );
    int sx = luaL_checkinteger( state, 4 );
    int sy = luaL_checkinteger( state, 5 );
    int r  = luaL_checkinteger( state, 6 );
    int alpha = luaL_checkinteger( state, 7 );
    int opt = luaL_checkinteger( state, 8 );

    lh->ons->getSprite2Info(no)->blending_mode = opt;
    sprintf(cmd_buf, "_amsp2 %d, %d, %d, %d, %d, %d, %d", no, x, y, sx, sy, r, alpha);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSSpCell(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );
    int cell = luaL_checkinteger( state, 2 );

    sprintf(cmd_buf, "_cell %d, %d", no, cell);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSSpClear(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );

    sprintf(cmd_buf, "_csp %d", no);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSSpGetInfo(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );

    AnimationInfo *ai = lh->ons->getSpriteInfo(no);

    lua_pushinteger( state, ai->orig_pos.w / ai->num_of_cells );
    lua_pushinteger( state, ai->orig_pos.h );
    lua_pushinteger( state, ai->num_of_cells );

    return 3;
}

int NSSpGetPos(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );

    AnimationInfo *ai = lh->ons->getSpriteInfo(no);

    lua_pushinteger( state, ai->orig_pos.x );
    lua_pushinteger( state, ai->orig_pos.y );
    lua_pushinteger( state, ai->trans );

    return 3;
}

int NSSpLoad(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );
    const char *str = luaL_checkstring( state, 2 );

    sprintf(cmd_buf, "_lsp %d, \"%s\", %d, 0", no, str, lh->ons->getWidth()+1);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSSpMove(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );
    int x  = luaL_checkinteger( state, 2 );
    int y  = luaL_checkinteger( state, 3 );
    int alpha = luaL_checkinteger( state, 4 );

    sprintf(cmd_buf, "_amsp %d, %d, %d, %d", no, x, y, alpha);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSSpVisible(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    int no = luaL_checkinteger( state, 1 );
    int v  = lua_toboolean( state, 2 );

    sprintf(cmd_buf, "_vsp %d, %d", no, v);
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int NSTimer(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    lua_pushinteger( state, SDL_GetTicks() );

    return 1;
}

int NSUpdate(lua_State *state)
{
    lua_getglobal(state, ONS_LUA_HANDLER_PTR);
    LUAHandler *lh = (LUAHandler*)lua_topointer(state, -1);

    sprintf(cmd_buf, "_print 1");
    lh->sh->enterExternalScript(cmd_buf);
    lh->ons->runScript();
    lh->sh->leaveExternalScript();

    return 0;
}

int lua_dummy(lua_State *state)
{
    return 0;
}

#define LUA_FUNC_LUT(s) {#s, s}
#define LUA_FUNC_LUT_DUMMY(s) {#s, lua_dummy}
static const struct luaL_Reg lua_lut[] = {
    LUA_FUNC_LUT(NL_dofile),
    LUA_FUNC_LUT(NSCheckComma),
    LUA_FUNC_LUT(NSDCall),
    LUA_FUNC_LUT(NSDDLL),
    LUA_FUNC_LUT(NSDDelete),
    LUA_FUNC_LUT(NSDLoad),
    LUA_FUNC_LUT(NSDPresentRect),
    LUA_FUNC_LUT(NSDSp2),
    LUA_FUNC_LUT(NSDSetSprite),
    LUA_FUNC_LUT(NSDoEvents),
    LUA_FUNC_LUT(NSEnd),
    LUA_FUNC_LUT(NSExec),
    LUA_FUNC_LUT(NSExecAnimation),
    LUA_FUNC_LUT(NSGosub),
    LUA_FUNC_LUT(NSGoto),
    LUA_FUNC_LUT(NSGetClick),
    LUA_FUNC_LUT(NSGetIntValue),
    LUA_FUNC_LUT(NSGetKey),
    LUA_FUNC_LUT(NSGetMouse),
    LUA_FUNC_LUT(NSGetSkip),
    LUA_FUNC_LUT(NSGetStrValue),
    LUA_FUNC_LUT(NSGetWindowSize),
    LUA_FUNC_LUT(NSLuaAnimationInterval),
    LUA_FUNC_LUT(NSLuaAnimationMode),
    LUA_FUNC_LUT(NSOggClose),
    LUA_FUNC_LUT(NSOggFade),
    LUA_FUNC_LUT(NSOggLoad),
    LUA_FUNC_LUT(NSOggPlay),
    LUA_FUNC_LUT(NSOggVolume),
    LUA_FUNC_LUT_DUMMY(NSOkBox),
    LUA_FUNC_LUT(NSPopInt),
    LUA_FUNC_LUT(NSPopIntRef),
    LUA_FUNC_LUT(NSPopStr),
    LUA_FUNC_LUT(NSPopStrRef),
    LUA_FUNC_LUT(NSPopLabel),
    LUA_FUNC_LUT(NSPopID),
    LUA_FUNC_LUT(NSPopComma),
    LUA_FUNC_LUT(NSReturn),
    LUA_FUNC_LUT(NSSetIntValue),
    LUA_FUNC_LUT(NSSetStrValue),
    LUA_FUNC_LUT(NSSleep),
    LUA_FUNC_LUT(NSSp2Clear),
    LUA_FUNC_LUT(NSSp2GetInfo),
    LUA_FUNC_LUT(NSSp2GetPos),
    LUA_FUNC_LUT(NSSp2Load),
    LUA_FUNC_LUT(NSSp2Move),
    LUA_FUNC_LUT(NSSpCell),
    LUA_FUNC_LUT(NSSpClear),
    LUA_FUNC_LUT(NSSpGetInfo),
    LUA_FUNC_LUT(NSSpGetPos),
    LUA_FUNC_LUT(NSSpLoad),
    LUA_FUNC_LUT(NSSpMove),
    LUA_FUNC_LUT(NSSpVisible),
    LUA_FUNC_LUT(NSTimer),
    LUA_FUNC_LUT(NSUpdate),
    {NULL, NULL}
};

static int nsutf_from_ansi(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );

    const char *str = luaL_checkstring( state, 1 );
    size_t len = strlen(str)*3+1;
    if (conv_buf_len < len){
        if (conv_buf) delete[] conv_buf;
        conv_buf = new char[len];
        conv_buf_len = len;
    }
    DirectReader::convertFromSJISToUTF8(conv_buf, str);
    lua_pushstring( state, conv_buf );

    return 1;
}

static int nsutf_to_ansi(lua_State *state)
{
    lua_getglobal( state, ONS_LUA_HANDLER_PTR );

    const char *str = luaL_checkstring( state, 1 );
    size_t len = strlen(str)*2+1;
    if (conv_buf_len < len){
        if (conv_buf) delete[] conv_buf;
        conv_buf = new char[len];
        conv_buf_len = len;
    }
    DirectReader::convertFromUTF8ToSJIS(conv_buf, str);
    lua_pushstring( state, conv_buf );

    return 1;
}

static const struct luaL_Reg module_nsutf[] = {
    LUA_FUNC_LUT(nsutf_from_ansi),
    LUA_FUNC_LUT(nsutf_to_ansi),
    {NULL, NULL}
};

static const struct luaL_Reg module_dpshadow[] = {
    LUA_FUNC_LUT_DUMMY(dpshadow_make),
    LUA_FUNC_LUT_DUMMY(dpshadow_merge),
    {NULL, NULL}
};

//LUAHandler::LUAHandler(ONScripter *ons)
LUAHandler::LUAHandler()
{
    state = NULL;

    is_animatable = false;
    duration_time  = 15;
    next_time = 0;

    screen_ratio1 = 1;
    screen_ratio2 = 1;
    error_str[0] = 0;
    
    for (unsigned int i=0 ; i<MAX_CALLBACK ; i++)
        callback_state[i] = false;
}

LUAHandler::~LUAHandler()
{
    if (state) lua_close(state);
}

#if LUA_VERSION_NUM >= 502
extern "C" int luaopen_nsutf(lua_State *state)
{
    luaL_newlib(state, module_nsutf);

    return 1;
}

extern "C" int luaopen_dpshadow(lua_State *state)
{
    luaL_newlib(state, module_dpshadow);

    return 1;
}
#endif

void LUAHandler::init(ONScripter *ons, ScriptHandler *sh,
                      int screen_ratio1, int screen_ratio2)
{
    this->ons = ons;
    this->sh = sh;
    
    state = luaL_newstate();
    luaL_openlibs(state);

#if LUA_VERSION_NUM >= 502
    lua_pushglobaltable(state);
    luaL_setfuncs(state, lua_lut, 0);
    luaL_setfuncs(state, module_dpshadow, 0);
    luaL_setfuncs(state, module_nsutf, 0);
    luaL_requiref(state, "nsutf", luaopen_nsutf, 1);
    luaL_requiref(state, "dpshadow", luaopen_dpshadow, 1);
#else
    lua_pushvalue(state, LUA_GLOBALSINDEX);
    luaL_register(state, NULL, lua_lut);
    luaL_register(state, NULL, module_nsutf);
    luaL_register(state, NULL, module_dpshadow);
    luaL_register(state, "nsutf", module_nsutf);
    luaL_register(state, "dpshadow", module_dpshadow);
#endif
    
    lua_pushlightuserdata(state, this);
    lua_setglobal(state, ONS_LUA_HANDLER_PTR);
}

void LUAHandler::loadInitScript()
{
    unsigned long length = sh->cBR->getFileLength(INIT_SCRIPT);
    if (length == 0){
        printf("cannot open %s\n", INIT_SCRIPT);
        return;
    }

    unsigned char *buffer = new unsigned char[length+1];
    int location;
    sh->cBR->getFile(INIT_SCRIPT, buffer, &location);
    buffer[length] = 0;

    unsigned char *buffer2 = new unsigned char[length*3/2];
    unsigned char *p = buffer;
    unsigned char *p2 = buffer2;
    while(*p){
        int n = sh->enc.getBytes(*p);
        for (int i=0 ; i<n; i++){
            if (i > 0 && *p == '\\') *p2++ = '\\';
            *p2++ = *p++;
        }
    }

    if (luaL_loadbuffer(state, (const char*)buffer2, p2 - buffer2, INIT_SCRIPT) || 
        lua_pcall(state, 0, 0, 0)){
        printf("cannot parse %s %s\n", INIT_SCRIPT, lua_tostring(state,-1));
    }

    delete[] buffer;
    delete[] buffer2;
}

void LUAHandler::addCallback(const char *label)
{
    if (strcmp(label, "tag") == 0)
        callback_state[LUA_TAG] = true;
    if (strcmp(label, "text0") == 0)
        callback_state[LUA_TEXT0] = true;
    if (strcmp(label, "text") == 0)
        callback_state[LUA_TEXT] = true;
    if (strcmp(label, "animation") == 0)
        callback_state[LUA_ANIMATION] = true;
    if (strcmp(label, "close") == 0)
        callback_state[LUA_CLOSE] = true;
    if (strcmp(label, "end") == 0)
        callback_state[LUA_END] = true;
    if (strcmp(label, "savepoint") == 0)
        callback_state[LUA_SAVEPOINT] = true;
    if (strcmp(label, "save") == 0)
        callback_state[LUA_SAVE] = true;
    if (strcmp(label, "load") == 0)
        callback_state[LUA_LOAD] = true;
    if (strcmp(label, "reset") == 0)
        callback_state[LUA_RESET] = true;
}

int LUAHandler::callFunction(bool is_callback, const char *cmd, void *data)
{
    char cmd2[64];
    
    if (is_callback)
        sprintf(cmd2, "NSCALL_%s", cmd);
    else
        sprintf(cmd2, "NSCOM_%s", cmd);

    lua_getglobal(state, cmd2);

    int num_argument_value = 0;
    int num_return_value = 0;
    char *buf=NULL;
    if (strcmp(cmd2, "NSCALL_animation") == 0)
        num_return_value = 1;
    else if (strcmp(cmd2, "NSCALL_load") == 0){
        num_argument_value = 1;
        lua_pushinteger(state, *(int*)data);
    }
    else if (strcmp(cmd2, "NSCALL_text") == 0){
        num_argument_value = 1;
        char *p = sh->getStringBuffer()+ons->getStringBufferOffset();
        buf = new char[strlen(p)+1];
        memcpy(buf, p, strlen(p)+1);
        lua_pushstring(state, buf);
    }
        
    if (lua_pcall(state, num_argument_value, num_return_value, 0) != 0){
        strcpy( error_str, lua_tostring(state, -1) );
        return -1;
    }

    if (strcmp(cmd2, "NSCALL_animation") == 0){
        if (lua_isboolean(state, -1) && lua_toboolean(state, -1)){
            sprintf(cmd2, "NSUpdate");
            lua_getglobal(state, cmd2);
            if (lua_pcall(state, 0, 0, 0) != 0){
                strcpy( error_str, lua_tostring(state, -1) );
                return -1;
            }
        }
    }

    if (buf) delete[] buf;

    return 0;
}

bool LUAHandler::isCallbackEnabled(int val)
{
    return callback_state[val];
}
