///////////////////////////////////////////////////
//
//	Contour-tree based isosurfaces
//	version 3.0
//	copyright Hamish Carr
//	February, 2001
//
//	------------------------
//	GLUTHorizSlider.cpp
//	------------------------
//	
//	A horizontal slider
//
//	Notes:
//	------
//
//
//	Change log:
//	-----------
//	Date:		Changes:
//	-----		--------
//	02/09/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 "GLUTHorizSlider.h"
#include "GLUTString.h"

#define HORIZ_SLIDER_INITIAL_SIZE 200
#define HORIZ_SLIDER_THUMB_OVERHANG 10
#define HORIZ_SLIDER_THUMB_WIDTH (2*HORIZ_SLIDER_THUMB_OVERHANG)
#define HORIZ_SLIDER_THUMB_HEIGHT 20
#define HORIZ_SLIDER_TICK_LENGTH 12
#define HORIZ_SLIDER_WIDTH 6

GLUTHorizSlider::GLUTHorizSlider(const char *Name, GLUTPane *Parent, float MinValue, float MaxValue, int NSteps, 
		float initialValue, float *Target, bool *Dirty)
	: GLUTPane(Name, Parent, HORIZ_SLIDER_INITIAL_SIZE, 
		HORIZ_SLIDER_THUMB_OVERHANG + HORIZ_SLIDER_WIDTH/2 + HORIZ_SLIDER_TICK_LENGTH, 
		true, false)
	{ // 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

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

void GLUTHorizSlider::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 = (width - HORIZ_SLIDER_THUMB_WIDTH) / (float) nSteps;

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

	int sliderLeft = left + HORIZ_SLIDER_THUMB_OVERHANG;		//	compute left end of slider

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

	int sliderRight = left + width - HORIZ_SLIDER_THUMB_OVERHANG;		//	and right end

	//	compute position of horizontal lines
	int sliderBottomLine = bottom + HORIZ_SLIDER_TICK_LENGTH;
	int sliderTopLine = sliderBottomLine + HORIZ_SLIDER_WIDTH; 				

	int thumbLeft = thumbLocation - HORIZ_SLIDER_THUMB_OVERHANG;										//	compute location of left-hand edge
	int thumbRight = thumbLeft + HORIZ_SLIDER_THUMB_WIDTH;							//	and right-hand edge 
	int thumbBottom = bottom + HORIZ_SLIDER_TICK_LENGTH - HORIZ_SLIDER_THUMB_OVERHANG + HORIZ_SLIDER_WIDTH/2;
	int thumbTop = bottom + height -1;

	glColor3fv(sliderColour);												//	switch to box colour
	if (thumbLeft > sliderLeft)								//	if it doesn't overlap at left
		{ // no left overlap
		glBegin(GL_LINE_LOOP);												//	use a strip of lines
		glVertex2i(thumbLeft, sliderTopLine);		glVertex2i(sliderLeft, sliderTopLine);
		glVertex2i(sliderLeft, sliderBottomLine);	glVertex2i(thumbLeft, sliderBottomLine);
		glEnd();
		} // no left overlap
		
	if (thumbRight < sliderRight)						//	if it doesn't overlap at right
		{ // no right overlap
		glBegin(GL_LINE_LOOP);												//	use a strip of lines
		glVertex2i(thumbRight, sliderTopLine); 		glVertex2i(sliderRight, sliderTopLine);	
		glVertex2i(sliderRight, sliderBottomLine);	glVertex2i(thumbRight, sliderBottomLine);
		glEnd();
		} // no right overlap

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

	glBegin(GL_LINES);
	glVertex2i(thumbLeft + 6, thumbBottom + 4);		glVertex2i(thumbLeft + 6, thumbTop - 2);
	glVertex2i(thumbLeft + 10, thumbBottom + 4);	glVertex2i(thumbLeft + 10, thumbTop - 2);
	glVertex2i(thumbLeft + 14, thumbBottom + 4);	glVertex2i(thumbLeft + 14, thumbTop - 2);
	glEnd();
	
	int tickBottom = bottom;
	int tickTop = sliderBottomLine - 2;
	glBegin(GL_LINES);
	for (int i = 0; i <= 10; i++)
		{ // loop through ticks
		int tickLocation = sliderLeft + (sliderRight - sliderLeft) * ((float) i / (float) 10);
		if ((tickLocation >= thumbLeft - 1) && (tickLocation <= thumbRight + 1))
			continue;
		glVertex2i(tickLocation, tickBottom);
		glVertex2i(tickLocation, tickTop);
		} // loop through ticks
	glEnd();
	} // Display()

void GLUTHorizSlider::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 GLUTHorizSlider::Motion(int x, int y)										//	mouse motion routine	
	{ // Motion()
	x = x - HORIZ_SLIDER_THUMB_OVERHANG;
	float scale = (width - 2 * HORIZ_SLIDER_THUMB_OVERHANG) / (float) nSteps;

	thumbLocation = x / 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 GLUTHorizSlider::PrintType()												//	prints text string for type
	{ // PrintType()
	printf("GLUTHorizSlider");
	} // PrintType()	
