/// tkmath.cpp
///
/// (c) 2008 by Carsten Busse <carsten.busse@googlemail.com>,
///             Bastian Spiegel <bs@tkscript.de>
///     - Distributed under terms of the Lesser GNU General Public License (LGPL).
///       See COPYING and <http://www.gnu.org/licenses/licenses.html#LGPL> for further information.
///
///

#include <math.h>

#include "tkmath_yac.h"

#ifndef DX_STATIC_TKMATH
#include <yac_host.cpp>
#endif

#include "tkmath.h"

#include "commonmath.h"

#include "matrix.h"
#include "vector2f.h"
#include "matrix2f.h"
#include "complexf.h"
#include "vector3f.h"
#include "matrix3f.h"
#include "vector4f.h"
#include "matrix4f.h"
#include "quaternionf.h"

#include "complexd.h"
#include "vector2d.h"
#include "matrix2d.h"
#include "vector3d.h"
#include "matrix3d.h"
#include "vector4d.h"
#include "matrix4d.h"
#include "quaterniond.h"

#ifndef DX_STATIC_TKMATH
YAC_Host *yac_host;
#endif

// Implement "standard" exid_* variables (e.g. exid_InvalidPointer)
Dyac_std_exid_impl;

// Implement tkmath specific exid_* variables
Dyac_exid_impl(TkMathError);
Dyac_exid_impl(TkMathDivisionByZero);
Dyac_exid_impl(TkMathInvalidValue);
Dyac_exid_impl(TkMathUnsupportedOperator);


#include "ying_Math.h"

#include "ying_Math.cpp"
#include "ying_Math_Math.cpp"
#include "ying_Math_Matrix.cpp"
#include "ying_Math_Vector2f.cpp"
#include "ying_Math_Matrix2f.cpp"
#include "ying_Math_Complexf.cpp"
#include "ying_Math_Vector3f.cpp"
#include "ying_Math_Matrix3f.cpp"
#include "ying_Math_Vector4f.cpp"
#include "ying_Math_Matrix4f.cpp"
#include "ying_Math_Quaternionf.cpp"
#include "ying_Math_Complexd.cpp"
#include "ying_Math_Vector2d.cpp"
#include "ying_Math_Matrix2d.cpp"
#include "ying_Math_Vector3d.cpp"
#include "ying_Math_Matrix3d.cpp"
#include "ying_Math_Vector4d.cpp"
#include "ying_Math_Matrix4d.cpp"
#include "ying_Math_Quaterniond.cpp"

/*sSI epsilon_flt_units=10;
sS64 epsilon_dbl_units=100;

typedef
union _yacmem64 {
   sUI                ui4;
   sSI                si4;
   sU64               ui8;
   sS64               si8;
   sF32                f4;
   sF64                f8;
   YAC_Object         *o;
   YAC_Object        **io;
   YAC_String         *s;
   void               *any;
} yacmem64;*/

/*sBool Dfltequal(sF32 a, sF32 b) {
   yacmem _a, _b, _c;
   _a.f4=a;
   _b.f4=b;
   if ((a==0.0f)||(b==0.0f)) {
      _c.f4=FLT_EPSILON;
   } else {
      _c.si=epsilon_flt_units;
   }
   //yac_host->printf("a:%08x b:%08x\n",_a.u,_b.u);
   //_a=ADJUST32(_a);
   if (_a.si<0) _a.ui=0x80000000-_a.ui;
   if (_b.si<0) _b.ui=0x80000000-_b.ui;
   //yac_host->printf("Dfltequal %d units diff a=%f b=%f\n",abs(_a.i-_b.i),a,b);
   if (abs(_a.si-_b.si)<=_c.si) return true;
   else return false;
}

sBool Dfltnotequal(sF32 a, sF32 b) {
   yacmem _a, _b, _c;
   _a.f4=a;
   _b.f4=b;
   if ((a==0.0f)||(b==0.0f)) {
      _c.f4=FLT_EPSILON;
   } else {
      _c.si=epsilon_flt_units;
   }
   //yac_host->printf("a:%08x b:%08x c:%08x\n",_a.u,_b.u,_c.u);
   yac_host->printf("\na:%f/%a b:%f/%a _a:%f _b:%f\n",_a.f4,_a.f4, _b.f4, _b.f4, a,b);
   yac_host->printf("a hex=%08x b hex= %08x\n",_a.ui,_b.ui);
   if (_a.si<0) _a.ui=0x80000000-_a.ui;
   if (_b.si<0) _b.ui=0x80000000-_b.ui;
   yac_host->printf("a:%f/%a b:%f/%a _a:%f _b:%f\n",_a.f4,_a.f4, _b.f4, _b.f4, a,b);
   yac_host->printf("Ddblnotequal %d units diff\n",abs(_a.si-_b.si));
   //yac_host->printf("Dfltnotequal %d units diff a=%f b=%f\n",abs(_a.i-_b.i),a,b);
   if (abs(_a.si-_b.si)>_c.si) return true;
   else return false;
}

sBool Ddblequal(sF64 a, sF64 b) {
   yacmem64 _a, _b, _c;
   _a.f8=a;
   _b.f8=b;
   if ((a==0.0)||(b==0.0)) {
      _c.f8=DBL_EPSILON;
   } else {
      _c.si8=epsilon_dbl_units;
   }
   if (_a.si8<0) _a.ui8=0x8000000000000000-_a.ui8;
   if (_b.si8<0) _b.ui8=0x8000000000000000-_b.ui8;
   //yac_host->printf("Ddblequal %lld units diff\n",llabs(_a.i-_b.i));
   if (llabs(_a.si8-_b.si8)<=_c.si8) return true;
   else return false;
}

sBool Ddblnotequal(sF64 a, sF64 b) {
   yacmem64 _a, _b, _c;
   _a.f8=a;
   _b.f8=b;
   if ((a==0.0)||(b==0.0)) {
      _c.f8=DBL_EPSILON;
   } else {
      _c.si8=epsilon_dbl_units;
   }
   if (_a.si8<0) _a.ui8=0x8000000000000000-_a.ui8;
   if (_b.si8<0) _b.ui8=0x8000000000000000-_b.ui8;
   //yac_host->printf("\na:%lf/%la b:%lf/%la _a:%lf _b:%lf\n",_a.f8,_a.f8, _b.f8, _b.f8, a,b);
   //yac_host->printf("Ddblnotequal %lld units diff\n",llabs(_a.si8-_b.si8));

   if (llabs(_a.si8-_b.si8)>_c.si8) return true;
   else return false;
}*/

#ifndef HAVE_HYPOTF
// from gcc/libstdc++-v3/src/maths_stubs_float.cc
float hypotf_emul(float x, float y)
{
   float s = fabsf(x) + fabsf(y);
   if (s == 0.0F)
      return s;
   x /= s; y /= s;
   return s * sqrtf(x * x + y * y);
}
#endif // HAVE_HYPOTF



#ifdef DX_STATIC_TKMATH
void YAC_CALL YAC_Init_tkmath(YAC_Host *_host) {
#else
void YAC_CALL YAC_Init(YAC_Host *_host) {
#endif
   if (_host->yacGetDebugLevel())
   {
      _host->printf("[dbg] tkmath::YAC_Init called\n");
   }
   yac_host = _host;
   YAC_Init_Math(_host);
   
   // Fix Matrix inheritance
   yac_host->cpp_typecast_map[clid_Matrix4f][clid_Matrix] = 1;

   // Resolve "standard" exception ids
   Dyac_std_exid_resolve;

   // Register tkmath specific exception ids
   Dyac_exid_register(TkMathError, CriticalError);
   Dyac_exid_register(TkMathDivisionByZero, TkMathError);
   Dyac_exid_register(TkMathInvalidValue, TkMathError);
   Dyac_exid_register(TkMathUnsupportedOperator, TkMathError);
   
   if (yac_host->yacGetDebugLevel())
   {
      yac_host->printf("[dbg] tkmath::YAC_Init finished\n");
   }
}

#ifdef DX_STATIC_TKMATH
void YAC_CALL YAC_Exit_tkmath(YAC_Host *_host) {
#else
void YAC_CALL YAC_Exit(YAC_Host *_host) {
#endif
   YAC_Exit_Math(_host);
   if (yac_host->yacGetDebugLevel()) 
   {
      yac_host->printf("[dbg] tkmath::YAC_Exit finished\n");
   }
}
