/******************************************************************
  
  Module:  script_pawn.cpp
  
  Author: Sean Craig
  
  Copyright 2005 Sony Online Entertainment.  All rights reserved.
  
*******************************************************************/

//-------------------------------------------------------- Includes
#include "script_pawn.h"
#include "script_func.h"

#ifndef SN_TARGET_PS3
#include <io.h>
#include <windows.h>
#endif

#include "pawn/amx/amxdbg.h"
#include "registry.h"
#include "stats.h"
//---------------------------------------------- Class Declarations

#if (defined(_DEBUG) && !defined(SN_TARGET_PS3))
#define DEBUG_SCRIPTS 1
#else
#define DEBUG_SCRIPTS 0
#endif

typedef enum
{
  SNT_NONE,
  SNT_WAIT,
  SNT_ANIMATION,
  SNT_PATH
} eScriptNotificationType;



class cScriptInstance
{
public:
  cScriptInstance(cScriptSys *sys);
  ~cScriptInstance();


  bool Run(tGameObjectID actor, tGameObjectID target,ePawnEventType  type,int *result = NULL);
  bool Notify(eScriptNotificationType type,tGameObjectID ThisObj);
  void Update(float time);
  bool Load(const char *filename);


  void SetSleepTime(float time){mSleepTime = time;};
  void SetSleepActor(tGameObjectID sleep_actor){mSleepActor = sleep_actor;};

  bool  IsCompleted(){return mCompleted;};
  tGameObjectID GetThis(){return mThis;};
  tGameObjectID GetOther(){return mOther;};
  cScriptSys *GetScriptSys(){return mpScriptSys;};
  bool IsActive(){return mpScriptSys->GetActive()==this;};
  void Clone(cScriptInstance *other); // makes this into a clone of other

  void DebugLoadInfo(const char *filename);
  void DebugEnable();
  void DebugDisable();
  const char *DebugGetFilename(AMX *amx);
  int         DebugGetLinenum(AMX *amx);
  void        DebugOutputVariables(AMX *amx,SyString &string);
protected:

  bool _GetFunctionIndex(int *ans,ePawnEventType type,cGameObject *obj);
  bool _Execute(int index,int *result = NULL);
  void _CopyDataFromParent();  // copies global data that may have changed
  void _CopyDataToParent(); 
  void _ReportFunctionCall(const char *str);// 

  // mAMX has to stay here; oh, and no virtual funcs allowed in this class.
  AMX                 mAMX;     // virtual machine

  cScriptSys          *mpScriptSys;
  tGameObjectID       mThis;
  tGameObjectID       mOther;

  void *              mProgram; // program it's running
  size_t              mMemSize;
  void *              mData;    // only for clones...
  int                 mDataSize;
  cScriptInstance    *mParent;  // of a cloned script
  // for sleeping scripts...
  eScriptNotificationType mSleepNotify;
  float               mSleepTime; // for sleep time...
  tGameObjectID       mSleepActor;
  bool                mCompleted;
  AMX_DBG             *mpAMX_DBG;  // symbolic information for debugging
};

//-------------------------------------------------------- local function declarations
int    srun_LoadProgram(AMX *amx,const char *filename, void *memblock);
bool   srun_LoadDebugInfo(AMX_DBG *amx,const char *filename);

size_t srun_ProgramSize(const char *filename);
cScriptInstance *Script_GetInstance(AMX *amx);

//-------------------------------------------------------------------- Local Variables

#if DEBUG_SCRIPTS
static const char *SCRIPT_PATHNAME = "data\\level\\";
static const char *SCRIPT_SRC_PATHNAME = "\\source\\scripts\\";
#endif

static const char *l_entry_names[] = 
{
  //"main", // PET_DIALOG,	// when the player tries to talk to someone
  "@onActivate", // PET_DIALOG,	// when the player tries to talk to someone
  "@onEnter", //PET_ENTER,	// for trigger areas, when entered
  "@onExit", //PET_EXIT,	    // for trigger areas, when exited
  "@onStartup", //PET_STARTUP,	// for trigger areas, when someone is inside
  "@onCollide", //PET_COLLIDE, 	// when destroyed or killed
  "@onHit", // PET_HIT // when object successfully hits a target
  "@onDeath", // PET_DEATH  // when object kills another object
  "@onInit", // PET_INIT  // when object is created
};

#define NUM_ENTRIES (sizeof(l_entry_names)/sizeof(l_entry_names[0]))
//---------------------------------------------------- Member Function Definitions
//----------------------------------------------------------- cScriptInstance
cScriptInstance::cScriptInstance(cScriptSys *scriptSys) :
mpScriptSys(scriptSys),
mThis(ID_NONE),
mOther(ID_NONE),
mProgram(NULL),
mMemSize(0),
mData(NULL),
mDataSize(0),
mParent(NULL),
mSleepNotify(SNT_NONE),
mSleepTime(0.0f),
mSleepActor(ID_NONE),
mCompleted(false),
mpAMX_DBG(NULL)
{
  mAMX.base = NULL;
};

cScriptInstance::~cScriptInstance()
{

  if (mParent == NULL) // this is a non-cloned script
  {
    if (mAMX.base != NULL)
    {
      amx_Cleanup(&mAMX);
    }
  }
  delete [] ((char*)mData); 
  delete [] ((char*)mProgram);


  mProgram = NULL;
  mData = NULL;
  if (mpAMX_DBG != NULL)
  {
    dbg_FreeInfo(mpAMX_DBG);
    delete mpAMX_DBG;
  }
};

bool cScriptInstance::Run(tGameObjectID ThisObj, tGameObjectID OtherObj,ePawnEventType  type,int *result)
{
  int index;

  cGameObject *actorPtr = mpScriptSys->GetTitan()->GetRegistry()->Fetch(ThisObj);

  if (_GetFunctionIndex(&index,type,actorPtr))
  {
    mThis = ThisObj;
    mOther = OtherObj;
    return _Execute(index,result);
  }
  return false;
};

bool 
cScriptInstance::Notify(eScriptNotificationType type,tGameObjectID id)
{
  SyAssertf(type != SNT_NONE,"Bad Pawn Script Notification Type");
  if (id == mSleepActor && type == mSleepNotify)
  {
    mSleepNotify = SNT_NONE;
    _Execute(AMX_EXEC_CONT);
    return true;
  }
  return false;

}

void 
cScriptInstance::Update(float time)
{
  if (mSleepNotify == SNT_WAIT)
  {
    mSleepTime -= time;
    if (mSleepTime < 0.0f)
    {
      mSleepNotify = SNT_NONE;
      _Execute(AMX_EXEC_CONT);
    }
  }
};

bool 
cScriptInstance::Load(const char *original_filename)
{
  char filename[512];
  static const char *path = "data\\amx";
  sprintf(filename,"%s\\%s",path,original_filename);
  mMemSize = srun_ProgramSize(filename);
  if (mMemSize == 0)
  {
    return false;
  }
  int err;

  mProgram = new char[mMemSize];
  if (mProgram == NULL)
  {
    SyAssertf(0,"Bad Alloc");
    return false;
  }
  err = srun_LoadProgram(&mAMX, filename, mProgram);
  if (err)
  {
    SyAssertf(0,"Unable to load program");
    return false;
  }

  // TODO
  script_RegisterNatives(&mAMX);

  return true;
};

void 
cScriptInstance::Clone(cScriptInstance *other)
{
// duplicate preloaded script...
  long program_size,datasize,heapsize;
  amx_MemInfo(&other->mAMX,&program_size,&datasize,&heapsize);
  mMemSize = 0;
  mProgram = NULL; // uses parent
  // use same data as parent
  mDataSize = datasize; 
  mData = new char[datasize+heapsize];

  if (other->mParent == NULL)
  {
    mParent = other;
  }
  else
  {
    mParent = other->mParent;
  }

  SyAssertf(mParent->mAMX.data == NULL, "Parent is a clone!  *gasp*!  I knew it!");

  mThis = other->mThis;
  mOther = other->mOther;

  memset(&mAMX,0,sizeof(AMX));
  amx_Clone(&mAMX,&other->mAMX,mData);

  mSleepTime = other->mSleepTime;
  mSleepActor = other->mSleepActor;

  // register self with script system
  mpScriptSys->AddClone(this);

};

void 
cScriptInstance::_ReportFunctionCall(const char *str)
{
#if 1
  mpScriptSys->DebugPrint(str);
#endif
}
bool 
cScriptInstance::_GetFunctionIndex(int *ans,ePawnEventType type,cGameObject *actor)
{
  static const int BUFFER_LEN = 512;
  char buffer[BUFFER_LEN]; 
  char debug_output[BUFFER_LEN]; 
  const char *entry_name = l_entry_names[(int)type];

  // first, try name specific function
  if (actor != NULL)
  {
    const char *object_name = actor->GetName();
    if (object_name != NULL)
    {
      sprintf(buffer,"%s_%s",entry_name,object_name);
      sprintf(debug_output,"%s...",buffer);
      _ReportFunctionCall(debug_output);
      int err = amx_FindPublic(&mAMX,buffer,ans);

      if (!err)
      {
        _ReportFunctionCall("Found!\n");
        return true;
      }
      _ReportFunctionCall("Not found.\n");

    }

    // next try master name 
    const char *master_name = actor->GetStats()->GetMasterName();
    sprintf(buffer,"%s_MASTER_%s",entry_name,master_name);
    sprintf(debug_output,"%s...",buffer);
    _ReportFunctionCall(debug_output);
    int err = amx_FindPublic(&mAMX,buffer,ans);

    if (!err)
    {
      _ReportFunctionCall("Found!\n");
      return true;
    }
    _ReportFunctionCall("Not found.\n");

  }
  // ok, just use the general handler (if there is one)
  sprintf(debug_output,"%s...",entry_name);
  _ReportFunctionCall(debug_output);
  int err = amx_FindPublic(&mAMX,entry_name,ans);

  if (!err)
  {
    _ReportFunctionCall("Found!\n");
    return true;
  }
  _ReportFunctionCall("Not found.\n");
  return false;
};

bool 
cScriptInstance::_Execute(int entry, int *retval)
{
  _CopyDataFromParent(); 
  cell ret = 0;
  int err = amx_Exec(&mAMX, &ret,entry);
  switch (err)
  {
  case AMX_ERR_SLEEP:
    mSleepNotify = (eScriptNotificationType)ret;
    // clone script if we're not already running a clone
    if (IsActive())
    {
      cScriptInstance *clone = new cScriptInstance(mpScriptSys);
      clone->Clone(this);
    }
    SyAssertf(retval == NULL, "Query event sleeping in pawn script!  Bad Scripter!");
    break;
  case AMX_ERR_NONE:
    if (!IsActive())
    {
      mCompleted = true;
    }
    if (retval != NULL)
    {
      *retval = (int) ret;
    }
    break;
  default:
    SyAssertf(0,"Unknown AMX error: %d",err);
  }
  _CopyDataToParent(); 
  mpScriptSys->DebugRestore();
  return true;
};


void
cScriptInstance::_CopyDataFromParent()
{
  if (mParent == this)
  {
    return;
  }
  void *parent_data = mParent->mAMX.base + ((AMX_HEADER*)mParent->mAMX.base)->dat;
  memcpy(mAMX.data,parent_data,mDataSize);
}

void
cScriptInstance::_CopyDataToParent()
{
  if (mParent == this)
  {
    return;
  }
  void *parent_data = mParent->mAMX.base + ((AMX_HEADER*)mParent->mAMX.base)->dat;
  memcpy(parent_data,mAMX.data,mDataSize);
}

int
AMXAPI DebugCallbackFunction(AMX *amx)
{
  Titan *titan = Script_GetTitan(amx);

  titan->GetScriptSys()->DebugCallback(amx);
  return 0;
}

void 
cScriptInstance::DebugEnable()
{
  amx_SetDebugHook(&mAMX,DebugCallbackFunction);
}

void 
cScriptInstance::DebugDisable()
{
  amx_SetDebugHook(&mAMX,NULL);
}

void 
cScriptInstance::DebugLoadInfo(const char *original_filename)
{
#if DEBUG_SCRIPTS

  char filename[512];
  static const char *path = "data\\amx";
  sprintf(filename,"%s\\%s",path,original_filename);

  mpAMX_DBG = new AMX_DBG;
  if (!srun_LoadDebugInfo(mpAMX_DBG,filename))
  {
    delete mpAMX_DBG;
  }
#endif 
}


const char *
cScriptInstance::DebugGetFilename(AMX *amx)
{
  if (mpAMX_DBG == NULL)
  {
    SyAssertf(0,"No debugging info");
    return NULL;
  }
  ucell address = (ucell) amx->cip;
  const char *ans;
  dbg_LookupFile(mpAMX_DBG,address,&ans);
  return ans;
}

int         
cScriptInstance::DebugGetLinenum(AMX *amx)
{
  if (mpAMX_DBG == NULL)
  {
    //SyAssertf(0,"No debugging info");
    return 0;
  }
  ucell address = (ucell) amx->cip;
  long ans;
  dbg_LookupLine(mpAMX_DBG,address,&ans);
  return ans;
}

void
cScriptInstance::DebugOutputVariables(AMX *amx,SyString &output)
{
  output.Concat("This: ");
  tGameObjectID thisID = Script_GetThisID(amx);
  tGameObjectID otherID = Script_GetOtherID(amx);

  Titan *titan = Script_GetTitan(amx);
  cGameObjectRegistry *registry = titan->GetRegistry();
  if (thisID == ID_NONE)
  {
    output.Concat("none\n");
  }
  else
  {
    cGameObject *obj = registry->Fetch(thisID);
    if (obj!=NULL)
    {
      output.Concat(obj->GetName());
    }
    output.Concat("\n");
  }
  output.Concat("Other: ");
  if (otherID == ID_NONE)
  {
    output.Concat("none\n");
  }
  else
  {
    cGameObject *obj = registry->Fetch(otherID);
    if (obj!=NULL)
    {
      output.Concat(obj->GetName());
    }
    output.Concat("\n");
  }



#if 0
  // this code is supposed to check the symbol data, find the symbols in scope, and add them to the window.
  // However, the symbol table doesn't seem to make sense.  The ident member, for example, is always 0,
  // which isn't a valid value for the enum.  Interestingly enough, it looks like the name for the variable
  // is at least initialized, to the string "(null)"...
  // Skipping for now.
  ucell codestart,codeend;
  int index;

  const AMX_DBG_SYMBOL *sym = NULL;
  ucell scopeaddr = amx->cip;
  codestart = codeend = 0;
  index = 0;
  char str_buffer[512];
  for ( ;; ) {
    /* find (next) matching variable */
    while (index < mpAMX_DBG->hdr->symbols
      && ((mpAMX_DBG->symboltbl[index]->ident == iFUNCTN )
      || (mpAMX_DBG->symboltbl[index]->codestart > scopeaddr || mpAMX_DBG->symboltbl[index]->codeend < scopeaddr)))
    {
      index++;
    }
    if (index >= mpAMX_DBG->hdr->symbols)
      break;
    sym = mpAMX_DBG->symboltbl[index];
    int value;
    AMX_HEADER *hdr=(AMX_HEADER *)amx->base;
    unsigned char *data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat;
    value =(int)( * (cell *)(data+(int)sym->address));
    sprintf (str_buffer,"%s : %d\n",sym->name,value);
    output.Concat(str_buffer);
    index++;
  } /* for */

#else  
  // just accessing public variables
  int num_pub_vars;
  amx_NumPubVars(amx,&num_pub_vars);


  char str_buffer[256];
  cell amx_addr;
  str_buffer[0] = '\0';

  AMX_HEADER *hdr=(AMX_HEADER *)amx->base;
  unsigned char *data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat;

  for (int ii=0;ii<num_pub_vars;++ii)
  {
    amx_GetPubVar(amx, ii,str_buffer,&amx_addr);
    int value =(int)( * (cell *)(data+(int)amx_addr));
    sprintf (str_buffer,"%s : %d\n",str_buffer,value);
    output.Concat(str_buffer);
  }
#endif

}


//----------------------------------------------------------- cScriptSys

cScriptSys::cScriptSys(Titan *titan) :
mSource(NULL),
mActive(NULL),
mpTitan(titan),
mFilename(NULL),
mbDebugging(false),
mSourceFile(NULL),
mSourceFilename(NULL),
mLastLinenum(-1),
mbRun(false),
mbStartup(false)
{
  SyAssertf(NUM_ENTRIES == NUM_EVENT_TYPES,"Missing event type function name");
  scriptFuncs_Init(titan);
};

cScriptSys::~cScriptSys()
{
  Clear();
  delete [] mFilename;
  delete [] mSourceFile;
  delete [] mSourceFilename;

};

void
cScriptSys::Clear()
{
  delete mSource;
  delete mActive;
  mSource = NULL;
  mActive = NULL;
  for (int ii=0;ii<mClones.Size();++ii)
  {
    delete mClones(ii);
  }
  mClones.Clear();
 

};

bool
cScriptSys::LoadScript(const char *filename)
{
  Clear();

  if (mFilename == NULL || strcmp(mFilename,filename)!=0)
  {
    delete [] mFilename;
    mFilename = new char [strlen(filename)+1];
    strcpy(mFilename,filename);
  }

  mSource = new cScriptInstance(this);
  if (!mSource->Load(filename))
  {
    delete mActive;
    mActive = NULL;
    return false;
  }

  mSource->DebugLoadInfo(filename);

  mActive = new cScriptInstance(this);
  mActive->Clone(mSource);


  if (mbDebugging)
  {
    mActive->DebugEnable();
  }

  return true;
};

void      
cScriptSys::ScriptEvent(ePawnEventType type, tGameObjectID actor, tGameObjectID target)
{
  if (mActive != NULL)
  {
    mActive->Run(actor,target,type);
  }
}

bool      
cScriptSys::ScriptQuery(ePawnEventType type, tGameObjectID actor, tGameObjectID target,int *result)
{
  if (mActive != NULL)
  {
     mActive->Run(actor,target,type,result);
  }
  return false;
}


void
cScriptSys::Update(float time)
{
  if (mbStartup)
  {
    mbStartup = false;
    ScriptEvent(PET_STARTUP,0,0);
  }
  for (int ii=0;ii<mClones.Size();++ii)
  {
    mClones(ii)->Update(time);
  }

  // clean up deleted script

  int prev = mClones.End();
  for (int ii=mClones.Begin();ii!=mClones.End();)
  {
    if (mClones(ii)->IsCompleted())
    {
      delete mClones(ii);

      int jj = ii;
      for (;mClones.Next(jj) != mClones.End();jj=mClones.Next(jj))
      {
        mClones(jj) = mClones(mClones.Next(jj));
      }
      mClones.Erase();
      if (prev == mClones.End())
      {
        break;
      }
      ii = mClones.Next(prev);
    }
    else
    {
      prev = ii;
      ii=mClones.Next(ii);
    }
  }

};

void      
cScriptSys::NotifyDestinationReached(tGameObjectID actor) // notify script that an actor has reached his destination.
{
  for (int ii=0;ii<mClones.Size();++ii)
  {
    mClones(ii)->Notify(SNT_PATH,actor);
  }
};

void      
cScriptSys::NotifyAnimationCompleted(tGameObjectID actor)
{
  for (int ii=0;ii<mClones.Size();++ii)
  {
    mClones(ii)->Notify(SNT_ANIMATION,actor);
  }
};

void      
cScriptSys::AddClone(cScriptInstance *instance)
{
  if (instance == mActive)
  {
    return;
  }
  mClones.Add(mActive);
  mActive = instance;
};

bool      
cScriptSys::Recompile() // recompiles current script
{
  Compile(mFilename);
  LoadScript(mFilename);
  Startup();
  return false;
};




bool
cScriptSys::Compile(const char *filename)
{
  if (filename == NULL)
  {
    return false;
  }
  // recompile script

#if DEBUG_SCRIPTS 

  static const char *ERRFILENAME = "pawn_error.txt";
  char dest_name[256];
  char src_name[256];

  static const char *dest_path = "data\\amx";
  sprintf(dest_name,"%s\\%s",dest_path,filename);

  static const char *src_path = "source\\script";
  sprintf(src_name,"%s\\%s",src_path,filename);
  char *extension = &src_name[strlen(src_name)];

  while (*extension != '.')
  {
    if (extension == src_path)
    {
      SyAssert(0);
      return false;
    }
    extension--;
  }
  strcpy(extension,".p");

  // check ages
  HANDLE src_file = CreateFile(src_name,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);

  if (src_file == INVALID_HANDLE_VALUE)
  {
    return false; // there is no script.
  };

  HANDLE dest_file = CreateFile(dest_name,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);

  if (dest_file != INVALID_HANDLE_VALUE  && srun_ProgramSize(dest_name) > 0)
  {
    // check age
    HANDLE inc_file = CreateFile("source\\script\\titan.inc",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);

    if (inc_file == INVALID_HANDLE_VALUE)
    {
      SyAssertf(0,"Include file not found?");
      return true; // no inc file?
    };

    // check age
    HANDLE prop_file = CreateFile("source\\script\\props.p",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);

    if (prop_file == INVALID_HANDLE_VALUE)
    {
      SyAssertf(0,"prop.p file not found?");
      return true; // no prop.p file?
    };
    // check file dates
    FILETIME src_time;
    FILETIME dest_time;
    FILETIME inc_time;
    FILETIME prop_time;


    GetFileTime(src_file,NULL,NULL,&src_time);
    GetFileTime(dest_file,NULL,NULL,&dest_time);
    GetFileTime(inc_file,NULL,NULL,&inc_time);
    GetFileTime(prop_file,NULL,NULL,&prop_time);

    if (CompareFileTime(&src_time,&dest_time) < 0 &&
        CompareFileTime(&inc_time,&dest_time) < 0 &&
        CompareFileTime(&prop_time,&dest_time) < 0)

    {
      CloseHandle(src_file);
      CloseHandle(dest_file);
      CloseHandle(inc_file);
      CloseHandle(prop_file);

      return false; // no need to compile; dest is newer than source.
    }
    CloseHandle(dest_file);
    CloseHandle(inc_file);
    CloseHandle(prop_file);
  }
  CloseHandle(src_file);
  // erase previous compile errors, if any

  char command[512];
  if (_access(ERRFILENAME,0)>=0)
  {
    sprintf(command,"del %s",ERRFILENAME);
    system(command);
  }

  // erase destination file (has old data)
  if (_access(dest_name,0)>=0)
  {
    sprintf(command,"del %s",dest_name);
    system(command);
  }        
  // compile
  sprintf(command,".\\source\\script\\pawncc -p.\\source\\script\\titan.inc -i.\\source\\script -e%s -d2 %s",ERRFILENAME,src_name);
  int retval = system(command);
  if (retval != 0)
  {
    sprintf(command,"notepad %s",ERRFILENAME);
    system(command);

    if (retval > 1)  // if it's just warnings, keep on going
    {
      return false;
    }
  }

  const char *end = &dest_name[strlen(dest_name)-1];

  while (end >= dest_name && *end != '\\' && *end != '/')
  {
    --end;
  }
  ++end;
  sprintf(command,"move .\\%s %s",end,dest_name);
  system(command);
  if (mSourceFilename)
  {
    mSourceFilename[0] = '\0';
  }
  return true;
#else
  return true;
#endif

};

void      
cScriptSys::EnableDebugging()
{
#if DEBUG_SCRIPTS
  if (mbDebugging == true)
  {
    return;
  }

  mbDebugging = true;
  // install debug hook
  if (mActive)
    mActive->DebugEnable();

  for (int ii=0;ii<mClones.Size();++ii)
  {
    mClones(ii)->DebugEnable();
  }
#endif
}

void      
cScriptSys::DisableDebugging()
{
#if DEBUG_SCRIPTS
  if (mbDebugging == false)
  {
    return;
  }

  mbDebugging = false;
  // install debug hook
  if (mActive)
    mActive->DebugDisable();

  for (int ii=0;ii<mClones.Size();++ii)
  {
    mClones(ii)->DebugDisable();
  }
#endif
}

bool 
cScriptSys::LoadSourceFile(const char *filename)
{

#if DEBUG_SCRIPTS
  if (mSourceFilename && strcmp(filename,mSourceFilename)== 0)
  {
    return false; // already loaded
  }
#if 0
  static const char *src_path = "source\\script";
  sprintf(src_name,"%s\\%s",src_path,mFilename);
  char *extension = &src_name[strlen(src_name)];

  while (*extension != '.')
  {
    if (extension == src_path)
    {
      SyAssert(0);
      return;
    }
    extension--;
  }
  strcpy(extension,".p");
#endif

  delete mSourceFile;
  delete mSourceFilename;
  mSourceFilename = strdup(filename);

  // load source file
  FILE *fp;
  if ((fp = fopen(filename, "rt")) == NULL )
    return false;
  if (fseek(fp,0,SEEK_END)!=0)
    return false;
  int size = ftell(fp);
  rewind(fp);
  mSourceFile = new char[size + 1];
  fread(mSourceFile, 1, size, fp);
  // trim off garbage at end of file
  while (size > 0 && mSourceFile[size-1]!='\n')
  {
    size--;
  }
  mSourceFile[size]='\0';
  fclose(fp);
  return true;

#else
  return false;
#endif

}

void
cScriptSys::DebugCallback(AMX *amx)
{
#if DEBUG_SCRIPTS
   // load up file
  const char *string = mSource->DebugGetFilename(amx);
  // we don't want to debug the titan.inc file
  if (strcmp(string,".\\source\\script\\titan.inc")==0)
  {
    return;
  }
  bool newfile = LoadSourceFile(string);  
  int line_num = mSource->DebugGetLinenum(amx);

 
  // sometimes you get two callbacks in a row (usually for new functions)
  // this skips the second callback.
  if (!newfile && line_num == mLastLinenum)
  {
    return; 
  }
  mLastLinenum = line_num;
  mpTitan->SetUnfocused(true);
  SyString variables;
  variables.Concat("File: ");
  variables.Concat(string);
  variables.Concat("\n");
 
  mSource->DebugOutputVariables(amx,variables);
  switch (mpTitan->GetTitanUI()->PawnDebugUI(mSourceFile,line_num,variables.AsChar()))
  {
    // SGC todo: Move this enum (from PawnDebug.h) somewhere I can access it here.
  case 0: // run
    mbRun = true;
    amx_SetDebugHook(amx,NULL);
    DisableDebugging();
    break;
  case 1: // step
    break;
  case 2: // stop debugging
    DisableDebugging();
    break;
  }
  mpTitan->SetUnfocused(false);

#endif
}


void
cScriptSys::DebugRestore()
{
#if DEBUG_SCRIPTS
  if (mbRun)
  {
    mbRun = false;
    EnableDebugging();
  }
#endif
}

void 
cScriptSys::Startup()
{
  mbStartup = true;
}

void      
cScriptSys::DebugPrint(const char *output)
{
  mpTitan->GetTitanUI()->PawnDebugOutput(output);
}

//-------------------------------------------------------- private functions


size_t srun_ProgramSize(const char *filename)
{
  FILE *fp;
  AMX_HEADER hdr;

  if ((fp=fopen(filename,"rb")) == NULL)
    return 0;
  fread(&hdr, sizeof hdr, 1, fp);
  fclose(fp);

  amx_Align16(&hdr.magic);
  amx_Align32((uint32_t*)&hdr.stp);
  return(hdr.magic==AMX_MAGIC) ? (size_t)hdr.stp : 0;
}

int srun_LoadProgram(AMX *amx,const char *filename, void *memblock)
{
  FILE *fp;
  AMX_HEADER hdr;

  if ((fp = fopen(filename, "rb")) == NULL )
    return AMX_ERR_NOTFOUND;
  fread(&hdr, sizeof hdr, 1, fp);
  rewind(fp);
  fread(memblock, 1, (size_t)hdr.size, fp);
  fclose(fp);

  memset(amx, 0, sizeof(*amx));
  return amx_Init(amx, memblock);
}

bool srun_LoadDebugInfo(AMX_DBG *amx,const char *filename)
{
  FILE *fp;

  if ((fp = fopen(filename, "rb")) == NULL )
    return false;

  bool retval;
  retval = (dbg_LoadInfo(amx,fp)==AMX_ERR_NONE);
  fclose(fp);
  return retval;
}

//-------------------------------------------------------------------------------------- Public Functions
cScriptInstance *
Script_GetInstance(AMX *amx)
{
  return (cScriptInstance*)(amx);
}
tGameObjectID 
Script_GetThisID(AMX *amx)
{
  return Script_GetInstance(amx)->GetThis();
}
tGameObjectID 
Script_GetOtherID(AMX *amx)
{
  return Script_GetInstance(amx)->GetOther();
}

Titan *       
Script_GetTitan(AMX *amx)
{
  return Script_GetInstance(amx)->GetScriptSys()->GetTitan();
}

void 
Script_SetWaitTime(AMX *amx,int time_msecs)
{
  Script_GetInstance(amx)->SetSleepTime((float)time_msecs/1000.0f);
}


// EOF
