/*
  15-462 Computer Graphics I
  Lecture 6: Hierarchical Modeling
  [Angel, Ch. 8]

  Robot program
  Left button: increase angle
  Middle button: menu
  Right button: decrease angle

  Author: Edward Angel
  Modified: Frank Pfenning
*/

#include <stdlib.h>
#include <GL/glut.h>

/* some constants in the model */
#define BASE_HEIGHT 2.0
#define BASE_RADIUS 1.0
#define LOWER_ARM_HEIGHT 5.0
#define LOWER_ARM_WIDTH 0.5
#define UPPER_ARM_HEIGHT 5.0
#define UPPER_ARM_WIDTH 0.5

typedef float point[3];

static GLfloat theta[3] = {0.0, 0.0, 0.0};
static GLint axis = 0;
GLUquadricObj  *p; /* pointer to quadric object */

typedef enum { BASE, LOWER, UPPER, QUIT } MENUSELECTION;

/* Define the three parts */
void drawBase()
{
  glPushMatrix();

  /* rotate cylinder to align with y axis */
  glRotatef(-90.0, 1.0, 0.0, 0.0);

  /* cylinder aligned with z axis */
  /* render with 5 slices for base and 5 along length */
  gluCylinder(p, BASE_RADIUS, BASE_RADIUS, BASE_HEIGHT, 5, 5);
  glPopMatrix();
}

void drawUpperArm()
{
  glPushMatrix();
  glTranslatef(0.0, 0.5*UPPER_ARM_HEIGHT, 0.0);
  glScalef(UPPER_ARM_WIDTH, UPPER_ARM_HEIGHT, UPPER_ARM_WIDTH);
  glutWireCube(1.0);
  glPopMatrix();
}

void drawLowerArm()
{
  glPushMatrix();
  glTranslatef(0.0, 0.5*LOWER_ARM_HEIGHT, 0.0);
  glScalef(LOWER_ARM_WIDTH, LOWER_ARM_HEIGHT, LOWER_ARM_WIDTH);
  glutWireCube(1.0);
  glPopMatrix();
}

void drawRobot()
{
  glLoadIdentity();
  glColor3f(1.0, 0.0, 0.0);
  glRotatef(theta[0], 0.0, 1.0, 0.0);
  drawBase();
  glTranslatef(0.0, BASE_HEIGHT, 0.0);
  glRotatef(theta[1], 0.0, 0.0, 1.0);
  drawLowerArm();
  glTranslatef(0.0, LOWER_ARM_HEIGHT, 0.0);
  glRotatef(theta[2], 0.0, 0.0, 1.0);
  drawUpperArm();
}

void display(void)
{
  glClear(GL_COLOR_BUFFER_BIT);

  drawRobot();

  glFlush();
  glutSwapBuffers();
}

void mouse(int button, int state, int x, int y)
{
  /* left button increase joint angle, right button decreases it */
  if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) 
    {
      theta[axis] += 5.0;
      if (theta[axis] > 360.0)  theta[axis] -= 360.0;
    }
  if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) 
    {
      theta[axis] -= 5.0;
      if (theta[axis] < 360.0)  theta[axis] += 360.0;
    }
  glutPostRedisplay();
}

void menu(int cmd)
{
  /* menu selects which angle to change or whether to quit */
  switch (cmd)
    {
    case BASE: axis = 0; break;
    case LOWER: axis = 1; break;
    case UPPER: axis = 2; break;
    case QUIT: exit(0); break;
    }
}

void reshape(int w, int h)
{
  GLfloat aspect = (GLfloat) w / (GLfloat) h;
  glViewport(0, 0, w, h);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if (w <= h)
    glOrtho(-10.0, 10.0, -5.0 * aspect, 15.0 * aspect, -10.0, 10.0);
  else
    glOrtho(-10.0 * aspect, 10.0 * aspect, -5.0, 15.0, -10.0, 10.0);
  glMatrixMode(GL_MODELVIEW);
}

void init()
{
  glClearColor(1.0, 1.0, 1.0, 1.0);
  
  p = gluNewQuadric();		/* allocate quadric object */
  gluQuadricDrawStyle(p, GLU_LINE); /* render it as wireframe */
}

int main(int argc, char **argv)
{
  /* create window */
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); /* no z-buffer - wire frame */
  glutInitWindowSize(500, 500);
  glutCreateWindow("Robot");

  /* set callbacks */
  glutReshapeFunc(reshape);
  glutDisplayFunc(display);
  glutMouseFunc(mouse);
  glutCreateMenu(menu);
  glutAddMenuEntry("Base", BASE);
  glutAddMenuEntry("Lower Arm", LOWER);
  glutAddMenuEntry("Upper Arm", UPPER);
  glutAddMenuEntry("Quit", QUIT);
  glutAttachMenu(GLUT_MIDDLE_BUTTON);

  /* initialize GL */
  init();

  glutMainLoop();
  return(0);
}
