/// TKS_ScriptModule.cpp
///
/// (c) 2005-2017 Bastian Spiegel <bs@tkscript.de>
///     - distributed under the terms of the GNU general public license (GPL).
///
#include <stdlib.h>
#include <string.h>

#include "tks.h"
#include "tks_inc_class.h"
#include "tks_inc_compiler.h"
#include "YAC_Value.h"
#include "YAC_ValueObject.h"
#include "YAC_ListNode.h"
#include "YAC_PointerArray.h" 
#include "YAC_ObjectArray.h" 

#include "TKS_ScriptObjectID.h"
#include "TKS_ScriptVariable.h"
#include "TKS_FunctionObject.h"
#include "TKS_ScriptModule.h"

#include "TKS_Mutex.h"



TKS_ScriptModule::TKS_ScriptModule(void) {
   cscript=0;
}

TKS_ScriptModule::~TKS_ScriptModule() {
   unload();
}

sSI TKS_ScriptModule::load(YAC_String *_s) {

#ifdef TKS_MT
   // The compiler is currently not re-entrant / thread safe. => Lock global mutex
   tkscript->mtx_global->lock();
#endif //TKS_MT

   unload();

   TKS_Compiler *c=(TKS_Compiler*)tkscript->compiler;
   script_id.s_id.id = c->newTempModule();

   if( ((sSI)script_id.s_id.id) >= 0)
   {
      script_id.s_id.magic=c->temp_modules[script_id.s_id.id].magic;
      TKS_CachedScript *cs=c->temp_modules[script_id.s_id.id].cscript;
      cs->is_tmod=1;
      TKS_TokenizedScript::AllocTSLCache();

      if(cs->scanSource(_s, 0/*copy source*/))
      {
         TKS_TokenizedScript::FreeTSLCache();

         if(cs->compileSource())
         {
            if(cs->parser_tree->resolveXRefAll())
            {
               cs->parser_tree->optimizeAll();

               cscript=(void*)cs;

#ifdef TKS_MT
               tkscript->mtx_global->unlock();
#endif //TKS_MT

               return 1;
            }
            else
            {
               Dprintf("[---]\n[---] parse error in temporary module, line %i.\n[---]\n",
                  (tkscript->ptn_error_loc&0xFFFF)+1
                  );
            }
         }
         else
         {
            Dprintf("[---] failed to compile temporary module.\n");
         }
      }
      else
      {
         Dprintf("\n[---] failed to preprocess temporary module.\n");
         TKS_TokenizedScript::FreeTSLCache();
      }
      c->deleteTempModule(script_id.s_id.id);
      tkscript->main_context->b_running = 1;  // [17Sep2017] continue after failed attempt
      Dtkscompiler->parse_error = 0;
   }
   else
   {
      Dprintf("\n[---] newTempModule() failed (id=%d)\n", script_id.s_id.id);
   }

#ifdef TKS_MT
   tkscript->mtx_global->unlock();
#endif //TKS_MT

   // Reset error state
   //  (note) [02Jan2018] TKS_ScriptEngine::yacRunning() now also checks only b_running flag, not ptn_error_code
   tkscript->ptn_error_code = 0;
   tkscript->ptn_error_loc  = 0;

   return 0;
}

void TKS_ScriptModule::unload(void) {
   if(cscript)
   {
      TKS_Compiler *c=(TKS_Compiler*)tkscript->compiler;
      c->deleteTempModule(script_id.s_id.id);
      cscript = NULL;
   }
}

void TKS_ScriptModule::findVariable(YAC_String *_s, YAC_Value *_r) {
   if(cscript)
   {
      TKS_CachedScript *cs=(TKS_CachedScript*)cscript;
      TKS_CachedObject *co = cs->module_vars.findEntry(_s);
      if(co)
      {
         TKS_ScriptVariable *v = (TKS_ScriptVariable*)YAC_NEW_CORE_POOLED(TKS_CLID_VARIABLE);
         v->var       = co;
         v->value.any = co->value.any;
         v->type      = co->type;
         v->deleteme  = 0;
         v->script_id.s_id.id    = script_id.s_id.id;
         v->script_id.s_id.magic = script_id.s_id.magic;
         YAC_RETO(v, 1);
         return;
      }
   }
   YAC_RETO(0,0);
}

void TKS_ScriptModule::findTaggedVariableByIndex(sSI _index, YAC_Value *_r) {
   TKS_CachedScript *cs=(TKS_CachedScript *)cscript;
   if(((sUI)_index) < cs->tagged_vars.num_elements)
   {
      TKS_ScriptVariable *v = (TKS_ScriptVariable*)YAC_NEW_CORE_POOLED(TKS_CLID_VARIABLE);
      TKS_CachedObject *co = cs->tagged_vars.elements[_index];
      v->var       = co;
      v->value.any = co->value.any;
      v->type      = co->type;
      v->deleteme  = 0;
      v->script_id.s_id.id    = script_id.s_id.id;
      v->script_id.s_id.magic = script_id.s_id.magic;
      YAC_RETO(v, 1);
      return;
   }
   YAC_RETO(0,0);
}

void TKS_ScriptModule::findFunction(YAC_String *_s, YAC_Value *_r) {
   if(cscript)
   {
      TKS_CachedScript *cs=(TKS_CachedScript *)cscript;
      PTN_Function *f=cs->findFunction(_s);
      if(f)
      {
         TKS_FunctionObject *fi = (TKS_FunctionObject*)YAC_NEW_CORE_POOLED(TKS_CLID_FUNCTION);
         fi->fun = f;
         fi->script_id.s_id.id    = script_id.s_id.id;
         fi->script_id.s_id.magic = script_id.s_id.magic;
         YAC_RETO(fi, 1);
         return;
      }
   }
   YAC_RETO(NULL,0);
}

void TKS_ScriptModule::eval(void) {
   if(cscript)
   {
      TKS_CachedScript *cs=(TKS_CachedScript*)cscript;

      // xxx TKS_MT: use *real* context (context creation is slow)
      TKS_Context *context = (TKS_Context *) tkscript->yacContextCreate();

      context->b_running=1;

      PTN_Env env;
      env.context = context;
      env.continue_flag=1;
      cs->parser_tree->evalFirst(&env);
      env.cret.unset();
      
      tkscript->yacContextDestroy((void*)context);
      /*context->b_running=1;
      context->ptn_error_code=0;
      context->ptn_error_loc=0;*/
   }
}
