#ifndef __TEST_H__
#define __TEST_H__


// small example for the YInG- the YAC Interface Generator.
//
// in order to compile this example library ("plugin"), first create the YAC interface by running
// "tks ../yac/ying test.h"
// which will create the following files:
//    ying_test.h
//    ying_test.cpp
//    ying_MyClass.h
//    ying_MyClass.cpp
//    .
//    .
//
// you may then e.g. load the plugin into the tkscript application host and test the whole thing
// by issueing e.g. "tks test"
//


// ---- 
// ---- 
// ---- the YG define is used to set the group name for the current set of classes/functions.
// ---- THe group name is used to prefix the output files, e.g. 'ying_<group>_<class>.cpp'
// ---- 
// ---- 
YG("test")

// ----
// ---- 
// ---- the YC define is used to export the given class. The class definition must appear
// ---- on the same text line.
// ---- 
// ---- the YAC define is used to forward-declarate some "magic" methods which will be implemented
// ---- by including the 'ying_<group>_<class>.cpp' file (which will be auto-generated by YInG).
// ----
// ---- 
YC class MyEmptyClass  : public YAC_Object {
	public:
		YAC(MyEmptyClass);
};

// ---- 
// ---- the YP define is used to export members (properties).
// ---- YInG will generate set/get methods for each member and also create an export-table
// ---- which contains the member offsets relative to the class object base pointer.
// ---- 
YC class MyVector : public YAC_Object {
	public:
		YP float x;
		YP float y;
		YP float z;

		YAC(MyVector);

      sSI YAC_VCALL yacTensorRank(void) { return YAC_TENSOR_RANK_VECTOR; }

      YM sF32 getAbs(void);
};

// ----
// ---- the next example will export the set/get methods in this class
// ---- (look for
// ----                YM //int getA(void);
// ----                YM //void setA(int);
// ---- )
// ---- 
class BaseClass : public YAC_Object {
public:
    sSI a;
    sSI b;

    sSI  getA(void)   { return a;}
    void setA(sSI _a) { a=_a; }
};

// ----
// ---- the YCI and YCF defines are used to export int and float constants.
// ----
// ---- please notice that YInG automatically creates the rtti-check/prepare code so
// ---- you can also directly export methods which use objects derived from YAC_Object
// ---- (in this example YAC_String and MyClass)
// ----
YC class MyClass : public BaseClass {
 public:
	YAC(MyClass);

#define              XXXA  YCI 42
#define XXXB  YCI 64
#define XXXPI YCF 3.1415f

    YP sSI i;
    YP sSI j;
    YP sSI k;
    YP YAC_String *myString;

    // ----
    // ---- the YM // syntax is used to export methods that were originally declared
    // ---- in a base-class.
    // ----
    YM //sSI  getA(void)   { return a;}
    YM //void setA(sSI _a) { a=_a; }


	               MyClass(void) {a=99; myString=0;}
    ~MyClass()
       {
          if(myString)
             yac_host->yacDelete(myString);
       }

    
    YM void        manyargs (int _i, float _f, YAC_String *_s, MyClass *_c) { yac_host->printf("manyargs _i=%i _f=%f _s=\"%s\" _c=%p\n", _i,_f,_s->chars,_c); }
    YM void        test1    (void)        { yac_host->printf("test1() called.\n"); }
    YM void        test2    (int _i)      { yac_host->printf("test2(%i) called.\n", _i); }
    YM int         test3    (void)        {  return 23; }
    YM int         test4    (int _i)      { return _i*_i; }
    YM void        test5    (YAC_Object*) {  }
    YM YAC_Object *test6    (void)        { return this; }
    YM void        test7    (YAC_String*) { }
    YM sF32        test8    (YAC_String*) { return 1.23f; }
    YM YAC_String *test9    (void)        { return 0;};
    YM MyClass    *test10   (void)        { return this; };
    YM void        calcState(sF32 _deltatime, sSI _binterpolate) { yac_host->printf("deltatime=%f, binterpolate=%i\n", _deltatime, _binterpolate); }

       // ----
       // ---- if the string "YAC_Value *_r" appears within the declaration of an exported
       // ---- method, the interface generator will interpret it as a "variable-return-type" method
       // ----
    YM void        returnNewMyClass(YAC_Value *_r);

    sBool YAC_VCALL yacToString(YAC_String *_s)
          // ----
          // ---- this method is called by the runtime whenever this object must be converted
          // ---- to a string, e.g. in a "print" statement.
          // ----
       {
#ifdef YAC_BIGSTRING
          _s->alloc(256);
          ::sprintf((char*)_s->chars, "(%i,%i,%i)", i,j,k);
          _s->fixLength();
          return 1;
#endif
          return 0;
       }

    // ---- Declare tensor rank of Object (used by testObjRet*() methods) ----
    sSI YAC_VCALL yacTensorRank(void) { return YAC_TENSOR_RANK_SCALAR; }
    

    // ---- getter/setter methods for i,j,k members ----
    YM sSI  getI (void)   { return i; }
    YM void setI (sSI _i) { i = _i; }

    YM sSI  getJ (void)   { return j; }
    YM void setJ (sSI _j) { j = _j; }

    YM sSI  getK (void)   { return k; }
    YM void setK (sSI _k) { k = _k; }

    // ---- Initialize "this" with integer value ----
    void YAC_VCALL yacValueOfI(sSI _i) { i = _i; }

    
    YM void getString(YAC_Value *_r)
          // ----
          // ---- this method returns a new string which is meant to be deleted by the tkscript runtime
          // ----
          // ---- if you define YAC_BIGSTRING before #including <yac.h>, the YAC_String class will
          // ---- implement the alloc() and fixLength() methods (along with some others)
          // ----
          // ---- Please notice that a getFoo() method can be addressed like a ".foo" property, i.e.
          // ---- print new MyClass().string; // will call MyClass::getString()
          // ----
       {
          YAC_String *r=YAC_New_String();
          yacToString(r);
          _r->initString(r, 1 /**deleteme */);
       }

    YM YAC_String *getMyString(void)
          // ----
          // ---- this method returns a "read-only" reference to the property "myString".
          // ----
          // ---- please remember that this class deletes "myString" when
          // ---- it is destructed; the tkscript runtime will just reference the returned
          // ---- object (which also means that it may only be referenced in scripts until
          // ---- the instance of this class which returned myString is destroyed)
          // ----
       {
          return myString;
       }


    YM void setMyString(YAC_Object *_s)
          // ----
          // ---- this method sets the property "myString" by assigning the given String.
          // ---- the YAC_BCHK() macro is used to test whether YAC_String is a baseclass of _s.
          // ----
       {
          if(YAC_BCHK(_s, YAC_CLID_STRING))
          {
             if(!YAC_VALID(myString))
             {
                myString = YAC_New_String();
             }
             myString->yacOperatorAssign(_s);
          }
          else
          {
             ::printf("[---] setMyString: parameter _s is not a String.\n");
          }
       }

    YM void setMyStringValue(YAC_Object *_vo)
          //
          // ---- this method assigns the given YAC_ValueObject (if it holds a YAC_String object)
          // ---- to the property "myString".
          // ----
          // ---- Notice that if the YAC_String value has the "deleteme" flag set, then the
          // ---- String object is unlinked from the ValueObject and placed in "myString".
          // ----
          // ---- script-wise this behaviour can be tested by using the #(<expr>) expression,
          // ---- which wraps the <expr> result in a YAC_ValueObject. Example: print #(new String()).string;
          // ---- resp. _=new MyClass().setMyStringValue( #(new String()) );
          // ----
          // ---- You should generally never keep object pointers that are passed from the script-engine to C++
          // ---- methods although it may sometimes be useful for speeds sake. At least *never* delete these
          // ---- object pointers unless they come from a YAC_Value that has the "deleteme" flag set.
          //
       {
          if(YAC_BCHK(_vo, YAC_CLID_VALUE))
          {
             YAC_ValueObject *vo=(YAC_ValueObject*)_vo;
             if(vo->type>3)
                if(YAC_BCHK(vo->value.object_val, YAC_CLID_STRING))
                {
                   if(vo->deleteme)
                   {
                      ::printf("[...] MyClass::setMyStringValue: unlinking value object..\n");
                      vo->deleteme=0;
                      if(YAC_VALID(myString))
                      {
                         YAC_DELETE(myString);
                      }
                      myString=vo->value.string_val;
                   }
                   else
                   {
                      if(!myString)
                         myString=YAC_New_String();
                      myString->yacOperatorAssign(vo->value.string_val);
                   }
                }
                else
                {
                      // ---- other type of YAC_Object
                   if(vo->value.object_val)
                   {
                      if(!myString)
                         myString=YAC_New_String();
                      vo->value.object_val->yacToString(myString);
                   }
                }
          }
          else
             setMyString(_vo);
       }

    //
    // Declare some methods that allow to deliver their return values in pre-allocated objects
    // e.g. o.testObjRet0() => myRetObj (statement, returns void)
    //  or  o.testObjRet0() <=> myRetObj (expression, returns myRetObj)
    // Note: In a script, the methods are named just "testObjRet0", "testObjRet1" !
    //
    YM void testObjRet0_YAC_RARG(YAC_Object *_ret42);  // Returns "42" in "return argument"
    YM void testObjRet1_YAC_RARG(sSI _i, YAC_Object *_ret); // Returns _i in "return argument"

    //
    // These methods are used when the testObjRet*() methods are called without the =>, <=> operator.
    // Note: The method signatures must be the same as the testObjRet*_YAC_RARG methods except for the
    //       last argument which is YAC_Value* instead of YAC_Object* !
    //
    YM void testObjRet0_YAC_RVAL(YAC_Value *_r); // Returns new Integer object (int. value= 42)
    YM void testObjRet1_YAC_RVAL(sSI _i, YAC_Value *_r); // Returns new Integer object (int.value = _i)
 
    //
    // Declare a method variant to be used when =>* is used. 
    // Note: If no dedicated RSELF variant is present, a fallback to RARG is used.
    //
    YM void testObjRet1_YAC_RSELF(sSI _i); // Returns _i in "self"


};


// ----
// ---- Example for a class that supports pooling
// ----
YC class MyPooledClass : public MyClass {
  public:
   YAC_POOLED(MyPooledClass, YAC_POOL_PRIORITY_MEDIUM);
};


// ----
// ---- the YF define is used to export global "C" functions
// ----
YF void YAC_CALL Say_Hello(void);

YF void YAC_CALL _Say_Hello2(void);

YF void YAC_CALL printMyVector(MyVector *_v) {
	yac_host->printf("printMyVector: (%f, %f, %f)\n", _v->x, _v->y, _v->z);
}

YF sF32 YAC_CALL Fun_ReturnF(sF32 _a, sF32 _b) {
	return _a*_a+_b*_b;
}

YF sSI YAC_CALL Fun_ReturnI(sSI _a, sSI _b) {
	return _a+_b;
}

YF sSI YAC_CALL Fun_ReturnI_CPP(void) {
	sSI r=1;
	sUI i;
	for(i=0; i<1000000; i++)
	{
		r=r+Fun_ReturnI(r, r);
		r=r+Fun_ReturnI(r, r);
		r=r+Fun_ReturnI(r, r);
		r=r+Fun_ReturnI(r, r);
		r=r+Fun_ReturnI(r, r);
		r=r+Fun_ReturnI(r, r);
		r=r+Fun_ReturnI(r, r);
		r=r+Fun_ReturnI(r, r);
		r=r+Fun_ReturnI(r, r);
		r=r+Fun_ReturnI(r, r);
	}
	return r;
}

YF sF32 YAC_CALL Fun_ReturnF_CPP(void) {
	sUI i;
	float f=1.1f;
	for(i=0; i<1000000; i++)
	{
		f=f+Fun_ReturnF(f, f);
		f=f+Fun_ReturnF(f, f);
		f=f+Fun_ReturnF(f, f);
		f=f+Fun_ReturnF(f, f);
		f=f+Fun_ReturnF(f, f);
		f=f+Fun_ReturnF(f, f);
		f=f+Fun_ReturnF(f, f);
		f=f+Fun_ReturnF(f, f);
		f=f+Fun_ReturnF(f, f);
		f=f+Fun_ReturnF(f, f);
	}
	return f;
}

// ----
// ---- if the string "YAC_Value *_r" appears within the declaration of an exported
// ---- function, the interface generator will interpret it as a "variable-return-type" function
// ----
YF void YAC_CALL Fun_ReturnNewMyClass(YAC_Value *_r);


YF void YAC_CALL _TestVarRetMangled(YAC_Object *_o, YAC_Value *_r);


/** Raise a segmentation fault on purpose.
 *
 *
 */
YF void YAC_CALL RaiseSegV(void);


// Raises InvalidPointer error/exception
YF void YAC_CALL Raise_InvalidPointer(void);


//
// Declare some functions that allow to deliver their return values in pre-allocated objects
// e.g. TestObjRet0() => myRetObj (statement, returns void)
//  or  TestObjRet0() <=> myRetObj (expression, returns myRetObj)
// Note: In a script, the functions are named just "TestObjRet0", "TestObjRet1" !
//
YF void YAC_CALL TestObjRet0_YAC_RARG(YAC_Object *_ret42);  // Returns "42" in "return argument"
YF void YAC_CALL TestObjRet1_YAC_RARG(int _i, YAC_Object *_ret); // Returns _i in "return argument"

//
// These functions are used when the TestObjRet*() functions are called without the =>, <=> operator.
// Note: The function signatures must be the same as the TestObjRet*_YAC_RARG functions except for the
//       last argument which is YAC_Value* instead of YAC_Object* !
//
YF void YAC_CALL TestObjRet0_YAC_RVAL(YAC_Value *_r); // Returns new Integer object (int. value= 42)
YF void YAC_CALL TestObjRet1_YAC_RVAL(int _i, YAC_Value *_r); // Returns new Integer object (int.value = _i)



#endif // __TEST_H__