#ifndef __OGL2_DYNAMIC__
#define __OGL2_DYNAMIC__

#include "types.h"
#include "miscmath.h"

extern "C++" {

template <class __dtype, class __type>
class Dynamic {
public:
  virtual ~Dynamic () {}
  virtual __dtype operator () (__type t=0) = 0;
};

template class Dynamic<Vector3f, GLfloat>;
typedef Dynamic<Vector3f, GLfloat> Dvector3f;

template <class __vtype, class __type>
class DvectorStatic : public Dynamic<__vtype, __type> {
  __vtype v;
public:
  void Set (const __vtype& a) {
    v = a;
  }
  __vtype operator () (__type t=0) {
    return v;
  }
};

template <class __vtype, class __type>
class DvectorLinear : public Dynamic<__vtype, __type> {
  __vtype a0, a1;
public:
  void From (const __vtype& a) {
    a0 = a;
  }
  void To (const __vtype& a) {
    a1 = a;
  }
  __vtype operator () (__type t=0) {
    return (a0 + t*(a1-a0));
  }
};

template <class __vtype, class __type>
class DvectorLinearFramed : public Dynamic<__vtype, __type> {
  __vtype *a;
  int frames; 
  int allframes; 
  int fbegin; 
  int fend; 
public:
  DvectorLinearFramed () {}
  ~DvectorLinearFramed () {
    delete[] a;
  }
  void Frames (int all, int n, int first, int last, __vtype* p) {
    allframes = all;
    frames = n;
    fbegin = first;
    fend = last;
    a = p;
  }
  __vtype operator () (__type t=0) {
    __type frame = allframes*t;
    int frame_i = (int)frame;
    __type fract = frame - frame_i;
    if(frame_i<fbegin)
      return a[0];
    if(frame_i>=fend)
      return a[frames-1];
    frame_i -= fbegin;
    return (a[frame_i] + fract*(a[frame_i+1]-a[frame_i]));
  }
};


template <class __mtype, class __type>
class DmatrixBasis : public Dynamic<__mtype, __type> {
  __mtype M;
public:
  DmatrixBasis() {
    M.Identity();
  }
  void Load(__mtype &m) {
    M=m;
  }
  void Load(__type *m) {
    M.Load(m);
  }
  void X(__type x, __type y, __type z) {
    M[0] = x; M[1] = y; M[2] = z;
  }
  void Y(__type x, __type y, __type z) {
    M[4] = x; M[5] = y; M[6] = z;
  }
  void Z(__type x, __type y, __type z) {
    M[8] = x; M[9] = y; M[10] = z;
  }
  void Translation(__type x, __type y, __type z) {
    M[12] = x; M[13] = y; M[14] = z;
  }
  __mtype operator () (__type t=0) {
    return M;
  }
};

template <class __mtype, class __type>
inline void quat2matrix(quaternion<__type>& q, __mtype& M)
{
  __type w = re(q);
  __type x = im1(q);
  __type y = im2(q);
  __type z = im3(q);

  __type wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;

  x2 = x + x; y2 = y + y; 
  z2 = z + z;
  xx = x * x2;   xy = x * y2;   xz = x * z2;
  yy = y * y2;   yz = y * z2;   zz = z * z2;
  wx = w * x2;   wy = w * y2;   wz = w * z2;

  M[0] = 1.0 - (yy + zz); M[4] = xy - wz;         M[8] = xz + wy;
  M[1] = xy + wz;         M[5] = 1.0 - (xx + zz); M[9] = yz - wx;
  M[2] = xz - wy;         M[6] = yz + wx;         M[10] = 1.0 - (xx + yy);
}

template <class __mtype, class __type>
class DmatrixOrientation : public Dynamic<__mtype, __type>
{
  quaternion<__type> q;
  Vector3<__type> a;
  Vector3<__type> pivot;
  __mtype M;

public:
  DmatrixOrientation () {
    M.Identity();
    q = quaternion<__type> (0,0,0,0);
  }
  void Orientation (__type angle, __type x, __type y, __type z) {
    q = AngleAxis2Quaternion(angle, x, y, z);
  }
  void Translation (__type x, __type y, __type z) {
    a = Vector3<__type> (x, y, z);
  }
  void Translation (const Vector3<__type>& T) {
    a = t;
  }
  void Pivot (__type x, __type y, __type z) {
    pivot = Vector3<__type> (x, y, z);
  }
  void Pivot (const Vector3<__type>& T) {
    pivot = T;
  }
  __mtype operator () (__type t=0) {
    __mtype mat;
    mat.Identity();
    mat.Translate(pivot.x, pivot.y, pivot.z);
    quat2matrix(q, M);
    M[12] = a.x; M[13] = a.y; M[14] = a.z;
    mat = mat*M;
    mat.Translate(-pivot.x, -pivot.y, -pivot.z);
    return mat;
  }
};

template <class __mtype, class __type>
class DmatrixOrientationLinear : public Dynamic<__mtype, __type> {
  quaternion<__type> q0, q1;
  Vector3<__type> a0, a1, pivot;
  __mtype M;

public:
  DmatrixOrientationLinear () {
    M.Identity();
    q0 = quaternion<__type> (0,0,0,0);
    q1 = quaternion<__type> (0,0,0,0);
  }
  void StartRot(__type angle, __type x, __type y, __type z) {
    q0 = AngleAxis2Quaternion(angle, x, y, z);
  }
  void EndRot(__type angle, __type x, __type y, __type z) {
    q1 = AngleAxis2Quaternion(angle, x, y, z);
  }
  void StartTranslation(__type x, __type y, __type z) {
    a0 = Vector3<__type> (x, y, z);
  }
  void EndTranslation(__type x, __type y, __type z) {
    a1 = Vector3<__type> (x, y, z);
  }
  void Pivot (__type x, __type y, __type z) {
    pivot = Vector3<__type> (x, y, z);
  }
  void Pivot (const Vector3<__type>& T) {
    pivot = T;
  }
  __mtype operator () (__type t=0) {
    __mtype mat;
    mat.Identity();
    mat.Translate(pivot.x, pivot.y, pivot.z);
    quaternion<__type> q = slerp(t, q0, q1);
    quat2matrix(q, M);
    M[12] = a0.x + (a1.x-a0.x)*t;
    M[13] = a0.y + (a1.y-a0.y)*t;
    M[14] = a0.z + (a1.z-a0.z)*t;
    mat = mat*M;
    mat.Translate(-pivot.x, -pivot.y, -pivot.z);
    return mat;
  }
};

/*
 * this one is still questionable
 */

template <class __mtype, class __type>
class DmatrixAnimation : public Dynamic<__mtype, __type> {
  int allframes;

  int pframes, pfbegin, pfend;
  Vector3<__type>* positions;

  int rframes, rfbegin, rfend;
  quaternion<__type>* rotations;

  __mtype M;
public:
  DmatrixAnimation () :
    positions(0), rotations(0) {}
  ~DmatrixAnimation () {
    delete[] positions;
    delete[] rotations;
  }
  void Positions (int all, int n, int first, int last, Vector3<__type>* p) {
    allframes = all;
    pframes = n;
    pfbegin = first;
    pfend = last;
    positions = p;
  }
  void Rotations (int all, int n, int first, int last, quaternion<__type>* p) {
    allframes = all;
    rframes = n;
    rfbegin = first;
    rfend = last;
    rotations = p;
  }

  __mtype operator () (__type t=0) {
    M.LoadIdentity();
    if(rotations) {
      __type f = rframes*t;
      int frame = (int)f;
      __type loc_t = f-frame;
      quaternion<__type> q = slerp(loc_t, rotations[frame], rotations[frame+1]);
      quat2matrix(q, M);
    }
    if(positions) {
      __type f = pframes*t;
      int frame = (int)f;
      __type loc_t = f-frame;
      Vector3<__type>& a0 = positions[frame];
      Vector3<__type>& a1 = positions[frame+1];
      Vector3<__type> v = a0 + loc_t*(a1-a0);
      M[12] = v.x;
      M[13] = v.y;
      M[14] = v.z;
    }
    return M;
  }
};


} // extern "C++"

#endif
