//  UtilityPlugin.cpp
//  MA_Plugin_x64
//
//  Created by Pasi Mankinen on 9.10.2012.
//  Copyright (c) 2012 Manage Applications. All rights reserved.
//

#include "Includes.h"
#include "UtilityPlugin.h"


// ---------------------------------------------------------
// globals
static std::map<string, lua_State*> luaStateTbl;
static std::map<string, lua_State*>::iterator luaStateIdx;
static double maxReal; // = pow(2.0, 49); // in init
static string traceBackString = ""; // = pow(2.0, 49); // in init

void InitUtility() // called from MA_Plugin_x64.cpp
{
  maxReal = pow(2.0, 49);
}

void DeInitUtility() // called from MA_Plugin_x64.cpp
{
  lua_State *L;
  for(luaStateIdx = luaStateTbl.begin(); luaStateIdx != luaStateTbl.end(); luaStateIdx++) {
    L = luaStateIdx->second;
    if(L){ // just to be sure
      unLoadLua(L);
    }
  }
  luaStateTbl.clear();
}

void CloseProcessUtility() // called from MA_Plugin_x64.cpp
{
  lua_State *L = lua_state_ref_get();
  if( L ){
    unLoadLua(L);
    string key =lua_state_key();
    // todo: do not show error if we are in server
    plg_ErrMessage( L"err: state existed on close process: " + plg_StringToWstring(key), L"CloseProcessUtility()" );
  }
}


long MA_UtilityPluginMain( long selector, PA_PluginParameters params )
{
  
	switch( selector )
	{
      
		case eCMD_MA_Microseconds :
    {
      MA_Microseconds( params );
      break;
    }
		case eCMD_MA_Milliseconds :
    {
      MA_Milliseconds( params );
      break;
    }
		case eCMD_MA_Seconds :
    {
      MA_Seconds( params );
      break;
    }

		case eCMD_MA_Markdown :
    {
      MA_Markdown( params );
      break;
    }
      
		case eCMD_MA_LuaBlob :
    {
      MA_LuaBlob( params );
      break;
    }
      
		case eCMD_MA_LuaText :
    {
      MA_LuaText( params );
      break;
    }
            
      
      
		default:
			selector = -1; // was not used
			break;
      
	}
  return selector;
}

/*
#include <CoreServices/CoreServices.h> // AbsoluteToNanoseconds(UpTime());
unsigned long long GetTimeSinceBootInMilliseconds()
{
  UnsignedWide uw = AbsoluteToNanoseconds(UpTime());
  return ((((unsigned long long)uw.hi)<<32)|(uw.lo))/1000000;
}
*/

double seconds(int multiplier){
	uint64_t	returnValue64;
	double returnValue;
  
#if VERSIONWIN
  
  //  Get the high resolution counter's accuracy.
  LARGE_INTEGER ticksPerSecond;
  QueryPerformanceFrequency (&ticksPerSecond);
  
  //  What time is it?
  LARGE_INTEGER tick;
  QueryPerformanceCounter (&tick);
  
  //  Convert the tick number into the number of seconds
  //  since the system was started.
  //double ticks_div = (double) (ticksPerSecond.QuadPart / 1000000);
  //returnValue64 = (uint64_t) (tick.QuadPart / ticks_div);
  
  //double dTime = double(tick.QuadPart)/double(ticksPerSecond.QuadPart); // this gives time in seconds?
  returnValue64 = ((uint64_t)tick.QuadPart * (uint64_t)100000) / ((uint64_t)ticksPerSecond.QuadPart / (uint64_t)1000); // time in microseconds
	
#else
  
  //  Use POSIX gettimeofday function to get precise time.
  struct timeval tv;
  int rc = gettimeofday (&tv, NULL);
  if (rc != 0){
    returnValue64 = -1; // error here
  } else {
    returnValue64 = (uint64_t)(tv.tv_sec * (long)1000000);
    returnValue64 += (uint64_t)tv.tv_usec;
  }
  
#endif
	/* convert uint64 to uint32
   uint64 num = 6294967296;
   uint32_t lsb = num & 0xffffffff;
   uint32_t msb = num >> 32;
   */
  uint32_t msb = returnValue64 >> 32;
  //returnValue = (long)(returnValue64 & 0xffffffff); // get lsb = smallest part (in little endian)
	//returnValue = returnValue & 0x7fffffff;		// 0x7fffffff = MAXINT, clear biggest bit > 7 to _not_to_ get negative number
    
  //double maxReal = DBL_MAX;
  //double maxReal = pow(2.0, 49); // in init
  if ( msb > 0x00ffffff ){//( returnValue64 > maxReal ){ // maxRealIn4D is defined in ConstStr.h
    // get rid of highest bits
    returnValue64 = returnValue64 & 0x00ffffff; // lsb = least signifigant bit = small part on little-endian processor
  
    // returnValue = static_cast<double>(returnValue64); // for test
    //uint32_t highPart = returnValue64 >> 32; // msb = most signifigant bit, does NOT work in PPC processor!!!
    //uint32_t lowPart = returnValue64 & 0xffffffff; // lsb = least signifigant bit = small part on little-endian processor
    //returnValue = highPart * 4294967296.0f + lowPart;
    
    /*
    uint32_t msb = returnValue64 >> 32;         // msb = most signifigant bit, does NOT work in PPC processor!!!
    msb = msb & 0x00ffffff; // reset 2 highest bits to get lower number, f=4 bits 8f = 32 bits, we loose 2f = 16 bits to get 64-16 = 48 bits (49 bits is max)
    returnValue64 = msb;
    returnValue64 = returnValue64 << 32;
    returnValue64 = returnValue64 | lsb;
    */
    
    // pow(2,48) = 281474976710656 /1000/1000/60 = 4 691 249 seconds = 78 187 hours = 3 257 days
    // 0x00ffffff could be 0x4300000000000000 or below, but we want to be safe, negative?
    
    // http://4d.1045681.n5.nabble.com/Biggest-whole-number-in-a-C-REAL-td1396169.html
    // So anyway my original result is proved incorrect. It now appears that
    // 2^49 (562,949,953,421,312) is the practical limit of C_REAL whole
    // number calculations in 4D.
     
    // double maxReal = pow(2.0, 49);
    // maxReal	double	5.6295e+14
    // maxReal	double	4827858800541171712
    // maxReal	double	0x4300000000000000
    
    // double maxReal = DBL_MAX; // DBL_MAX is in C header file float.h
    // maxReal	double	1.79769e+308
    // maxReal	double	0x7fefffffffffffff
    // maxReal	double	9218868437227405311
    
  
    // memcpy(&returnValue, &returnValue64, sizeof(double)); // this copies 8 bytes exactly but does not work because double has different representation of numbers
  }
  
  returnValue = static_cast<double>(returnValue64); // this can do overflow
  
#if VERSIONWIN
  if(multiplier==1){
    returnValue = returnValue / (double)100000000; //seconds -> microseconds
  } else if(multiplier==2){
    returnValue = returnValue / (double)100000; // seconds -> milliseconds
  } else {
		returnValue = returnValue / (double)100;
	}
#else
  if(multiplier==1){
    returnValue = returnValue / (double)1000000; // microseconds -> second
  } else if(multiplier==2){
    returnValue = returnValue / (double)1000; // microseconds -> milliseconds
  }
#endif
  
  return returnValue;
}

void MA_Microseconds( PA_PluginParameters params ){
  double ms = seconds(3);
  //ms = ms * double(1000000.0); // 1000000 = 1000*1000;
  double prevMs = PA_GetDoubleParameter( params, 1 );
  if( prevMs > ms ){
    ms = prevMs - ms;
  } else {
    ms = ms - prevMs;
  }
	PA_ReturnDouble( params, ms );
}
void MA_Milliseconds( PA_PluginParameters params ){
  double ms = seconds(2);
  //ms = ms * double(1000.0);
  double prevMs = PA_GetDoubleParameter( params, 1 );
  if( prevMs > ms ){
    ms = prevMs - ms;
  } else {
    ms = ms - prevMs;
  }
	PA_ReturnDouble( params, ms  );
}
void MA_Seconds( PA_PluginParameters params ){
  double ms = seconds(1);
  double prevMs = PA_GetDoubleParameter( params, 1 );
  if( prevMs > ms ){
    ms = prevMs - ms;
  } else {
    ms = ms - prevMs;
  }
	PA_ReturnDouble( params, ms );
}

void MA_Markdown( PA_PluginParameters params ){
	long returnValue = kNoErr;
  
  PA_Handle inBlobHandle = PA_GetBlobHandleParameter( params, 1 );
  if ( inBlobHandle == NULL ) {
    returnValue = -1;
  } else {
    const char* inDoc = PA_LockHandle( inBlobHandle );
    // uint8_t *inDoc;
    // uint8_t *outDoc;
    long inDocSize = PA_GetHandleSize( inBlobHandle );
    // PA_GetBlobHandleParameter( params, 2 ); == return blob
    long inExtensions = PA_GetLongParameter( params, 3 );
    long inRendererFlags = PA_GetLongParameter( params, 4 );
    long inTypography = PA_GetLongParameter( params, 5 );
    
    struct buf *inbuf, *outbuf, *spbuf, *outData;
    
    struct sd_callbacks callbacks;
    struct html_renderopt options;
    struct sd_markdown *markdown;
    
    /* reading everything */
    inbuf = bufnew(128);
    // int32_t targetSize = (inDocSize * OVERHEAD_GUESS);
    // bufput(inbuf, inDoc, targetSize);
    // if you use OVERHEAD_GUESS then outbuf size may be bigger than actual output and you will get bad text
    bufput(inbuf, inDoc, inDocSize);
    
    /* performing markdown parsing */
    outbuf = bufnew(128);
    sdhtml_renderer(&callbacks, &options, inRendererFlags);
    markdown = sd_markdown_new(inExtensions, 16, &callbacks, &options);
    
    sd_markdown_render(outbuf, inbuf->data, inbuf->size, markdown);
    sd_markdown_free(markdown);
    bufrelease(inbuf);
    
    if( inTypography > 0 ){
      // http://daringfireball.net/projects/smartypants/
      spbuf = bufnew(128);
      bufgrow(spbuf, outbuf->size);
      sdhtml_smartypants(spbuf, outbuf->data, outbuf->size);
      outData = spbuf;
    } else {
      outData = outbuf;
    }
    
    // return result
    if( returnValue == kNoErr ){
      // PA_GetBlobHandleParameter( params, 2 );
      // PA_SetBlobParameter ( params, 2, outData->data, outData->size );
      plg_SetBlobParameter ( params, 2, (char*)outData->data, outData->size );
      returnValue = outData->size;
    }
    
    //  cleanup
    bufrelease(outbuf);
    if( inTypography > 0 ){
      bufrelease(spbuf);
    }
    // bufrelease(outData); // this just points to outbuf or spbuf, it does not need to be released
    
    PA_UnlockHandle( inBlobHandle );
  }
  
	PA_ReturnLong( params, returnValue );
  
  /*
  // inExtensions:
  enum mkd_extensions {
    MKDEXT_NO_INTRA_EMPHASIS = (1 << 0), // no_intra_emphasis
    MKDEXT_TABLES = (1 << 1), // tables
    MKDEXT_FENCED_CODE = (1 << 2), // fenced_code_blocks
    MKDEXT_AUTOLINK = (1 << 3), // autolink
    MKDEXT_STRIKETHROUGH = (1 << 4), // strikethrough
    MKDEXT_SPACE_HEADERS = (1 << 6), // space_after_headers
    MKDEXT_SUPERSCRIPT = (1 << 7), // superscript
    MKDEXT_LAX_SPACING = (1 << 8), // lax_spacing
  };
  
  // inRendererFlags (=HtmlOptions):
  typedef enum {
    HTML_SKIP_HTML = (1 << 0), // filter_html
    HTML_SKIP_STYLE = (1 << 1), // no_styles
    HTML_SKIP_IMAGES = (1 << 2), // no_images
    HTML_SKIP_LINKS = (1 << 3), // no_links
    HTML_EXPAND_TABS = (1 << 4), // expand_tabs
    HTML_SAFELINK = (1 << 5), // safe_links_only
    HTML_TOC = (1 << 6), // with_toc_data
    HTML_HARD_WRAP = (1 << 7), // hard_wrap
    HTML_USE_XHTML = (1 << 8), // xhtml
    HTML_ESCAPE = (1 << 9), // html_escape (?)
  } html_render_mode;
   */
  
}


void MA_LuaBlob( PA_PluginParameters params ){
	long returnValue = kNoErr;
  
  PA_Handle commandBlobHandle, paramBlobHandle; //, returnBlobHandle;
  
  commandBlobHandle = PA_GetBlobHandleParameter( params, 1 );
  if ( commandBlobHandle == NULL ) {
    returnValue = -1;
  } else {
    paramBlobHandle = PA_GetBlobHandleParameter( params, 2 );
    if ( paramBlobHandle == NULL ) {
      returnValue = -2;
    }
  }
  
  if( returnValue == kNoErr ){
    const char* blobCommand = PA_LockHandle( commandBlobHandle );
    const char* blobParam = PA_LockHandle( paramBlobHandle );
    const char* command = blobCommand + 2; // remove "x:" or "s:" or "f:" from front
    //const char* dataPtr = NULL;
    string returnTxt = "error";
    
    if( strncmp( blobCommand, "x:", 2 ) == 0 ){
      returnTxt = luaScriptCallFunction( command, blobParam );
    } else if( strncmp( blobCommand, "s:", 2 ) == 0 ){ // strncmp: A zero value indicates that the characters compared in both strings form the same string.
      returnTxt = luaScriptRunChar( command, blobParam, 0 );
    } else if ( strncmp( blobCommand, "f:", 2 ) == 0 ){
      returnTxt = luaScriptRunChar( command, blobParam, 1 );
    } else if ( strncmp( blobCommand, "l:", 2 ) == 0 ){
      lua_State *L = loadLua();
      if( L ){
        returnTxt = "loaded";
      } else {
        returnTxt = "already loaded";
      }
    } else if ( strncmp( blobCommand, "u:", 2 ) == 0 ){
      lua_State *L = lua_state_ref_get();
      if( L ){
        unLoadLua(L);
        returnTxt = "unloaded";
      } else {
        returnTxt = "does not exist";
      }
    } else if ( strncmp( blobCommand, "z:", 2 ) == 0 ){
      returnTxt = blobCommand;
    } else {
      returnTxt = "err: Command did not start with 'x:'[function call] or 'f:'[script filepath] or 's:'[script text] or 'l:'[load] or 'u:'[unload] (MA_LxBlob)";
      returnValue = -2;
    }
    PA_UnlockHandle( commandBlobHandle );
    PA_UnlockHandle( paramBlobHandle );
    
    long dataLen = returnTxt.length();
    if( returnValue == kNoErr ){
      // return result
      // memory leak with: PA_SetBlobParameter ( params, 3, (void *)returnTxt.data(), dataLen );
      char* returnData = (char *)returnTxt.data();
      returnValue = plg_SetBlobParameter ( params, 3, returnData, dataLen );
    }
    if( returnValue == kNoErr ){
      returnValue = dataLen;
    }
  }
  
  PA_ReturnLong( params, returnValue );
}


void MA_LuaText( PA_PluginParameters params ){
	string luaCommand, luaParam, luaCommandType, returnTxt;
	
	// plg_GetStringParameter( params, 1, luaCommand );
	// plg_GetStringParameter( params, 2, luaParam );
  
  // PA_Unichar arrName[60];
  // long i = 1;
  // long identity_len = plg_CharToUnichar( "_atArr", arrName, sizeof( arrName ) );
  // PA_Variable arr = PA_GetVariable  ( arrName );
  // PA_Unistring txt = PA_GetStringInArray( arr, i );
  
  
	PA_Unistring* Arg1 = PA_GetStringParameter( params, 1 );
	PA_Unistring* Arg2 = PA_GetStringParameter( params, 2 );
	// long length1 = PA_GetUnistringLength( Arg1 );
	// long length2 = PA_GetUnistringLength( Arg1 );
  char *utf8CharCommand = plg_CreateUtf8CharFromUnistring( Arg1 );
  char *utf8CharParams = plg_CreateUtf8CharFromUnistring( Arg2 );
  
  if( !utf8CharCommand  ) {
    returnTxt = "err: Command conversion to UTF8 failed (MA_LxText)";
  } else if ( !utf8CharParams ) {
      returnTxt = "err: Parameter conversion to UTF8 failed (MA_LxText)";
  } else {
    luaCommand = utf8CharCommand;
    luaParam = utf8CharParams;
    luaCommandType = luaCommand.substr( 0, 2 );
    if(luaCommandType == "x:"){
      luaCommand = luaCommand.substr(2);
      returnTxt = luaScriptCallFunction(luaCommand.c_str(), luaParam.c_str());
    } else if (luaCommandType == "s:"){
      returnTxt = luaScriptRunString( luaCommand.substr( 2 ), luaParam, 0 );
    } else if (luaCommandType == "f:"){
      returnTxt = luaScriptRunString( luaCommand.substr( 2 ), luaParam, 1 );
    } else if (luaCommandType == "l:"){
      lua_State *L = loadLua();
      if( L ){
        returnTxt = "loaded";
      } else {
        returnTxt = "already loaded";
      }
    } else if (luaCommandType == "u:"){
      lua_State *L = lua_state_ref_get();
      if( L ){
        unLoadLua(L);
        returnTxt = "unloaded";
      } else {
        returnTxt = "does not exist";
      }
    } else {
      returnTxt = "err: Command did not start with 'x:'[function call] or 'f:'[script filepath] or 's:'[script text] (MA_LxText)";
    }
  }
  
  // return result
  
  PA_Unistring uniStr;
	PA_Unichar* uniChars;
  uniStr = plg_CreateUnistringFromUtf8Char( returnTxt.c_str() );
	uniChars = PA_GetUnistring( &uniStr );
  PA_ReturnString( params, uniChars );
  PA_DisposeUnistring(&uniStr); // PM 2018-06-20
  
  // usually we would dispose unistr: PA_ClearVariable( &unistr );
  /*
   * When you call PA_SetStringInArray, PA_SetPictureInArray, the given
   object will now belongs to the array. Any previous object in the array
   will be disposed.
   
   Once you give the variable to 4D by calling PA_SetVariable, 4D will be
   the owner of the variable and you should not dispose it. This is true
   if you get the variable by calling PA_GetVariable or if you create it
   by calling PA_CreateVariable.
   */
  
  /*
  long size = returnTxt.length();
  size = ( size * 2 ) + 2; // PA_Unichar may be 2x (+1) bigger than string
  PA_Unichar *returnUnichar = new PA_Unichar[ size ];
	plg_StringToUnichar( returnTxt, returnUnichar, size );
	PA_ReturnString( params, returnUnichar );
  delete[] returnUnichar;
  */ 
}

string lua_state_key(){
  long prsNum = PA_GetCurrentProcessNumber();
  string key = "prs-" + NarrowString(prsNum);
  return key;
}

// look at the end of file for boost::unordered_map example
lua_State* lua_state_ref_get(){
  string key = lua_state_key();
  // check if key is present
  luaStateIdx = luaStateTbl.find(key);
  if (luaStateIdx != luaStateTbl.end()){
    return luaStateIdx->second; // ->second is lua_State* value
  }
  return NULL;
}

long lua_state_ref_add( lua_State *state )
{
  string key = lua_state_key();
  luaStateTbl[key] = state;
  return 1;
}

long lua_state_ref_delete(lua_State *L)
{
  string key = lua_state_key();
  luaStateIdx = luaStateTbl.find( key );
  if ( luaStateIdx == luaStateTbl.end() ) { // not found
    plg_ErrMessage( L"err: state key was not found: " + plg_StringToWstring(key), L"lua_state_ref_delete()" );
    return -10; // error here
  } else if(luaStateIdx->second != L){ // double check that key maches value to delete
    plg_ErrMessage( L"err: state key does not match state pointer: " + plg_StringToWstring(key), L"lua_state_ref_delete()" );
    return -20; // error here
  } else {
    luaStateTbl.erase( luaStateIdx );
  }
  return 0;
}


/*
// http://stackoverflow.com/questions/12256455/print-stacktrace-from-c-code-with-embedded-lua
int traceback(lua_State *L) {
    lua_getglobal(L, "debug");
    lua_getfield(L, -1, "traceback");
    //---------------------------
    lua_pop(L, -2); // to popup the 'debug'
    //---------------------------
    lua_pushvalue(L, 1);
    lua_pushinteger(L, 2);
    lua_call(L, 2, 1);
    traceBackString = "\n";
    traceBackString += lua_tostring(L, -1);
    // fprintf(stderr, "%s\n", traceBackString.c_str());
    return 1;
}
*/

lua_State* loadLua()
{
  lua_State *L = lua_state_ref_get();
  if( L == NULL ){
    L = lua_open();
    luaL_openlibs( L );
    // lua_pushcfunction(L, traceback);
    lua_state_ref_add( L );
    return L;
  }
  return NULL;
}

void unLoadLua(lua_State *L)
{
  if( L ){
    lua_close( L );
    lua_state_ref_delete( L );
  } else {
    plg_ErrMessage( L"err: state was null", L"unLoadLua()" );
  }
}

int popStack( lua_State *L ){
  // http://stackoverflow.com/questions/6511432/managing-stack-with-lua-and-c
  int stackSizeOrig = lua_gettop(L);
  int stackSize = stackSizeOrig;
  while (stackSize > 0) {
    lua_pop(L, stackSize);
    stackSize = lua_gettop(L); // recheck
  }
  if (stackSizeOrig > 0) {
    plg_ErrMessage( L"err: stack size > 0", L"lx_popStack()" );
  }
  return stackSizeOrig;
}

string returnError( lua_State *L, string msg, int getString, string luaCommand, string luaParam ){
  string returnTxt = msg;
  returnTxt += "\n\ncommand: '" + luaCommand + "'\nparameter: '" + luaParam + "'\n\n";
  int stackSize = lua_gettop(L);
  
  /*
  string err = "Lua debugger not found.";
  lua_getglobal(L,"debug");
  if (lua_istable(L,-1)!=0)
  {
    lua_getfield(L,-1,"traceback");
    if (lua_isfunction(L,-1)!=0)
    {
      if (lua_pcall(L,0,1,0)==0)
      {
        if (lua_isstring(L,-1)!=0)
        {
          err = lua_tostring(L,-1);
        }
      }
    }
  }
  returnTxt += err;
  */
  
  if (getString == 1) {
    if ( !lua_isstring( L, -1 ) ){
      returnTxt += "\n*** error string does not exist ***\n";
    } else {
      returnTxt += lua_tostring( L, -1 );
    }
  }
  if (stackSize < 1) {
    returnTxt += "\n*** result parameter does not exist ***\n";
  } else {
    lua_pop(L, 1); // remove the result from the stack
  }
  if (stackSize == 2) {
    lua_pop(L, 1); // remove debug.traceback from the stack
  }
  popStack(L);
  return returnTxt;
}

string returnOk( lua_State *L ){
  string returnTxt = "";
  if ( !lua_isstring( L, -1 ) ){
    returnTxt += "\n*** result string does not exist ***\n";
  } else {
    returnTxt += lua_tostring(L, -1); // get the result
  }
  int stackSize = lua_gettop(L);
  if (stackSize < 1) {
    returnTxt += "\n*** result parameter does not exist ***\n";
  } else {
    lua_pop(L, 1); // remove the result from the stack
  }
  if (stackSize == 2) {
    lua_pop(L, 1); // remove debug.traceback from the stack
  }
  popStack(L);
  return returnTxt;
}

string luaScriptCallFunction( const char* luaCommand, const char* luaParam ){
  lua_State *L = lua_state_ref_get();
  string returnTxt;
  if (L == NULL) {
    returnTxt = "err: lx state was not loaded when calling function";
    returnTxt += "\n\ncommand: '" + (string)luaCommand + "'\nparameter: '" + (string)luaParam + "'\n\n";
    return returnTxt;
  }
  
  // http://tinylittlelife.org/?p=254
  // on error, execute lua function debug.traceback
  // debug is a table, put it on the stack
  lua_getglobal(L, "debug");
  // -1 is the top of the stack
  lua_getfield(L, -1, "traceback");
  // traceback is on top, remove debug from 2nd spot
  lua_replace(L, -2);
  
  lua_getglobal(L, luaCommand); // function to be called
  lua_pushstring(L, luaParam); // 1st argument
  if ( lua_pcall( L, 1, 1, -3 ) ) // call function luaCommand with 1 arguments and 1 result, error index -3 because of: [debug, traceback, luaParam]
  {
    return returnError(L, "err: lx_pcall failed", 1, luaCommand, luaParam);
  }
  if ( !lua_isstring( L, -1 ) )
  {
    return returnError(L, "err: Lx function return value should be a string", 0, luaCommand, luaParam);
  }
  return returnOk(L);
}

string luaScriptRunChar( const char* luaCommand, const char* luaParam, int isFile ){
  lua_State *L = lua_state_ref_get();
  string returnTxt;
  
  bool unload = ( L == NULL );
  if (unload){
    L = loadLua();
  }
  lua_getglobal(L, "debug");
  // -1 is the top of the stack
  lua_getfield(L, -1, "traceback");
  // traceback is on top, remove debug from 2nd spot
  lua_replace(L, -2);
  
  // now comes the file to be called
  if( isFile == 1 ){
    if ( luaL_loadfile( L, luaCommand ) ) // luaCommand is .lua file path
    {
      returnTxt = returnError(L, "err: lxL_loadfile failed", 1, luaCommand, luaParam);
      if (unload){
        unLoadLua(L);
      }
      return returnTxt;
    }
  } else {
    if ( luaL_loadstring( L, luaCommand ) )
    {
      returnTxt = returnError(L, "err: lxL_loadstring failed", 1, luaCommand, luaParam);
      if (unload){
        unLoadLua(L);
      }
      return returnTxt;
    }
  }
  lua_pushstring(L, luaParam); // 1st argument
  if ( lua_pcall( L, 1, 1, -3 ) ) // call function luaCommand with 1 arguments and 0 results, error index 0
  {
    returnTxt = returnError(L, "err: lx_pcall failed", 1, luaCommand, luaParam);
    if (unload){
      unLoadLua(L);
    }
    return returnTxt;
  }
  
  if ( !lua_isstring( L, -1 ) )
  {
    returnTxt = returnError(L, "err: Lx return value is not a string", 0, luaCommand, luaParam);
    if (unload){
      unLoadLua(L);
    }
    return returnTxt;
  }
  
  returnTxt = returnOk(L);
  if (unload){
    unLoadLua(L);
  }
  return returnTxt;
}



string luaScriptRunString( string luaCommand, string luaParam, int isFile ){
  return "err: luaScriptRunString is not supported";
  /*
  lua_State *L = lua_state_ref_get();
  string returnTxt;
  
  bool unload = ( L == NULL );
  if (unload){
    L = loadLua();
  }
  
  if( isFile == 1 ){
    if ( luaL_loadfile( L, luaCommand.c_str() ) ) // luaCommand is .lua file path
    {
      returnTxt = "err: lxL_loadfile failed";
      returnTxt += lua_tostring(L, -1);
      returnTxt += traceBackString;
      if (unload){
        unLoadLua(L);
      }
      return returnTxt;
    }
  } else {
    if ( luaL_loadstring( L, luaCommand.c_str() ) )
    {
      returnTxt = "err: lxL_loadstring failed";
      returnTxt += lua_tostring(L, -1);
      returnTxt += traceBackString;
      if (unload){
        unLoadLua(L);
      }
      return returnTxt;
    }
  }
  
  lua_pushstring( L, luaParam.c_str() ); // push 1st argument
  // lua_setglobal( L, "inTxt" );
  if ( lua_pcall( L, 1, 1, 0 ) )
  {
    returnTxt = "err: lx_pcall failed";
    returnTxt += lua_tostring( L, -1 );
    lua_pop(L, 1);
    returnTxt += traceBackString;
    if (unload){
      unLoadLua(L);
    }
    return returnTxt;
  }
  
  // lua_getglobal( L, "returnTxt" );
  if ( !lua_isstring( L, -1 ) )
  {
    lua_pop(L, 1);
    returnTxt = "err: Lx global variable returnTxt should be a string";
    if (unload){
      unLoadLua(L);
    }
    return returnTxt;
  }
  
  returnTxt = lua_tostring( L, -1 );
  lua_pop(L, 1);  if (unload){
    unLoadLua(L);
  }
  return returnTxt;
  */
}


/*
 static boost::unordered_map<string, lua_State *> luaStateList;
 static boost::unordered_map<string, lua_State *>::iterator luaStateListIterator;
 
 lua_State* lua_state_ref_valid( string luaStateId )
 {
 lua_State *state;
 
 luaStateListIterator = luaStateList.find( luaStateId );
 if ( luaStateListIterator == luaStateList.end() ) { // not found
 state = NULL;
 } else {
 state = luaStateListIterator;
 }
 return state;
 }
 
 long lua_state_ref_add( lua_State *state )
 {
 long luaStateId = (long)state;
 if( luaStateId == 0 ){
 luaStateId = 0;
 } else {
 long prsNum = PA_GetCurrentProcessNumber();
 luaStateList.insert( std::pair<long, long>(luaStateId, prsNum) );
 }
 return luaStateId;
 }
 
 long lua_state_ref_delete( lua_State *state )
 {
 string luaStateId = ""; //(long)state;
 luaStateListIterator = luaStateList.find( luaStateId );
 if ( luaStateListIterator == luaStateList.end() ) { // not found
 luaStateId = -10; // error here
 } else {
 luaStateListIterator = luaStateList.erase( luaStateListIterator );
 }
 // Returns:	The iterator following the erased elements - i.e. last.
 return 0;
 }
 */
