/*
 *  Simple trackball-like motion adapted (ripped off) from projtex.c
 *  (written by David Yu and David Blythe).  See the SIGGRAPH '96
 *  Advanced OpenGL course notes.
 */


/* includes */
#include <math.h>
#include <assert.h>
#include <GL/glut.h>
#include "tb.h"
#include "trackball.h"

/* globals */
static GLuint    tb_lasttime;

float curquat[4];
float lastquat[4];
int beginx, beginy;

static GLuint    tb_width;
static GLuint    tb_height;

static GLint     tb_button = -1;
static GLboolean tb_tracking = GL_FALSE;
static GLboolean tb_animate = GL_TRUE;

void
tbStepAnimation(void)
{
  add_quats(lastquat, curquat, curquat);
}

void
_tbAnimate(void)
{
  tbStepAnimation();
  glutPostRedisplay();
}

static void
defaultAnimateFunc(int animate)
{
  if (animate) {
    glutIdleFunc(_tbAnimate);
  } else {
    glutIdleFunc(0);
  }
}

void (*animateFunc)(int animate) = defaultAnimateFunc;

void
_tbStartMotion(int x, int y, int time)
{
  assert(tb_button != -1);

  animateFunc(0);
  tb_tracking = GL_TRUE;
  tb_lasttime = time;
  beginx = x;
  beginy = y;
}

void
_tbStopMotion(unsigned time)
{
  assert(tb_button != -1);

  tb_tracking = GL_FALSE;

  if (time == tb_lasttime && tb_animate) {
    animateFunc(1);
  } else {
    if (tb_animate) {
      animateFunc(0);
    }
  }
}

void
tbAnimate(GLboolean animate)
{
  tb_animate = animate;
}

void
tbInit(GLuint button)
{
  tb_button = button;
  trackball(curquat, 0.0, 0.0, 0.0, 0.0);
}

void
tbMatrix(void)
{
  GLfloat m[4][4];

  assert(tb_button != -1);
  build_rotmatrix(m, curquat);
  glMultMatrixf(&m[0][0]);
}

void
tbReshape(int width, int height)
{
  assert(tb_button != -1);

  tb_width  = width;
  tb_height = height;
}

void
tbMouse(int button, int state, int x, int y)
{
  assert(tb_button != -1);

  if (state == GLUT_DOWN && button == tb_button)
    _tbStartMotion(x, y, glutGet(GLUT_ELAPSED_TIME));
  else if (state == GLUT_UP && button == tb_button)
    _tbStopMotion(glutGet(GLUT_ELAPSED_TIME));
}

void
tbMotion(int x, int y)
{
  if (tb_tracking) {
    trackball(lastquat,
      (2.0 * beginx - tb_width) / tb_width,
      (tb_height - 2.0 * beginy) / tb_height,
      (2.0 * x - tb_width) / tb_width,
      (tb_height - 2.0 * y) / tb_height
      );
    beginx = x;
    beginy = y;
    tb_animate = 1;
    tb_lasttime = glutGet(GLUT_ELAPSED_TIME);
    tbStepAnimation();
  }
}

void
tbAnimateFunc(void (*func)(int animate))
{
  animateFunc = func;
}
