/*
 * AXFLuaInternal.cpp
 */
#ifdef USE_LUA

#include "AXFLuaInternal.hpp"
#include "AXFDaemonInternal.hpp"
#include "AXFLog.hpp"

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

const UINT32_t AXFLuaInternal::luaStrMaxSize = LUAL_BUFFERSIZE;
const UINT32_t AXFLuaInternal::luaFileNameMaxSize = 256;

const static char* axLuaGValueActor = "actor";
const static char* axLuaGValueLog = "log";

// @todo: AXShell Lua対応時に整理する
extern const char* cmdStringBreakInfo;
extern const char* cmdStringBreakAdd;
extern const char* cmdStringBreakSetdate;
extern const char* cmdStringBreakLifeCycle;
extern const char* cmdStringBreakStateMachine;
extern const char* cmdStringBreakDel;

static int axlibLuaSend (lua_State* L, AXFEvent_t event, void* param,
    int paramSize, int stageIndex) {

  /*
   * Actorオブジェクト・ポインタとLogオブジェクト・ポインタを
   * Luaグローバル値から取得する
   */

  // "actor" 値と "log" 値を、一時的に Lua仮想スタックへ push する
  lua_getglobal(L, axLuaGValueActor);
  lua_getglobal(L, axLuaGValueLog);

  // "actor" 値と "log" 値を Lua仮想スタックから取得 する
  AXFActor* actor = (AXFActor*)lua_touserdata(L, -2);
  AXFLog* log = (AXFLog*)lua_touserdata(L, -1);

  // 一時的に Lua仮想スタックに push した "actor" 値と "log" 値を pop する
  lua_pop(L, 2);



  /*
   * axdaemon に指定されたイベント値とパラメータを通知する
   */

  // Lua仮想スタックに push された引数の数を取得する
  int n = lua_gettop(L);

  std::string stageName;
  if (stageIndex <= n) {
    // 引数の型チェック
    luaL_argcheck(L, (1 == lua_isstring(L, stageIndex)), stageIndex, "stageName");

    // Lua仮想スタックから宛先Stage名を取得する
    stageName = lua_tostring(L, stageIndex);
  } else {
    // 宛先Stage名が指定されていない場合、自Stage名を設定する
    stageName = AXConfig.getStageNameObject().getStageName();
  }
  stageName += ".axdaemon";
  AXFObjectName daemonName(stageName);

  // axdaemon にイベント通知する
  if (0 > actor->send(daemonName, event, param, paramSize)) {
    std::ostringstream evnetStr;
    evnetStr << std::hex << event;
    log->write(AXFLOG_ERR, "fail send" + evnetStr.str());
   }

  // Lua仮想スタックに戻り値を push する
  lua_pushnumber(L, AXFLuaInternal::LUA_STATUS_OK);
  return 1; /* 戻り値の数 */
}

static int l_logon (lua_State* L) {
  // logon [StageName]
  return axlibLuaSend(L, AXFDaemonInternal::EVENT_LOG_ON, NULL, 0, 1);
}

static int l_logoff (lua_State* L) {
  // logoff [StageName]
  return axlibLuaSend(L, AXFDaemonInternal::EVENT_LOG_OFF, NULL, 0, 1);
}

static int l_loglevel (lua_State* L) {
  // log level [StageName]

  // Lua仮想スタックにPushされた引数の数を取得する
  int n = lua_gettop(L);
  int level;

  // 引数の数チェック(最低でもLog Level値は必要)
  luaL_argcheck(L, (1 <= n), n, "no param");

  // Log Level値の型チェック
  luaL_argcheck(L, (1 == lua_isnumber(L, 1)), 1, "Log level type");

  // Lua仮想スタックからLog Level値を取得する
  level = lua_tointeger(L, 1);

  // Log Level値の範囲チェック
  luaL_argcheck(L, ((AXFLOG_DEBUG <= level) && (level <= AXFLOG_EMERG)), level, "Log level value");

  return axlibLuaSend(L, AXFDaemonInternal::EVENT_LOG_LEVEL, &level, sizeof(level), 2);
}

static int l_logtranson (lua_State* L) {
  // logtranson [StageName (defalut = Self Stage)]
  return axlibLuaSend(L, AXFDaemonInternal::EVENT_LOGTRANS_ON, NULL, 0, 1);
}

static int l_logtransoff (lua_State* L) {
  // logtransoff [StageName (defalut = Self Stage)]
  return axlibLuaSend(L, AXFDaemonInternal::EVENT_LOGTRANS_OFF, NULL, 0, 1);
}

static int l_info (lua_State* L) {
  // info [StageName (defalut = Self Stage)]
  return axlibLuaSend(L, AXFDaemonInternal::EVENT_INFO, NULL, 0, 1);
}

static int l_rinfo (lua_State* L) {
  // rinfo [StageName (defalut = Self Stage)]
  return axlibLuaSend(L, AXFDaemonInternal::EVENT_RINFO, NULL, 0, 1);
}

static int l_create (lua_State* L) {
  // create [StageName (defalut = Self Stage)]
  return axlibLuaSend(L, AXFDaemonInternal::EVENT_CREATE_AXM, NULL, 0, 1);
}

static int l_run (lua_State* L) {
  // run [StageName (defalut = Self Stage)]
  return axlibLuaSend(L, AXFDaemonInternal::EVENT_RUN_AX, NULL, 0, 1);
}

static int l_setdate (lua_State* L) {
  // setdate "YYYY/MM/DD-hh:mm:ss.ttttt" [StageName (defalut = Self Stage)]

  // Lua仮想スタックにPushされた引数の数を取得する
  int n = lua_gettop(L);

  // 引数の数チェック(最低でも日時情報は必要)
  luaL_argcheck(L, (1 <= n), n, "no param");

  // 日時情報の型チェック
  luaL_argcheck(L, (1 == lua_isstring(L, 1)), 1, "Date type");

  // Lua仮想スタックから日付情報値を取得する
  std::string dateStr = lua_tostring(L, 1);

  // 日時情報を axdaemon 用パラメータに変換する
  AXFChrono::TimeSpec date;
  AXFChrono chrono;
  luaL_argcheck(L, (chrono.set(dateStr) == AXFChrono::CHRONO_SUCCESS), 1,
                "date format is \'YYYY/MM/DD-hh:mm:ss.ttttt\'");
  date = chrono.getCounter();

  return axlibLuaSend(L, AXFDaemonInternal::EVENT_SETDATE, &date, sizeof(date), 2);
}

static int l_quit (lua_State* L) {
  // quit [StageName (defalut = Self Stage)]
  return axlibLuaSend(L, AXFDaemonInternal::EVENT_QUIT_AX, NULL, 0, 1);
}

static int l_tdon (lua_State* L) {
  // tdon [StageName (defalut = Self Stage)]
  return axlibLuaSend(L, AXFDaemonInternal::EVENT_ENABLE_TIMEDOMAIN, NULL, 0, 1);
}

static int l_tdoff (lua_State* L) {
  // tdoff [StageName (defalut = Self Stage)]
  return axlibLuaSend(L, AXFDaemonInternal::EVENT_DISABLE_TIMEDOMAIN, NULL, 0, 1);
}

static int l_tdbreak (lua_State* L) {
  // tdbreak [subcmd] [subparam] [StageName (defalut = Self Stage)]

  // Lua仮想スタックにPushされた引数の数を取得する
  int n = lua_gettop(L);

  AXFDaemonInternal::ParamBreak paramBreak;
  int stageIndex = 2;
  AXFEvent_t event = AXFDaemonInternal::EVENT_BREAK_INFO;
  void* param = NULL;
  int pSize = 0;
  const char* subCmd = NULL;

  if (1 <= n) {
    // break サブコマンドの型チェック
    luaL_argcheck(L, (1 == lua_isstring(L, 1)), 1, "subCmd type");

    // Lua仮想スタックからbreakサブコマンドを取得する
    subCmd = lua_tostring(L, 1);
  }

  if ((0 == n) || ((1 == n) && (0 != strcmp(subCmd, cmdStringBreakInfo)))) {
    // break [StageName (defalut = Self Stage)]
    stageIndex = 1;
    event = AXFDaemonInternal::EVENT_BREAK_FORCE;
    param = NULL;
    pSize = 0;
  } else if ((2 <= n) && (0 == strcmp(subCmd, cmdStringBreakAdd))) {
    // break add tick [StageName (defalut = Self Stage)]

    // Tick値の型チェック
    luaL_argcheck(L, (1 == lua_isnumber(L, 2)), 2, "Tick type");

    // Lua仮想スタックからTick値を取得する
    paramBreak.add.breakTick = lua_tointeger(L, 2);
    stageIndex = 3;
    event = AXFDaemonInternal::EVENT_BREAK_ADD;
    param = &paramBreak.add;
    pSize = sizeof(paramBreak.add);
  } else if ((2 <= n) && (0 == strcmp(subCmd, cmdStringBreakSetdate))) {
    // break setdate "yyyy/MM/dd-hh:mm:ss.aaaaaa" [StageName (defalut = Self Stage)]

    // 日時情報の型チェック
    luaL_argcheck(L, (1 == lua_isstring(L, 2)), 2, "Date type");

    // Lua仮想スタックから日付情報値を取得する
    std::string dateStr = lua_tostring(L, 2);

    // 日時情報を axdaemon 用パラメータに変換する
    AXFChrono chrono;
    luaL_argcheck(L, (chrono.set(dateStr) == AXFChrono::CHRONO_SUCCESS), 2,
                  "date format is \'YYYY/MM/DD-hh:mm:ss.ttttt\'");

    stageIndex = 3;
    paramBreak.setdate.breakDate = chrono.getCounter();
    event = AXFDaemonInternal::EVENT_BREAK_SETDATE;
    param = &paramBreak.setdate;
    pSize = sizeof(paramBreak.setdate);
  } else if ((3 <= n) && (0 == strcmp(subCmd, cmdStringBreakLifeCycle))) {
    // break lc actor state [StageName (defalut = Self Stage)]

    // アクタ名型チェック
    luaL_argcheck(L, (1 == lua_isstring(L, 2)), 2, "Actor Name type");

    // Lua仮想スタックからアクタ名を取得する
    const char* actorName = lua_tostring(L, 2);

    // State値の型チェック
    luaL_argcheck(L, (1 == lua_isnumber(L, 3)), 3, "State type");

    // Lua仮想スタックからState値を取得する
    AXFActor::actorState n = static_cast<AXFActor::actorState>(lua_tointeger(L, 3));

    strncpy(paramBreak.lifeCycle.fullActorName, actorName,
            sizeof(paramBreak.lifeCycle.fullActorName) - 1);
    paramBreak.lifeCycle.fullActorName[sizeof(paramBreak.lifeCycle.fullActorName) - 1] = '\0';
    paramBreak.lifeCycle.state = n;
    stageIndex = 4;
    event = AXFDaemonInternal::EVENT_BREAK_LIFECYCLE;
    param = &paramBreak.lifeCycle;
    pSize = sizeof(paramBreak.lifeCycle);
  } else if ((3 <= n) && (0 == strcmp(subCmd, cmdStringBreakStateMachine))) {
    // break sm actorName state [StageName (defalut = Self Stage)]

    // アクタ名型チェック
    luaL_argcheck(L, (1 == lua_isstring(L, 2)), 2, "Actor Name type");

    // Lua仮想スタックからアクタ名を取得する
    const char* actorName = lua_tostring(L, 2);

    // State値の型チェック
    luaL_argcheck(L, (1 == lua_isnumber(L, 3)), 3, "State type");

    // Lua仮想スタックからState値を取得する
    INT32_t state = lua_tointeger(L, 3);

    strncpy(paramBreak.stateMachine.fullActorName, actorName,
            sizeof(paramBreak.stateMachine.fullActorName) - 1);
    paramBreak.stateMachine.fullActorName[sizeof(paramBreak.stateMachine.fullActorName) - 1] = '\0';
    paramBreak.stateMachine.state = state;
    stageIndex = 4;
    event = AXFDaemonInternal::EVENT_BREAK_STATEMACHINE;
    param = &paramBreak.stateMachine;
    pSize = sizeof(paramBreak.stateMachine);
  } else if ((2 <= n) && (0 == strcmp(subCmd, cmdStringBreakDel))) {
    // break del ID [StageName (defalut = Self Stage)]

    // Break ID の型チェック
    luaL_argcheck(L, (1 == lua_isnumber(L, 2)), 2, "Break ID type");

    // Lua仮想スタックから Break ID値を取得する
    INT32_t breakId = lua_tointeger(L, 2);

    stageIndex = 3;
    event = AXFDaemonInternal::EVENT_BREAK_DEL;
    paramBreak.del.breakId = breakId;
    param = &paramBreak.del;
    pSize = sizeof(paramBreak.del);
  }

  return axlibLuaSend(L, event, param, pSize, stageIndex);
}

static int l_next (lua_State* L) {
  // next [StageName (defalut = Self Stage)]
  return axlibLuaSend(L, AXFDaemonInternal::EVENT_NEXT, NULL, 0, 1);
}

static const struct luaL_Reg axlib [] = {
  {"logon", l_logon},
  {"logoff", l_logoff},
  {"log", l_loglevel},
  {"logtranson", l_logtranson},
  {"logtransoff", l_logtransoff},
  {"info", l_info},
  {"rinfo", l_rinfo},
  {"setdate", l_setdate},
  {"create", l_create},
  {"run", l_run},
  {"quit", l_quit},
  {"tdon", l_tdon},
  {"tdoff", l_tdoff},
  {"tdbreak", l_tdbreak},
  {"next", l_next},
  {NULL, NULL}
};

AXFLuaInternal::AXFLuaInternal(AXFActor* actor, AXFLog* log)
    : m_actor(actor), m_log(log), m_luaState(NULL) {
}

AXFLuaInternal::~AXFLuaInternal() {
  if (m_luaState) {
    // Lua を close する
    lua_close(m_luaState);
  }
  m_luaState = NULL;
}

AXFLuaInternal::luaStatus AXFLuaInternal::init() {
  if (m_luaState) {
    return LUA_STATUS_BUSY;
  }

  /*
   * Lua の初期化
   */

  // Lua Stateを生成する
  m_luaState = luaL_newstate();
  if (NULL == m_luaState) {
    return LUA_STATUS_FAILNEW;
  }

  // Lua 標準ライブラリを open する
  luaL_openlibs(m_luaState);

  // AX Lua ライブラリ open する
  luaL_register(m_luaState, "ax", axlib);


  /*
   * Actorオブジェクト・ポインタとLogオブジェクト・ポインタを
   * Luaグローバル値として登録し、 AX Luaライブラリから参照できるようにする
   */

  // Actorオブジェクト・ポインタ を Luaグローバル値に設定するために、
  // 一時的に Lua仮想スタックに push する
  lua_pushlightuserdata(m_luaState, m_actor);

  // Lua仮想スタックから値を pop して、これを Luaグローバル値 "actor"とする
  lua_setglobal(m_luaState, axLuaGValueActor);

  // Logオブジェクト・ポインタ を Luaグローバル値に設定するために、
  // 一時的に Lua仮想スタックに push する
  lua_pushlightuserdata(m_luaState, m_log);

  // Lua仮想スタックから値を pop して、これを Luaグローバル値 "log" とする
  lua_setglobal(m_luaState, axLuaGValueLog);

  return LUA_STATUS_OK;
}

AXFLuaInternal::luaStatus AXFLuaInternal::doString(const std::string& str) {
  if (NULL == m_luaState) {
    return LUA_STATUS_NOINIT;
  }

  return static_cast<luaStatus>luaL_dostring(m_luaState, str.c_str());
}

AXFLuaInternal::luaStatus AXFLuaInternal::doFile(const std::string& fileName) {
  if (NULL == m_luaState) {
    return LUA_STATUS_NOINIT;
  }
  return static_cast<luaStatus>luaL_dofile(m_luaState, fileName.c_str());
}

#endif /* USE_LUA */
