/*
   RGB cube with ...
   
   X-rotations  :  q  w
   Y-rotations  :  a  s
   Z-rotations  :  z  x
   
   Sample for intro computer graphics course.  © 2000, Steve Cunningham
*/
#include "glut.h"
#include "mui.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

static char ch; // the character from the keyboard that controls the rotation
static GLfloat saveState[16] = {1.0,0.0,0.0,0.0,
                                0.0,1.0,0.0,0.0,
                                0.0,0.0,1.0,0.0,
                                0.0,0.0,0.0,1.0},
               viewProj[16]; // hold transforms

typedef GLfloat point3[3];

// boundaries of RGB cube in display
#define MINX  -4.0
#define MAXX   4.0
#define MINY  -4.0
#define MAXY   4.0
#define MINZ  -4.0
#define MAXZ   4.0

// define coefficients of YZ-plane
float AX=1.0, BX=0.0, CX=0.0, DX=0.0;
// define coefficients of XZ-plane
float AY=0.0, BY=1.0, CY=0.0, DY=0.0;
// define coefficients of XY-plane
float AZ=0.0, BZ=0.0, CZ=1.0, DZ=0.0;
// hold coordinates and colors of intersection point
float xx = 0.5, yy = 0.5, zz = 0.5;
float cx = 0.5, cy = 0.5, cz = 0.5;
float rr = 0.5, gg = 0.5, bb = 0.5;
int sphereControl = 1;

muiObject *Rslider, *Gslider, *Bslider;
muiObject *Rlabel, *Glabel, *Blabel;
muiObject *noSphereB, *smallSphereB, *largeSphereB;
int muiWin, glWin;


void box(void);
float functionX(void);
float functionY(void);
float functionZ(void);
void myinit(void);
void surfaceX(void);
void surfaceY(void);
void surfaceZ(void);
void display(void);
void readButton( muiObject *, enum muiReturnValue );
void readSliders( muiObject *, enum muiReturnValue );
void reshape(int,int);
void keyboard(unsigned char, int, int);

void box(void) {
	glLineWidth(3.0);
	glColor3f(1.0,1.0,1.0);
	glBegin(GL_LINE_STRIP);
		glVertex3f(MINX,MINY,MINZ);
		glVertex3f(MINX,MINY,MAXZ);
		glVertex3f(MINX,MAXY,MAXZ);
		glVertex3f(MINX,MAXY,MINZ);
		glVertex3f(MINX,MINY,MINZ);
	glEnd();
	glBegin(GL_LINE_STRIP);
		glVertex3f(MAXX,MINY,MINZ);
		glVertex3f(MAXX,MINY,MAXZ);
		glVertex3f(MAXX,MAXY,MAXZ);
		glVertex3f(MAXX,MAXY,MINZ);
		glVertex3f(MAXX,MINY,MINZ);
	glEnd;
	glBegin(GL_LINE_STRIP);
		glVertex3f(MINX,MINY,MINZ);
		glVertex3f(MINX,MINY,MAXZ);
		glVertex3f(MAXX,MINY,MAXZ);
		glVertex3f(MAXX,MINY,MINZ);
		glVertex3f(MINX,MINY,MINZ);
	glEnd;
	glBegin(GL_LINE_STRIP);
		glVertex3f(MINX,MAXY,MINZ);
		glVertex3f(MINX,MAXY,MAXZ);
		glVertex3f(MAXX,MAXY,MAXZ);
		glVertex3f(MAXX,MAXY,MINZ);
		glVertex3f(MINX,MAXY,MINZ);
	glEnd;
	glBegin(GL_QUADS);	//MAJOR kluge -- whattheheck?
		glVertex3f(MINX,MAXY,MINZ);
		glVertex3f(MINX,MAXY,MINZ);
		glVertex3f(MINX,MAXY,MINZ);
		glVertex3f(MINX,MAXY,MINZ);
	glEnd();
}

float functionX(void)
{
	return -DX/AX;
}

float functionY(void)
{
	return -DY/BY;
}

float functionZ(void)
{
	return -DZ/CZ;
}
	
void myinit(void)
{
	glClearColor( 0.4, 0.4, 0.4, 1.0 );
	glEnable(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);
}

void surfaceX(void) {
	xx = functionX();
	cx = (xx+4.)/8.;
	glBegin(GL_QUADS);
		glColor3f (cx, 1., 1.);
		glVertex3f(xx, 4., 4.);
		glColor3f (cx, 1., 0.);
		glVertex3f(xx, 4.,-4.);
		glColor3f (cx, 0., 0.);
		glVertex3f(xx,-4.,-4.);
		glColor3f (cx, 0., 1.);
		glVertex3f(xx,-4., 4.);
	glEnd();
}

void surfaceY(void) {
	yy = functionY();
	cy = (yy+4.)/8.;
	glBegin(GL_QUADS);
		glColor3f ( 1.,cy, 1.);
		glVertex3f( 4.,yy, 4.);
		glColor3f ( 1.,cy, 0.);
		glVertex3f( 4.,yy,-4.);
		glColor3f ( 0.,cy, 0.);
		glVertex3f(-4.,yy,-4.);
		glColor3f ( 0.,cy, 1.);
		glVertex3f(-4.,yy, 4.);
	glEnd();
}

void surfaceZ(void) {
	zz = functionZ();
	cz = (zz+4.)/8.;
	glBegin(GL_QUADS);
		glColor3f ( 1., 1.,cz);
		glVertex3f( 4., 4.,zz);
		glColor3f ( 1., 0.,cz);
		glVertex3f( 4.,-4.,zz);
		glColor3f ( 0., 0.,cz);
		glVertex3f(-4.,-4.,zz);
		glColor3f ( 0., 1.,cz);
		glVertex3f(-4., 4.,zz);
	glEnd();
}

void display( void )
{
#define Angle 5.0
//  The GL_MODELVIEW_MATRIX starts out including the viewing transformation,
//  so we'll first take that off, then construct the modeling transformation,
//  then take *THAT* off, then put them back together.
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glGetFloatv( GL_MODELVIEW_MATRIX, viewProj );
    glLoadIdentity();
    
    switch(ch)
    {
        case 'q':
            glRotatef( Angle, 1.0, 0.0, 0.0); break;
        case 'w':
            glRotatef(-Angle, 1.0, 0.0, 0.0); break;
        case 'a':
            glRotatef( Angle, 0.0, 1.0, 0.0); break;
        case 's':
            glRotatef(-Angle, 0.0, 1.0, 0.0); break;
        case 'z':
            glRotatef( Angle, 0.0, 0.0, 1.0); break;
        case 'x':
            glRotatef(-Angle, 0.0, 0.0, 1.0); break;
    }
    ch = ' ';
    
    //  NOW we apply the rest of the modeling transformation by POST-multiplying
    //  by the saved matrix, and then save that and put the identity back on the
    //  stack.  *whew*
    glMultMatrixf( saveState );
    glGetFloatv( GL_MODELVIEW_MATRIX, saveState );
    glLoadIdentity();
    
    //  And finally rebuild the overall modelview matrix by multiplying by the
    //  viewing transformation and then by the modeling transformation
    glMultMatrixf( viewProj );
    glMultMatrixf( saveState );

    box();
    surfaceX();
    surfaceY();
    surfaceZ();
    glColor3f(cx,cy,cz);
    glPushMatrix();
    glTranslatef(xx,yy,zz);
    if (sphereControl != 0) {
    	if (sphereControl == 1)
    		glScalef(0.3, 0.3, 0.3);
    	if (sphereControl == 2)
    		glScalef(0.6, 0.6, 0.6);
    glutSolidSphere(1.,12.,12.);
    }
    glPopMatrix();

    //  And put ourselves back into the original GL_MODELVIEW_MATRIX by popping
    //  off the new work.  The stack again has one thing on it, and that is the
    //  viewing transformation.
    glPopMatrix();
    glutSwapBuffers();
    
    glutSetWindow(muiWin);
    glutSwapBuffers();
}

void readButton( muiObject *obj, enum muiReturnValue rv )
{
	if ( obj == noSphereB )
		sphereControl = 0;
	if ( obj == smallSphereB )
		sphereControl = 1;
	if (obj == largeSphereB )
		sphereControl = 2;
  glutSetWindow( glWin );
  glutPostRedisplay();
}

void readSliders ( muiObject *obj, enum muiReturnValue rv )
{
	char rs[32], gs[32], bs[32];
	
	glutPostRedisplay();

	rr = muiGetHSVal( Rslider );
	gg = muiGetHSVal( Gslider );
	bb = muiGetHSVal( Bslider );
	
	sprintf(rs,"%6.2f",rr);
	muiChangeLabel( Rlabel, rs ) ;
	sprintf(gs,"%6.2f",gg);
	muiChangeLabel( Glabel, gs ) ;
	sprintf(bs,"%6.2f",bb);
	muiChangeLabel( Blabel, bs ) ;
	
	DX = -4.0 + rr*8.0;
	DY = -4.0 + gg*8.0;
	DZ = -4.0 + bb*8.0;
	
	glutSetWindow( glWin );
	glutPostRedisplay();
}

void reshape(int w,int h)
{
	glViewport(0,0,(GLsizei)w,(GLsizei)h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0,1.0,1.0,40.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt( 10.0,  10.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
}

void keyboard(unsigned char key, int x, int y)
{
	ch = ' ';
	switch (key)
	{
        case 'q' :    // rotate around X; q = positive, w = negative
        case 'w' :
        case 'a' :    // rotate around Y; a = positive, s = negative
        case 's' :
        case 'z' :    // rotate around Z; z = positive, x = negative
        case 'x' :
           ch = key; break;
        case 'o':
        	if (DX < 4. ) DX += 0.2; break;
        case 'p':
        	if (DX > -4.) DX -= 0.2; break;
        case 'k':
        	if (DY < 4. ) DY += 0.2; break;
        case 'l':
        	if (DY > -4.) DY -= 0.2; break;
        case 'm':
        	if (DZ < 4. ) DZ += 0.2; break;
        case ',':
        	if (DZ > -4.) DZ -= 0.2; break;
	}
	glutPostRedisplay(); /* perform display again */
}

void main(int argc, char** argv)
{
	char rs[32], gs[32], bs[32];

//	Create MUI control window and its callbacks
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowSize(270,350);
	glutInitWindowPosition(600,70); /* window near top left of display */
	muiWin = glutCreateWindow("Control Panel"); /* window title */
	glutSetWindow( muiWin );
	muiInit();
	muiNewUIList(1);
	muiSetActiveUIList( 1 );

//	Define color control sliders and add to MUI list 1
	muiNewLabel(90, 330, "Color controls");

	muiNewLabel(5, 310, "Red");
	sprintf(rs,"%6.2f",rr);
	Rlabel = muiNewLabel(35, 310, rs);
	Rslider = muiNewHSlider(5, 280, 265, 130, 10);
	muiSetCallback( Rslider, readSliders );

	muiNewLabel(5, 255, "Green");
	sprintf(gs,"%6.2f",gg);
	Glabel = muiNewLabel(35, 255, gs);
	Gslider = muiNewHSlider(5, 225, 265, 130, 10);
	muiSetCallback( Gslider, readSliders );

	muiNewLabel(5, 205, "Blue");
	sprintf(bs,"%6.2f",bb);
	Blabel = muiNewLabel(35, 205, bs);
	Bslider = muiNewHSlider(5, 175, 265, 130, 10);
	muiSetCallback( Bslider, readSliders );

	muiNewLabel( 100, 150, "Sphere size");
	noSphereB    = muiNewRadioButton ( 10, 110 ) ;
	smallSphereB = muiNewRadioButton ( 100, 110 ) ;
	largeSphereB = muiNewRadioButton ( 190, 110 ) ;
	muiLinkButtons ( noSphereB, smallSphereB ) ;
	muiLinkButtons ( smallSphereB, largeSphereB ) ;

	muiLoadButton( noSphereB,    "None" );
	muiLoadButton( smallSphereB, "Small" );
	muiLoadButton( largeSphereB, "Large");

	muiSetCallback( noSphereB,    readButton );
	muiSetCallback( smallSphereB, readButton );
	muiSetCallback( largeSphereB, readButton );
	muiClearRadio( noSphereB );
	
	muiAddToUIList( 1, Rslider );
	muiAddToUIList( 1, Gslider );
	muiAddToUIList( 1, Bslider );
	muiAddToUIList( 1, noSphereB );
	muiAddToUIList( 1, smallSphereB );
	muiAddToUIList( 1, largeSphereB );

//	Create display window and its callbacks
	glutInit(&argc,argv);
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(500,500);
	glutInitWindowPosition(70,70);
	glWin = glutCreateWindow("Interactive color picker in RGB cube");
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);

	myinit();
	glutMainLoop();
}