#ifndef __OGL2_CAMERA__
#define __OGL2_CAMERA__

#include "types.h"
#include "entity.h"
#include "dynamic.h"
#include "switchers.h"

#ifndef PI
#define PI 3.14159265358979324
#endif

#include <iostream>

extern "C++" {

class Observer : public Entity {
public:
  virtual void GL (double time = 0.0) = 0;
  virtual GLmatrix ModelviewMatrix (double time = 0.0) = 0;
  virtual ~Observer () {}
};

template class EntityList<Observer>;
typedef EntityList<Observer> Observers;

class Camera : public Observer
{
protected:
  GLfloat horizontal_fov, vertical_fov; 
  GLfloat near_clipplane;
  GLfloat far_clipplane;

public:
  Camera () : horizontal_fov(PI/2), vertical_fov(PI/2),
              near_clipplane(1.0), far_clipplane(1000.0) {}

  void HorizontalFOV (GLfloat hf) {
    horizontal_fov = hf;
  }
  void VerticalFOV (GLfloat vf) {
    vertical_fov = vf;
  }
  void NearClipplane (GLfloat n) {
    near_clipplane = n;
  }
  void FarClipplane (GLfloat f) {
    far_clipplane = f;
  }
  void DepthRange (GLfloat n, GLfloat f) {
    near_clipplane = n;
    far_clipplane = f;
  }

  GLfloat HorizontalFOV () {
    return horizontal_fov;
  }
  GLfloat VerticalFOV () {
    return vertical_fov;
  }
  GLfloat NearClipplane () {
    return near_clipplane;
  }
  GLfloat FarClipplane () {
    return far_clipplane;
  }
};

template class EntityList<Camera>;
typedef EntityList<Camera> Cameras;

class CameraTargetGL : public Camera {
  DynamicSwitch<Vector3f, GLfloat> origin, target;
  DynamicSwitch<GLfloat, GLfloat> roll;

  GLfloat M[16];
public:
  CameraTargetGL () {
    origin(Vector3f(0.0F, 0.0F, 0.0F));
    target(Vector3f(0.0F, 0.0F, 1.0F));
    roll(0.0F);
  }

  void GL (double time = 0.0);
  GLmatrix ModelviewMatrix (double time = 0.0) {
    return M;
  }

  void Origin (GLfloat x, GLfloat y, GLfloat z),
       Origin (const Vector3f& V),
       Origin (Dynamic<Vector3f, GLfloat> *ptr, bool strong = false);
  void Target (GLfloat x, GLfloat y, GLfloat z),
       Target (const Vector3f& V),
       Target (Dynamic<Vector3f, GLfloat> *ptr, bool strong = false);
  void Roll (GLfloat r),
       Roll (Dynamic<GLfloat, GLfloat> *ptr, bool strong = false);

  Vector3f Origin (double time = 0.0),
           Target (double time = 0.0);
};

class CameraTarget3DS : public Camera {
  DynamicSwitch<Vector3f, GLfloat> origin, target;
  DynamicSwitch<GLfloat, GLfloat> roll;

  GLfloat M[16];
public:
  CameraTarget3DS () {
    origin(Vector3f(0.0F, 0.0F, 0.0F));
    target(Vector3f(0.0F, 0.0F, 1.0F));
    roll(0.0F);
  }

  void GL (double time = 0.0);
  GLmatrix ModelviewMatrix (double time = 0.0) {
    return M;
  }

  void Origin (GLfloat x, GLfloat y, GLfloat z),
       Origin (const Vector3f& V),
       Origin (Dynamic<Vector3f, GLfloat> *ptr, bool strong = false);
  void Target (GLfloat x, GLfloat y, GLfloat z),
       Target (const Vector3f& V),
       Target (Dynamic<Vector3f, GLfloat> *ptr, bool strong = false);
  void Roll (GLfloat r),
       Roll (Dynamic<GLfloat, GLfloat> *ptr, bool strong = false);

  Vector3f Origin (double time = 0.0),
           Target (double time = 0.0);
};

class CameraDescent3DS : public Camera
{

  Vector3f O;
  Vector3f I, J, K; // coordinate system for descent alike camera
                        // I - to the right
                        // J - to the top
                        // K - to the front
  GLfloat Vroll,Uroll,Nroll; // rolls for descent camera

  void rotate(Vector3f& U, GLfloat Nroll, GLfloat Vroll, GLfloat Uroll);
  void uprav(Vector3f& U, Vector3f& I, Vector3f& J, Vector3f& K);

  GLfloat M[16];
public:
  CameraDescent3DS() {
    O.x = 0.0; O.y = 0.0; O.z = 0.0;
    J.x = 1.0; J.y = 0.0; J.z = 0.0;
    I.x = 0.0; I.y = 1.0; I.z = 0.0;
    K.x = 0.0; K.y = 0.0; K.z = 1.0;
    Nroll = 0.0; Uroll = 0.0; Vroll = 0.0;
  }
  void GL (double time = 0.0);
  void GLmirror (double a, double b, double c, double d, double time = 0.0);
  GLmatrix ModelviewMatrix (double time = 0.0) {
    return M;
  }

  GLmatrix infoGL(double time = 0.0);
  void Info()
   {
   cout << "Descent origin       " << O << endl;
   cout << "Descent right        " << I << endl;
   cout << "Descent up           " << J << endl;
   cout << "Descent forward      " << K << endl;
   }

  void I3f(GLfloat x, GLfloat y, GLfloat z) {
    I.x = x; I.y = y; I.z = z;
  }
  void I3fv(GLfloat* v) {
    I.x = *v; I.y = *(v+1); I.z = *(v+2);
  }
  void J3f(GLfloat x, GLfloat y, GLfloat z) {
    J.x = x; J.y = y; J.z = z;
  }
  void J3fv(GLfloat* v) {
    J.x = *v; J.y = *(v+1); J.z = *(v+2);
  }
  void K3f(GLfloat x, GLfloat y, GLfloat z) {
    K.x = x; K.y = y; K.z = z;
  }
  void K3fv(GLfloat* v) {
    K.x = *v; K.y = *(v+1); K.z = *(v+2);
  }
  void IJKmove(GLfloat a, GLfloat b, GLfloat c) {
    O.x+= a*I.x + b*J.x + c*K.x;
    O.y+= a*I.y + b*J.y + c*K.y;
    O.z+= a*I.z + b*J.z + c*K.z;
  }

  void URoll(GLfloat ur) { Uroll = ur; }
  void VRoll(GLfloat vr) { Vroll = vr; }
  void NRoll(GLfloat nr) { Nroll = nr; }

  void SetAll(const Vector3f& o,const Vector3f& i,const Vector3f& j,const Vector3f& k) {
    O=o;
    I=i;
    J=j;
    K=k;
  }

  void Origin (const Vector3f& o) {
   O=o;
  }


  Vector3f Origin () {
    return O;
  }
  Vector3f IVector () {
    return I;
  }
  Vector3f JVector () {
    return J;
  }
  Vector3f KVector () {
    return K;
  }


};

} // extern "C++"

#endif
