///////////////////////////////////////////////////
//
//	Contour-tree based isosurfaces
//	version 3.0
//	copyright Hamish Carr
//	February, 2001
//
//	------------------------
//	GLUTVertSlider.cpp
//	------------------------
//	
//	A vertical slider
//
//	Notes:
//	------
//
//
//	Change log:
//	-----------
//	Date:		Changes:
//	-----		--------
//	01/12/01	Initial version
//
///////////////////////////////////////////////////	

#include <stdlib.h>															//	needed for NULL
#include <stdio.h>															//	for sprintf()
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
#include "GLUTVertSlider.h"
#include "GLUTString.h"

#define VERT_SLIDER_INITIAL_SIZE 200
#define VERT_SLIDER_THUMB_OVERHANG 10
#define VERT_SLIDER_THUMB_HEIGHT (2*VERT_SLIDER_THUMB_OVERHANG)
#define VERT_SLIDER_THUMB_WIDTH 20
#define VERT_SLIDER_TICK_LENGTH 12
#define VERT_SLIDER_WIDTH 6

GLUTVertSlider::GLUTVertSlider(const char *Name, GLUTPane *Parent, float MinValue, float MaxValue, int NSteps, 
		float initialValue, float *Target, bool *Dirty)
	: GLUTPane(Name, Parent, VERT_SLIDER_THUMB_OVERHANG + VERT_SLIDER_WIDTH/2 + VERT_SLIDER_TICK_LENGTH,
					VERT_SLIDER_INITIAL_SIZE, 
		 			false, true)
	{ // constructor
	minValue = MinValue; maxValue = MaxValue; nSteps = NSteps;						//	copy the parameters
	float currentValue = initialValue;												//	compute the initial value
	if (currentValue < minValue) currentValue = minValue;								//	clamp at minimum
	if (currentValue > maxValue) currentValue = maxValue;								//	and at maximum

	delta = (maxValue - minValue) / nSteps;										//	compute the "size" of a step
	thumbLocation = int(0.5 + (currentValue - minValue) / delta);							//	compute thumb location
	if (thumbLocation == nSteps) currentValue = maxValue; 							//	avoid round-off error at top end
	else currentValue = minValue + thumbLocation * delta;								//	otherwise compute current value

	target = Target; if (target != NULL) *target = currentValue;							//	set the target
	dirty = Dirty; if (dirty != NULL) *dirty = true;										//	and the dirty flag
	} // constructor

GLUTVertSlider::~GLUTVertSlider()												//	destructor
	{ // destructor
	} // destructor

void GLUTVertSlider::Display()													//	display
	{ // Display()
	GLfloat sliderColour[3] = { 0.0, 0.0, 0.0 };										//	we want to draw in black

	glMatrixMode(GL_PROJECTION);											//	set the projection matrix
	glLoadIdentity();														//	revert to identity matrix
	glOrtho(left, left + width, bottom, bottom + height, -1.0, 1.0);							//	set projection to match viewport

	float scale = (height - VERT_SLIDER_THUMB_WIDTH) / (float) nSteps;

	float currentValue = *target; 												//	retrieve current value
	if (currentValue < minValue) currentValue = minValue;								//	and clamp
	if (currentValue > maxValue) currentValue = maxValue;

	int sliderBottom = bottom + VERT_SLIDER_THUMB_OVERHANG;//	compute bottom end of slider

	thumbLocation = sliderBottom + (int) (scale * (currentValue - minValue) / delta);							//	compute thumb location

	int sliderTop = bottom + height - VERT_SLIDER_THUMB_OVERHANG;

	//	compute position of vertical lines
	int sliderLeftLine = left + VERT_SLIDER_THUMB_OVERHANG - (VERT_SLIDER_WIDTH/2);
	int sliderRightLine = sliderLeftLine + VERT_SLIDER_WIDTH; 				

	int thumbBottom = thumbLocation - VERT_SLIDER_THUMB_OVERHANG;									//	compute location of bottom edge
	int thumbTop = thumbBottom + VERT_SLIDER_THUMB_HEIGHT;					//	and top edge 
	int thumbLeft = left + 1;
	int thumbRight = left + VERT_SLIDER_THUMB_WIDTH;
	
	// draw the underlying slider bar
	glColor3fv(sliderColour);													//	switch to box colour
	if (thumbBottom > sliderBottom)							//	if it doesn't overlap at bottom
		{ // no bottom overlap
		glBegin(GL_LINE_STRIP);												//	use a strip of lines
		glVertex2i(sliderRightLine, thumbBottom);	glVertex2i(sliderRightLine, sliderBottom);
		glVertex2i(sliderLeftLine, sliderBottom);	glVertex2i(sliderLeftLine, thumbBottom);
		glEnd();
		} // no bottom overlap
		
	if (thumbTop < sliderTop)					//	if it doesn't overlap at top
		{ // no top overlap
		glBegin(GL_LINE_STRIP);												//	use a strip of lines
		glVertex2i(sliderRightLine, thumbTop);		glVertex2i(sliderRightLine, sliderTop);	
		glVertex2i(sliderLeftLine, sliderTop);		glVertex2i(sliderLeftLine, thumbTop);
		glEnd();
		} // no top overlap

	// draw the thumb box
	glBegin(GL_LINE_LOOP);													//	use a strip of lines
	glVertex2i(thumbLeft, thumbBottom);		glVertex2i(thumbRight, thumbBottom);
	glVertex2i(thumbRight, thumbTop);		glVertex2i(thumbLeft, thumbTop);
	glEnd();

	// draw the grooves on the thumb
	glBegin(GL_LINES);
	glVertex2i(thumbLeft + 4, thumbBottom + 6);		glVertex2i(thumbRight - 4, thumbBottom + 6);
	glVertex2i(thumbLeft + 4, thumbBottom + 10);	glVertex2i(thumbRight - 4, thumbBottom + 10);
	glVertex2i(thumbLeft + 4, thumbBottom + 14);	glVertex2i(thumbRight - 4, thumbBottom + 14);
	glEnd();
	
	// draw the ticks on the bar
	int tickLeft = sliderRightLine + 2;
	int tickRight = left + width;
	glBegin(GL_LINES);
	for (int i = 0; i <= 10; i++)
		{ // loop through ticks
		int tickLocation = sliderBottom + (sliderTop - sliderBottom) * ((float) i / (float) 10);
		if ((tickLocation >= thumbBottom - 1) && (tickLocation <= thumbTop + 1))
			continue;
		glVertex2i(tickLeft, tickLocation);
		glVertex2i(tickRight, tickLocation);
		} // loop through ticks
	glEnd();
	} // Display()

void GLUTVertSlider::Mouse(int button, int state, int x, int y)								//	mouse down / up routine 
	{ // Mouse()
	if (state == GLUT_DOWN)													//	if anywhere in the pane
		{ // mouse-down 
		AcquireDragFocus(this);												//	acquire focus
		Motion(x, y);														//	snap to it
		} // mouse-down 
	else if ((state == GLUT_UP) && (hasDragFocus))									//	if we were dragging a checkmark, and it's mouse-up
		{ // mouse-up when dragging checkmark
		ReleaseDragFocus();													//	release drag focus
		} // mouse-up when dragging checkmark
	} // Mouse()
	
void GLUTVertSlider::Motion(int x, int y)											//	mouse motion routine	
	{ // Motion()
	y = y - VERT_SLIDER_THUMB_OVERHANG;

	float scale = (height - 2 * VERT_SLIDER_THUMB_OVERHANG) / (float) nSteps;

	thumbLocation = y / scale;														//	reset the thumb location
	if (thumbLocation < 0) thumbLocation = 0;										//	clamp left
	if (thumbLocation > nSteps) thumbLocation = nSteps;								//	clamp right

	float currentValue;	
	if (thumbLocation == nSteps) currentValue = maxValue;								//	avoid round-off errors at top end
	else currentValue = minValue + (float)(delta * thumbLocation);
	
	if (target != NULL)														//	if there is a target
		*target = currentValue;												//	update the associated variable
	if (dirty != NULL)														//	if there is a dirty flag
		*dirty = true;														//	set it		
	glutPostRedisplay();														//	force a redisplay
	} // Motion()

void GLUTVertSlider::PrintType()												//	prints text string for type
	{ // PrintType()
	printf("GLUTVertSlider");
	} // PrintType()	
