// testSqPlus.cpp
// Created by John Schultz 9/5/2005
// Free for any use.

#include <stdarg.h> 
#include <stdio.h>
#include "sqplus.h"

using namespace SqPlus;

struct CTestObj {
  float x;
  float y;
  float z;
  CTestObj() {
    x = y = z = 0.f;
  }
  void update(float t) {
    x += t;
  } // update
  void print(void) {
    SQChar buff[256];
    scsprintf(buff,_SC("x: %f\n"),x);
//    OutputDebugString(buff);
    SCPUTS(buff);
  } // print
};

_DECL_CLASS(TestObj);

_IMPL_NATIVE_CONSTRUCTION(TestObj,CTestObj);

_MEMBER_FUNCTION_IMPL(TestObj,constructor) {
  CTestObj * newv = NULL;
  StackHandler sa(v);
  int nparams = sa.GetParamCount();
  newv = new CTestObj();
  return construct_TestObj(newv);
}

_MEMBER_FUNCTION_IMPL(TestObj,_set) {
  StackHandler sa(v);
  _CHECK_SELF(CTestObj,TestObj);
  const SQChar *s = sa.GetString(2);
  int index = s?s[0]:sa.GetInt(2);
  switch(index) {
  case 0: case 'x': case 'r':
    return sa.Return(self->x = sa.GetFloat(3));
    break;
  case 1: case 'y': case 'g':
    return sa.Return(self->y = sa.GetFloat(3));
    break;
  case 2: case 'z': case 'b':
    return sa.Return(self->z = sa.GetFloat(3));
    break;
  } // switch
  return SQ_ERROR;
}

_MEMBER_FUNCTION_IMPL(TestObj,_get) {
  StackHandler sa(v);
  _CHECK_SELF(CTestObj,TestObj);
  const SQChar *s = sa.GetString(2);
  if(s && (s[1] != 0)) return SQ_ERROR;
  int index = s && (s[1] == 0)?s[0]:sa.GetInt(2);
  switch(index) {
  case 0: case 'x': case 'r': return sa.Return(self->x); break;
  case 1: case 'y': case 'g':	return sa.Return(self->y); break;
  case 2: case 'z': case 'b': return sa.Return(self->z); break;
  } // switch
  return SQ_ERROR;
}

_MEMBER_FUNCTION_IMPL(TestObj,update) {
  StackHandler sa(v);
  _CHECK_SELF(CTestObj,TestObj);
  SQObjectType type = (SQObjectType)sa.GetType(2);
  if (type == OT_FLOAT || type == OT_INTEGER) {
    float t = sa.GetFloat(2);
    self->update(t);
  } else {
    SQChar buff[256];
    scsprintf(buff,_SC("Invalid type for CTestObj::update(float): type %d\n"),type);
//    OutputDebugString(buff);
    SCPUTS(buff);
  } // if
  return SQ_OK;;
}

_MEMBER_FUNCTION_IMPL(TestObj,print) {
  StackHandler sa(v);
  _CHECK_SELF(CTestObj,TestObj);
  SQChar buff[256];
  scsprintf(buff,_SC("x: %f y: %f z: %f\n"),self->x,self->y,self->z);
//  OutputDebugString(buff);
  SCPUTS(buff);
//  return sa.ThrowError(_SC("Error initializing the device"));
  return SQ_OK;;
}

_MEMBER_FUNCTION_IMPL(TestObj,_print) {
  _CHECK_SELF(CTestObj,TestObj);
  SCPUTS(_SC("_print: "));
  return __TestObj_print(v);
}

_BEGIN_CLASS(TestObj)
_MEMBER_FUNCTION(TestObj,constructor,1,_SC("x")) // x = instance ('self/this' not yet created), no arguments.
_MEMBER_FUNCTION(TestObj,_set,3,_SC("xs|n"))     // x = instance, string, or int/float, as .x, .y, .z, or [0], [1], [2].
_MEMBER_FUNCTION(TestObj,_get,2,_SC("xs|n"))     // x = instance, string, or int/float, as .x, .y, .z, or [0], [1], [2].
_MEMBER_FUNCTION(TestObj,update,2,_SC("xn"))     // x = instance (this), n = int or float.
_MEMBER_FUNCTION(TestObj,print,1,_SC("x"))       // x = instance (this).
_MEMBER_FUNCTION(TestObj,_print,1,_SC("x"))      // x = instance (this).
_END_CLASS(TestObj)

#ifdef SQUNICODE 
#define scvprintf vwprintf 
#else 
#define scvprintf vprintf 
#endif 

void printfunc(HSQUIRRELVM v,const SQChar *s,...) { 
  va_list arglist;
  va_start(arglist, s);
  scvprintf(s, arglist);
  va_end(arglist);
}

int testFunc(HSQUIRRELVM v) {
  StackHandler sa(v);
  int paramCount = sa.GetParamCount();
  scprintf(_SC("testFunc: numParams[%d]\n"),paramCount);
  for (int i=1; i <= paramCount; i++) {
    scprintf(_SC("param[%d]: "),i);
    switch(sa.GetType(i)) {
    case OT_TABLE:   scprintf(_SC("OT_TABLE[0x%x]\n"),sa.GetObjectHandle(i)); break;
    case OT_INTEGER: scprintf(_SC("OT_INTEGER[%d]\n"),sa.GetInt(i));    break;
    case OT_FLOAT:   scprintf(_SC("OT_FLOAT[%f]\n"),sa.GetFloat(i));    break;
    case OT_STRING:  scprintf(_SC("OT_STRING[%s]\n"),sa.GetString(i));  break;
    default:
      scprintf(_SC("TYPEID[%d]\n"),sa.GetType(i));
    } // switch
  } // for
  return SQ_OK;
} // testFunc

// === BEGIN User Pointer version ===
#if 0

int setVarFunc2(HSQUIRRELVM v) {
  StackHandler sa(v);
  if (sa.GetType(1) == OT_TABLE) {
    HSQOBJECT htable = sa.GetObjectHandle(1);
    SquirrelObject table(htable);
    const SQChar * el = sa.GetString(2);
    SquirrelObject upValMapPtr  = table.GetValue(_SC("_uvp"));
    SquirrelObject upValMapType = table.GetValue(_SC("_uvt"));
    if (!upValMapType.Exists(el)) {
      return SQ_ERROR;
    } // if
    int vType = upValMapType.GetInt(el);
    switch (vType) {
    case TypeInfo<int>::TypeID: {
      int * val = (int *)upValMapPtr.GetUserPointer(el);
      if (val) {
        *val = sa.GetInt(3);
        return sa.Return(*val);
      } else {
        return sa.Return(-1);
      } // if
    } // case
    case TypeInfo<float>::TypeID: {
      float * val = (float *)upValMapPtr.GetUserPointer(el);
      if (val) {
        *val = sa.GetFloat(3);
        return sa.Return(*val);
      } else {
        return sa.Return(-1);
      } // if
    } // case
    case TypeInfo<bool>::TypeID: {
      bool * val = (bool *)upValMapPtr.GetUserPointer(el);
      if (val) {
        *val = sa.GetBool(3) ? true : false;
        return sa.Return(*val);
      } else {
        return sa.Return(-1);
      } // if
    } // case
    } // switch
  } // if
  return SQ_ERROR;
} // setVarFunc2

int getVarFunc2(HSQUIRRELVM v) {
  StackHandler sa(v);
  if (sa.GetType(1) == OT_TABLE) {
    HSQOBJECT htable = sa.GetObjectHandle(1);
    SquirrelObject table(htable);
    int type = sa.GetType(2);
    const SQChar * el = sa.GetString(2);
    SquirrelObject upValMap     = table.GetValue(_SC("_uvp"));
    SquirrelObject upValMapType = table.GetValue(_SC("_uvt"));
    if (!upValMapType.Exists(el)) {
      return SQ_ERROR;
    } // if
    int vType = upValMapType.GetInt(el);
    switch (vType) {
    case TypeInfo<int>::TypeID: {
      int * val = (int *)upValMap.GetUserPointer(el);
      if (val) {
        return sa.Return(*val);
      } else {
        return sa.Return(-1);
      } // if
    } // case
    case TypeInfo<float>::TypeID: {
      float * val = (float *)upValMap.GetUserPointer(el);
      if (val) {
        return sa.Return(*val);
      } else {
        return sa.Return(-1);
      } // if
    } // case
    case TypeInfo<bool>::TypeID: {
      bool * val = (bool *)upValMap.GetUserPointer(el);
      if (val) {
        return sa.Return(*val);
      } else {
        return sa.Return(-1);
      } // if
    } // case
    } // switch
  } // if
  return SQ_ERROR;
} // getVarFunc2

template<typename T>
void bindVariable2(SquirrelObject & so,T & var,const SQChar * scriptVarName) {
  SquirrelObject __upValMapPtr;
  SquirrelObject __upValMapType;

  if (so.Exists(_SC("_uvp"))) {
    __upValMapPtr  = so.GetValue(_SC("_uvp"));
    __upValMapType = so.GetValue(_SC("_uvt"));
  } else {
    __upValMapPtr  = SquirrelVM::CreateTable();
    __upValMapType = SquirrelVM::CreateTable();
  } // if

  int varType = TypeInfo<T>::TypeID;
  __upValMapPtr.SetUserPointer(scriptVarName,&var);
  __upValMapType.SetValue(scriptVarName,varType);

  so.SetValue(_SC("_uvp"),__upValMapPtr);
  so.SetValue(_SC("_uvt"),__upValMapType);

  SquirrelObject delegate = so.GetDelegate();
  if (!delegate.Exists(_SC("_set"))) {
    delegate = SquirrelVM::CreateTable();
    SquirrelVM::CreateFunction(delegate,"_set",setVarFunc2,_SC("sn|b")); // String var name = number(int or float) or bool.
    SquirrelVM::CreateFunction(delegate,"_get",getVarFunc2,_SC("s"));    // String var name.
    so.SetDelegate(delegate);
  } // if

} // bindVariable2
#endif

// === BEGIN Old, initial test versions. ===

int setIntFunc(HSQUIRRELVM v) {
  StackHandler sa(v);
  if (sa.GetType(1) == OT_TABLE) {
    HSQOBJECT htable = sa.GetObjectHandle(1);
    SquirrelObject table(htable);
    const SQChar * el = sa.GetString(2);
    SquirrelObject upValMap = table.GetValue(_SC("upValMap"));
    int * val = (int *)upValMap.GetUserPointer(el);
    if (val) {
      *val = sa.GetInt(3);
      return sa.Return(*val);
    } else {
      return sa.Return(-1);
    } // if
  } // if
  return SQ_ERROR;
} // setIntFunc

int getIntFunc(HSQUIRRELVM v) {
  StackHandler sa(v);
  if (sa.GetType(1) == OT_TABLE) {
    HSQOBJECT htable = sa.GetObjectHandle(1);
    SquirrelObject table(htable);
    int type = sa.GetType(2);
    const SQChar * el = sa.GetString(2);
    SquirrelObject upValMap = table.GetValue(_SC("upValMap"));
    int * val = (int *)upValMap.GetUserPointer(el);
    if (val) {
      return sa.Return(*val);
    } else {
      return sa.Return(-1);
    } // if
  } // if
  return SQ_ERROR;
} // getIntFunc

// === END Old, initial tests versions. ===

_DECL_STATIC_NAMESPACE(GB); // Globals

_MEMBER_FUNCTION_IMPL(GB,Update) {
  StackHandler sa(v);
  scprintf(_SC("GB.Update()\n"));
  return sa.Return(true);
}

enum {TEST_CONST=123};

_BEGIN_NAMESPACE(GB)
_MEMBER_FUNCTION(GB,Update,0,0)
_BEGIN_NAMESPACE_CONSTANTS(GB)
_CONSTANT_IMPL(TEST_CONST,OT_INTEGER)
_END_NAMESPACE(GB,NULL)

#if 0
int getVarName(HSQUIRRELVM v) {
  StackHandler sa(v);
  const SQChar * varName = sq_getlocal(v,1,0);
  return sa.Return(varName);
} // getVarName
#endif

void newtest(void) {
  scprintf(_SC("NewTest\n"));
}

SQChar * newtestR1(const SQChar * inString) {
  scprintf(_SC("NewTestR1: %s\n"),inString);
  return _SC("Returned String");
}

struct NewTestObj {
  ScriptStringVar64 s1;
  ScriptStringVar32 s2;
  int pad;
  int val;
  int c1;
  ScriptStringVar8 c2; // 8 char plus null (max string is 8 printable chars).
  NewTestObj() : val(777) {
    s1 = _SC("s1");
    s2 = _SC("s2");
    c1 = 996;
    c2 = _SC("It's a 997"); // Prints: "It's a 9", as only 8 chars in static buffer (plus null).
  }
  void newtest(void) {
    scprintf(_SC("NewTest: %d\n"),val);
  }
  SQChar * newtestR1(const SQChar * inString) {
    scprintf(_SC("NewTestR1: Member var val is %d, function arg is %s\n"),val,inString);
    return _SC("Returned String");
  }

  int multiArgs(HSQUIRRELVM v) {
    StackHandler sa(v);
    int paramCount = sa.GetParamCount();
    int p1 = sa.GetInt(2);
    int p2 = sa.GetInt(3);
    int p3 = sa.GetInt(4);
    return 0;
  } // multiArgs

  int _set(HSQUIRRELVM v) {
    StackHandler sa(v);
    int paramCount = sa.GetParamCount();
    const SQChar * el = sa.GetString(2);
    val = sa.GetInt(3);
    return sa.Return(val);
//    return setInstanceVarFunc(v);
  }

  int _get(HSQUIRRELVM v) {
    StackHandler sa(v);
    int paramCount = sa.GetParamCount();
//    return getInstanceVarFunc(v);
    return sa.Return(val);
  }

};

struct CustomTestObj {
  ScriptStringVar128 name;
  int val;
  bool state;
  CustomTestObj() : val(0), state(false) { name = _SC("empty"); }
  CustomTestObj(const SQChar * _name,int _val,bool _state) : val(_val), state(_state) {
    name = _name;
  }
  static int construct(HSQUIRRELVM v) {
    StackHandler sa(v);
    int paramCount = sa.GetParamCount();
    if (paramCount == 1) {
      return PostConstruct(v,new CustomTestObj(),release);
    } if (paramCount == 4) {
      return PostConstruct(v,new CustomTestObj(sa.GetString(2),sa.GetInt(3),sa.GetBool(4)?true:false),release);
    } // if
    return sq_throwerror(v,_SC("Invalid Constructor arguments"));
  } // construct

  SQ_DECLARE_RELEASE(CustomTestObj)

  // Member function that handles variable types.
  int varArgTypes(HSQUIRRELVM v) {
    StackHandler sa(v);
    int paramCount = sa.GetParamCount();
    if (sa.GetType(2) == OT_INTEGER) {
      val = sa.GetInt(2);
    } // if
    if (sa.GetType(2) == OT_STRING) {
      name = sa.GetString(2);
    } // if
    if (sa.GetType(3) == OT_INTEGER) {
      val = sa.GetInt(3);
    } // if
    if (sa.GetType(3) == OT_STRING) {
      name = sa.GetString(3);
    } // if
    return 0;
  } // varArgTypes

  // Member function that handles variable types and has variable return types+count.
  int varArgTypesAndCount(HSQUIRRELVM v) {
    StackHandler sa(v);
    int paramCount = sa.GetParamCount();
    SQObjectType type1 = (SQObjectType)sa.GetType(1); // Always OT_INSTANCE
    SQObjectType type2 = (SQObjectType)sa.GetType(2);
    SQObjectType type3 = (SQObjectType)sa.GetType(3);
    SQObjectType type4 = (SQObjectType)sa.GetType(4);
    int returnCount = 0;
    if (paramCount == 3) {
      sq_pushinteger(v,val);
      returnCount = 1;
    } else if (paramCount == 4) {
      sq_pushinteger(v,val);
      sq_pushstring(v,name,-1);
      returnCount = 2;
    } // if
    return returnCount;
  } //

  int noArgsVariableReturn(HSQUIRRELVM v) {
    if (val == 123) {
      val++;
      return 0; // This will print (null).
    } else if (val == 124) {
      sq_pushinteger(v,val); // Will return int:124.
      val++;
      return 1;
    } else if (val == 125) {
      sq_pushinteger(v,val);
      name = _SC("Case 125");
      sq_pushstring(v,name,-1);
      val = 123; // reset
      return 2;
    } // if
    return 0;
  } // noArgsVariableReturn

  // Registered with func() instead of funcVarArgs(): fixed (single) return type.
  const SQChar * variableArgsFixedReturnType(HSQUIRRELVM v) {
    StackHandler sa(v);
    int paramCount = sa.GetParamCount();
    SQObjectType type1 = (SQObjectType)sa.GetType(1); // Always OT_INSTANCE
    SQObjectType type2 = (SQObjectType)sa.GetType(2);
    SQObjectType type3 = (SQObjectType)sa.GetType(3);
    if (paramCount == 1) {
      return _SC("No Args");
    } else if (paramCount == 2) {
      return _SC("One Arg");
    } else if (paramCount == 3) {
      return _SC("Two Args");
    } // if
    return _SC("More than two args");
  } // variableArgsFixedReturnType

  void manyArgs(int i,float f,bool b,const SQChar * s) {
    scprintf(_SC("i: %d, f: %f, b: %s, s: %s\n"),i,f,b?_SC("true"):_SC("false"),s);
  } // manyArgs

  float manyArgsR1(int i,float f,bool b,const SQChar * s)  {
    manyArgs(i,f,b,s);
    return i+f;
  } // manyArgsR1

};

#if 0
struct Base {
  int val1;
  ScriptStringVar16 nameBase;
  Base() : nameBase(_SC("Base")) {
    val1 = 123;
  }
  void funcBase(void) {
    scprintf(_SC("Val1: %d, Name: %s\n"),val1,nameBase.s);
  }
};

struct Derived {
  int val2;
  ScriptStringVar16 nameDerived;
  Derived() : nameDerived(_SC("Derived")), val2(456) {
    val2 = 456;
  }
  void funcDerived(void) {
    scprintf(_SC("Val2: %d, Name: %s\n"),val2,nameDerived.s);
  }
};
#endif

//SQ_DECLARE_CLASS(NewTestObj);

class TestSqPlus {
public:
  void init(void) {
    SquirrelVM::Init();
//    sq_setprintfunc(SquirrelVM::GetVMPtr(),printfunc); //sets the print function.
    _INIT_CLASS(TestObj);
    _INIT_STATIC_NAMESPACE(GB);
  } // if

  TestSqPlus() {
    init();

    try {
      HSQUIRRELVM v = SquirrelVM::GetVMPtr();

      // === BEGIN Global Function binding tests ===

      // Implemented as SquirrelVM::CreateFunction(rootTable,func,name,typeMask);
      SquirrelVM::CreateFunctionGlobal(&testFunc,_SC("testFunc0"));
      SquirrelVM::CreateFunctionGlobal(&testFunc,_SC("testFuncN"),_SC("n"));
      SquirrelVM::CreateFunctionGlobal(&testFunc,_SC("testFuncS"),_SC("s"));

      // === Register Standard Functions ===

      RegisterGlobal(v,&newtest,_SC("test"));
      RegisterGlobal(v,&newtestR1,_SC("testR1"));

      // === Register Member Functions to existing classes (as opposed to instances of classes) ===

      NewTestObj t1,t2,t3;
      t1.val = 123;
      t2.val = 456;
      t3.val = 789;
      RegisterGlobal(v,t1,&NewTestObj::newtest,_SC("testObj_newtest1"));
      RegisterGlobal(v,t2,&NewTestObj::newtest,_SC("testObj_newtest2")); // Register newtest() again with different name and object pointer.
      SquirrelObject tr = SquirrelVM::GetRootTable(); // Can be any object supporting closures (functions).
      Register(v,tr.GetObjectHandle(),t3,&NewTestObj::newtestR1,_SC("testObj_newtestR1")); // Return value version.

      // === END Global Function binding tests ===

      // === BEGIN Class Instance tests ===

#if 0
      SQClassDef<NewTestObj> sqClass(_SC("NewTestObj"));
      sqClass.func(&NewTestObj::newtestR1,_SC("newtestR1"));
      sqClass.var(&NewTestObj::val,_SC("val"));
      sqClass.var(&NewTestObj::s1,_SC("s1"));
      sqClass.var(&NewTestObj::s2,_SC("s2"));
      sqClass.var(&NewTestObj::c1,_SC("c1"),VAR_ACCESS_READ_ONLY);
      sqClass.var(&NewTestObj::c2,_SC("c2"),VAR_ACCESS_READ_ONLY);
      sqClass.funcVarArgs(&NewTestObj::multiArgs,_SC("multiArgs"));
#else
      SQClassDef<NewTestObj>(_SC("NewTestObj")).
        func(&NewTestObj::newtestR1,_SC("newtestR1")).
        var(&NewTestObj::val,_SC("val")).
        var(&NewTestObj::s1,_SC("s1")).
        var(&NewTestObj::s2,_SC("s2")).
        var(&NewTestObj::c1,_SC("c1"),VAR_ACCESS_READ_ONLY).
        var(&NewTestObj::c2,_SC("c2"),VAR_ACCESS_READ_ONLY).
        funcVarArgs(&NewTestObj::multiArgs,_SC("multiArgs"));
#endif

      SQClassDef<CustomTestObj> customClass(_SC("CustomTestObj"));
      customClass.staticFuncVarArgs(&CustomTestObj::construct,_SC("constructor"),_SC("snb"));            // string, number, bool (all types must match).
      customClass.funcVarArgs(&CustomTestObj::varArgTypes,_SC("varArgTypes"),_SC("s|ns|ns|ns|n"));       // string or number + string or number.
      customClass.funcVarArgs(&CustomTestObj::varArgTypesAndCount,_SC("varArgTypesAndCount"),_SC("*"));  // "*"): no type or count checking.
      customClass.funcVarArgs(&CustomTestObj::noArgsVariableReturn,_SC("noArgsVariableReturn"));        // No type string means no arguments allowed.
      customClass.func(&CustomTestObj::variableArgsFixedReturnType,_SC("variableArgsFixedReturnType")); // Variables args, fixed return type.
      customClass.func(&CustomTestObj::manyArgs,_SC("manyArgs"));                                       // Many args, type checked.
      customClass.func(&CustomTestObj::manyArgsR1,_SC("manyArgsR1"));                                   // Many args, type checked, one return value.

// Old macro-based method. Must use SQ_DECLARE_CLASS() macro above.
#if 0
      SquirrelObject newClass = SQ_REGISTER_CLASS(NewTestObj);
      SQ_REGISTER_INSTANCE(newClass,NewTestObj,newtestR1);
// Currently, can use either automatic variable handling OR manual handling (but not both at once).
#if 1
      SQ_REGISTER_INSTANCE_VARIABLE(newClass,NewTestObj,val); // _set/_get will be defined to use automatic methods for val.
#else
      SQ_REGISTER_INSTANCE_VARARGS(newClass,NewTestObj,_set); // _set is now defined and won't be overridden.
      SQ_REGISTER_INSTANCE_VARARGS(newClass,NewTestObj,_get);
      SQ_REGISTER_INSTANCE_VARIABLE(newClass,NewTestObj,val); // Access will be through _set/_get member functions above, not automatic methods.
#endif

#if 1
      // With an HSQUIRRELVM argument, can handle variable args, and when registered this way can return multiple values.
      SQ_REGISTER_INSTANCE_VARARGS(newClass,NewTestObj,multiArgs);
#else
      // With an HSQUIRRELVM argument, can handle variable args, but can only return one value (defined by the function definition).
      SQ_REGISTER_INSTANCE(newClass,NewTestObj,multiArgs); 
#endif
#endif

#if 1
      SquirrelObject testReg0 = SquirrelVM::CompileBuffer(_SC(" co <- CustomTestObj(\"hello\",123,true); co.varArgTypes(\"str\",123,123,\"str\"); co.varArgTypes(123,\"str\",\"str\",123); "));
      SquirrelVM::RunScript(testReg0);

      SquirrelObject testReg0a = SquirrelVM::CompileBuffer(_SC(" print(co.varArgTypesAndCount(1,true)); print(co.varArgTypesAndCount(2,false,3.)); print(\"\\n\"); "));
      SquirrelVM::RunScript(testReg0a);

      SquirrelObject testReg0b = SquirrelVM::CompileBuffer(_SC(" print(co.noArgsVariableReturn()); print(co.noArgsVariableReturn()); print(co.noArgsVariableReturn()); print(\"\\n\"); "));
      SquirrelVM::RunScript(testReg0b);

      SquirrelObject testReg0c = SquirrelVM::CompileBuffer(_SC(" print(co.variableArgsFixedReturnType(1)); print(co.variableArgsFixedReturnType(1,2)); print(co.variableArgsFixedReturnType(1,2,3)); print(\"\\n\"); "));
      SquirrelVM::RunScript(testReg0c);

      SquirrelObject testReg0d = SquirrelVM::CompileBuffer(_SC(" co.manyArgs(111,222.2,true,\"Hello\"); print(co.manyArgsR1(333,444.3,false,\"World\")); print(\"\\n\"); "));
      SquirrelVM::RunScript(testReg0d);
#endif

// Inheriting from an existing base class in this way is not currently supported.
// Requires either a Squirrel language/behavior change, or extra code at the interface
// layer to allocate memory and call constructor of parent class (and perhaps
// handle constructor args) store pointer in UserData, and handle proper variable access
// (get correct class/struct pointer) and member function calls (store class
// type/id in function up-var and search/hash for actual 'this' pointer for function call).
#if 0
      SQClassDef<Base>(_SC("Base")).
        var(&Base::nameBase,_SC("nameBase")).
        func(&Base::funcBase,_SC("funcBase"));

      SQClassDef<Derived>("Derived",_SC("Base")).
        var(&Derived::nameDerived,_SC("nameDerived")).
        func(&Derived::funcDerived,_SC("funcDerived"));

//      SquirrelObject testBaseDerived = SquirrelVM::CompileBuffer(_SC(" local base = Base(); print(base.nameBase); local derived = Derived(); print(\"NameBase: \"+derived.nameBase+\" NameDerived: \"+derived.nameDerived); derived.funcBase(); "));
      SquirrelObject testBaseDerived = SquirrelVM::CompileBuffer(_SC(" local derived = Derived(); print(\"NameBase: \"+derived.nameBase+\" NameDerived: \"+derived.nameDerived); derived.funcBase(); "));
      SquirrelVM::RunScript(testBaseDerived);
#endif

#if 1
      SquirrelObject testReg1a = SquirrelVM::CompileBuffer(_SC(" co <- CustomTestObj(\"hello\",123,true); co.noArgsVariableReturn(); local t = NewTestObj(); print(\"C1: \"+t.c1); print(\"C2: \"+t.c2); // t.c1 = 123; "));
      SquirrelVM::RunScript(testReg1a);

      // Constant test (read only var). Var can change on C++ side, but not on script side.
      try {
        SquirrelObject testRegConstant = SquirrelVM::CompileBuffer(_SC(" local t = NewTestObj(); t.c1 = 123; "));
        SquirrelVM::RunScript(testRegConstant);
      } // try
      catch (SquirrelError & e) {
        SQChar buff[256];
        scsprintf(buff,_SC("Error: %s, %s\n"),e.desc,_SC("Squirrel::TestConstant"));
        //      OutputDebugString(buff);
        SCPUTS(buff);
      } // catch

      SquirrelObject testReg1 = SquirrelVM::CompileBuffer(_SC(" local t = NewTestObj(); t.newtestR1(\"Hello\"); t.val = 789; print(t.val); print(t.s1); print(t.s2); t.s1 = \"New S1\"; print(t.s1); "));
      SquirrelVM::RunScript(testReg1);

      SquirrelObject testReg2 = SquirrelVM::CompileBuffer(_SC(" local t = NewTestObj(); t.val = 789; print(t.val); t.val = 876; print(t.val); t.multiArgs(1,2,3); t.multiArgs(1,2,3,4); "));
      SquirrelVM::RunScript(testReg2);
      SquirrelObject testReg3 = SquirrelVM::CompileBuffer(_SC(" test(); local rv = testR1(\"Hello\"); print(rv); "));
      SquirrelVM::RunScript(testReg3);     
      SquirrelObject testReg4 = SquirrelVM::CompileBuffer(_SC(" print(\"\\nMembers:\"); testObj_newtest1(); testObj_newtest2(); print(testObj_newtestR1(\"Hello Again\")); "));
      SquirrelVM::RunScript(testReg4);

      SquirrelObject defCallFunc = SquirrelVM::CompileBuffer(_SC(" function callMe(var) { print(\"I was called by: \"+var); return 123; }"));
      SquirrelVM::RunScript(defCallFunc);

      SquirrelObject root = SquirrelVM::GetRootTable();

      // Get a function from the root table and call it.
      SquirrelFunction<int> callFunc(_SC("callMe"));
      int ival = callFunc(_SC("Squirrel"));
      scprintf(_SC("IVal: %d\n"),ival);
      ival = 0;
      // Get a function from any table.
      SquirrelFunction<int> callFunc2(root.GetObjectHandle(),_SC("callMe"));
      ival = callFunc(456); // Argument count is checked; type is not.

      // === END Class Instance tests ===

      // === BEGIN macro-only class-registrated tests ===
//      SquirrelVM::CreateFunctionGlobal(_SC("getName",getVarName,"*")); // * = any type.
//      SquirrelObject main = SquirrelVM::CompileBuffer(_SC("local testObj = TestObj(); testObj.print(); testObj.update(\"ab\"); testObj.print()"));
//      SquirrelObject main = SquirrelVM::CompileBuffer(_SC("local LF = \"\\n\"; local testObj = TestObj(); testObj.print(); testObj.update(1.5); testObj.print(); testObj.y += 10.; testObj.z = -1.; print(testObj.y+LF); print(\"Array: \"+testObj[0]+LF); print(testObj); print(LF); "));
//      SquirrelObject main = SquirrelVM::CompileBuffer(_SC("local testObj = TestObj(); testObj.z = -1.; print(testObj); testFunc(); testFunc0(); testFuncN(1.); testFuncS(\"Hello\"); "));
//      SquirrelObject main = SquirrelVM::CompileBuffer(_SC("testFunc0(); testFuncN(1); testFuncN(1.23); testFuncS(\"Hello\");"));

      SquirrelObject main = SquirrelVM::CompileBuffer(_SC("table1 <- {key1=\"keyVal\",key2 = 123};\n if (\"key1\" in table1)\n print(\"Sq: Found it\");\n else\n print(\"Sq: Not found\");"));
      SquirrelVM::RunScript(main);
      SquirrelObject table1 = root.GetValue(_SC("table1"));
      if (table1.Exists(_SC("key1"))) {
        scprintf(_SC("C++: Found it.\n"));
      } else {
        scprintf(_SC("C++: Did not find it.\n"));
      } // if

      // === BEGIN Simple variable binding tests ===

      int iVar = 777;
      float fVar = 88.99f;
      bool bVar = true;
      BindVariable(root,&iVar,_SC("iVar"));
      BindVariable(root,&fVar,_SC("fVar"));
      BindVariable(root,&bVar,_SC("bVar"));

      static ScriptStringVar128 testString;
      scsprintf(testString,_SC("This is a test string"));
      BindVariable(root,&testString,_SC("testString"));

      // === END Simple variable binding tests ===

      // === BEGIN Array Tests ===

      SquirrelObject array = SquirrelVM::CreateArray(10);
      int i;
      for (i = 0; i < 10; i++) array.SetValue(i,i);
      array.ArrayAppend(123);          // int
      array.ArrayAppend(true);         // bool (must use bool and not SQBool (SQBool is treated as INT by compiler).
      array.ArrayAppend(false);        // bool (must use bool and not SQBool (SQBool is treated as INT by compiler).
      array.ArrayAppend(123.456f);     // float
      array.ArrayAppend(_SC("string")); // string
      array.ArrayAppend(456);          // Will be popped and thrown away (below).

      // Pop 3 items from array:
      array.ArrayPop(SQFalse);                 // Don't retrieve the popped value (int:123).
      SquirrelObject so1 = array.ArrayPop();   // Retrieve the popped value.
      const SQChar * val1 = so1.ToString();    // Get string.
      float val2 = array.ArrayPop().ToFloat(); // Pop and get float.
      scprintf(_SC("[Popped values] Val1: %s, Val2: %f\n"),val1,val2);

      int startIndex = array.Len();
      array.ArrayExtend(10); // Implemented as: ArrayResize(Len()+amount).
      for (i = startIndex; i < array.Len(); i++) array.SetValue(i,i*10);
      root.SetValue(_SC("array"),array);

      SquirrelObject arrayr = array.Clone(); // Get a copy as opposed to another reference.
      arrayr.ArrayReverse();
      root.SetValue(_SC("arrayr"),arrayr);

      // === END Array Tests ===

//      SquirrelObject test = SquirrelVM::CompileBuffer(_SC(" print(iVar); print(fVar); print(bVar); iVar += 1; fVar += 100.; bVar = false; print(iVar); print(fVar); print(bVar); xVar = 1; ")); // Test for xVar error.
//      SquirrelObject test = SquirrelVM::CompileBuffer(_SC(" print(iVar); print(fVar); print(bVar); iVar += 1; fVar += 100.; bVar = false; print(iVar); print(fVar); print(bVar); print(testString); testString = \"New string value\"; print(testString);"));
      SquirrelObject define_printArray = SquirrelVM::CompileBuffer(_SC(" function printArray(name,array) { print(name+\".len() = \"+array.len()); foreach(i, v in array) if (v != null) { if (typeof v == \"bool\") v = v ? \"true\" : \"false\"; print(\"[\"+i+\"]: \"+v); } } "));
      SquirrelVM::RunScript(define_printArray);
      SquirrelObject test = SquirrelVM::CompileBuffer(_SC(" printArray(\"array\",array); printArray(\"arrayr\",arrayr); "));

//      SquirrelObject test = SquirrelVM::CompileBuffer(_SC(" iVar = 1; print(iVar); "));
//      SquirrelObject test = SquirrelVM::CompileBuffer(_SC(" print(\"GB:\"+GB.TEST_CONST); GB.TEST_CONST += 1; GB.Update(); print(\"GB2:\"+GB.TEST_CONST); \n"));

      SquirrelVM::RunScript(test);
#endif

    } // try
    catch (SquirrelError & e) {
//      SquirrelVM::DumpStack();
      SQChar buff[256];
      scsprintf(buff,_SC("Error: %s, %s\n"),e.desc,_SC("Squirrel::TestObj"));
//      OutputDebugString(buff);
      SCPUTS(buff);
    } // catch

  }

  ~TestSqPlus() {
    SquirrelVM::Shutdown();
  }

};

void doTest(void) {
  TestSqPlus testSqPlus;
} // doTest

int main(int argc,char * argv[]) {

  // Run twice to make sure cleanup/shutdown works OK.
  SCPUTS(_SC("Start Pass 1\n"));
  doTest();
#if 0
  SCPUTS(_SC("Start Pass 2\n"));
  doTest();
#endif
  SCPUTS(_SC("Done.\n"));

  scprintf(_SC("Press RETURN to exit."));
  getchar();

	return 0;
}