// GLUT mouse motion handling

#include "motion.h"
#include "view.h"

// Apple's annoying non-standard GL include location
#if defined(__APPLE__) || defined(MACOSX)
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include <math.h>
#include <stdio.h>
#include <list>

using std::list;

// data on button press or drag
static struct PressData {
    enum Event {PRESS,DRAG} event; // was this a press or drag?
    int button;			// which mouse button was pressed?
    int x, y;			// where was our last position?

    PressData(Event _event, int _button, int _x, int _y) 
	: event(_event), button(_button), x(_x), y(_y) {}
	
} lastPress(PressData::PRESS,0,0,0);


// mouse drag delayed by latency
// use difference between last x,y and x,y to define a rotation
void drag(const PressData &pd)
{
    float dx = pd.x - lastPress.x; //record differences from last position
    float dy = pd.y - lastPress.y;

    lastPress.x = pd.x;	// update stored position
    lastPress.y = pd.y;

    if (lastPress.button == GLUT_LEFT_BUTTON) { // rotate
	// save current view state
	float matrix[16];
	glGetFloatv(GL_MODELVIEW_MATRIX, matrix);

	// From the starting state, rotate around axis perpendicular to
	// mouse motion, where the window x axis is aligned with the 3D
	// view x axis, while the window y axis is aligned with the 3D
	// view z axis. Find perpendicular using rule that (y,-x) is
	// perpendicular to (x,y). Rotation angle is scaled so 1/2
	// window width = about 90 degrees
	glLoadIdentity();
	glRotatef(180 * sqrt(dx*dx + dy*dy) / winWidth, dy,dx,0);
	glMultMatrixf(matrix);
    }
    else if (lastPress.button == GLUT_MIDDLE_BUTTON) { // zoom
	viewDistance -= dy;
	reshape(winWidth, winHeight);
    }
    else if (lastPress.button == GLUT_RIGHT_BUTTON) {
	// save current view state
	float matrix[16];
	glGetFloatv(GL_MODELVIEW_MATRIX, matrix);

	// apply translation to head of list
	glLoadIdentity();
	glTranslatef(dx,-dy,0);
	glMultMatrixf(matrix);
    }

    // tell GLUT that something has changed and we must redraw
    glutPostRedisplay();
}

// called when a mouse button is pressed -- schedule delayed callback
extern "C" void
mousePress(int b, int state, int x, int y)
{
    lastPress = PressData(PressData::PRESS,b,x,y);
}

// called when the mouse moves -- schedule delayed callback
extern "C" void
mouseDrag(int x, int y)
{
    drag(PressData(PressData::DRAG,0,x,y));
}

