/* four_windows.c  display four windows of data, control and reportwindow */

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

static int surfacew;        /* surface display window handle */
static int velocityw;       /* velocity vector display window handle */
static int pressurew;       /* pressure display window handle */
static int forcew;          /* force vector display window handle */
static int controlw;        /* control window handle */
static int reportw;         /* report window handle */
static float theta = 22.0;  /* object rotation about X axis */
static float phi   = 16.0;  /* object rotation about Z axis */
static float chi   = 0.0;   /* object rotation about Y axis */
static int width   = 300;   /* window width */
static int height  = 300;   /* window height */
static int cwidth  = 200;   /* control width */
static int grid_on  = 0;    /* display grid points */
static int cells_on = 0;    /* display cells */
static int vectors_on = 0;  /* display vectors */
static int xm;              /* mouse x */
static int ym;              /* mouse y corrected */
static int nx = 4;          /* grid sizes */
static int ny = 4;
static int nz = 4;
static double t = 0.0;      /* simulation time */
static double dt = 0.1;     /* simulation step time */

/* function prototypes */
static void draw_box(float x1, float x2, float y1, float y2, float z1, float z2);
static writeText(GLfloat x, GLfloat y, GLfloat z, GLfloat size, char * msg);
static void draw_grid(void);
static void draw_cells(void);
static void draw_vectors(void);
static void display_surface(void);
static void display_velocity(void);
static void display_pressure(void);
static void display_force(void);
static void display_control(void);
static void update_displays(void);   /* all */
static void myinit(void);
static void mouse(int btn, int state, int x, int y);
static void mouse_control(int btn, int state, int x, int y);
static int in_rect(int x, int y, int w, int h);
static void keys(unsigned char key, int x, int y);
static void reshape(int w, int h);
static void controlReshape(int w, int h);
    /* int  main(int argc, char * argv[]); */
static void finish(void);

static void draw_box(float x1, float x2, float y1, float y2, float z1, float z2)
{
  /* six faces: x1,y1,z1 x1,y1,z2 x2,y1,z2 x2,y1,z1  */
  /*            x1,y1,z1 x1,y2,z1 x2,y2,z1 x2,y1,z1  */
  /*            x1,y1,z1 x1,y2,z1 x1,y2,z2 x1,y1,z2  */
  /*            x2,y2,z2 x2,y2,z1 x1,y2,z1 x1,y2,z2  */
  /*            x2,y2,z2 x2,y1,z2 x1,y1,z2 x1,y2,z2  */
  /*            x2,y2,z2 x2,y1,z2 x2,y1,z1 x2,y2,z1  */
  glBegin(GL_POLYGON);    /* set color before call */
    glVertex3f(x1,y1,z1);
    glVertex3f(x1,y1,z2);
    glVertex3f(x2,y1,z2);
    glVertex3f(x2,y1,z1);
  glEnd();
  glBegin(GL_POLYGON);
    glVertex3f(x1,y1,z1);
    glVertex3f(x1,y2,z1);
    glVertex3f(x2,y2,z1);
    glVertex3f(x2,y1,z1);
  glEnd();
  glBegin(GL_POLYGON);
    glVertex3f(x1,y1,z1);
    glVertex3f(x1,y2,z1);
    glVertex3f(x1,y2,z2);
    glVertex3f(x1,y1,z2);
  glEnd();
  glBegin(GL_POLYGON);
    glVertex3f(x2,y2,z2);
    glVertex3f(x2,y2,z1);
    glVertex3f(x1,y2,z1);
    glVertex3f(x1,y2,z2);
  glEnd();
  glBegin(GL_POLYGON);
    glVertex3f(x2,y2,z2);
    glVertex3f(x2,y1,z2);
    glVertex3f(x1,y1,z2);
    glVertex3f(x1,y2,z2);
  glEnd();
  glBegin(GL_POLYGON);
    glVertex3f(x2,y2,z2);
    glVertex3f(x2,y1,z2);
    glVertex3f(x2,y1,z1);
    glVertex3f(x2,y2,z1);
  glEnd();
} /* end draw_box */

static writeText(GLfloat x, GLfloat y, GLfloat z, GLfloat size, char * msg)
{
  char * p;
  
  glPushMatrix();
    glLoadIdentity ();
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glTranslatef(x, y, z);
    glScalef(size/1000.0, size/1000.0, 0.0);
    for(p=msg; *p; p++)
      glutStrokeCharacter(GLUT_STROKE_ROMAN, *p);
  glPopMatrix();
} /* end writeText, may have to reset glEnable's after call */

static void draw_grid(void)
{
  int i, j, k;
  float x, y, z, stepx, stepy, stepz;
  
  glColor3f (0.0, 0.0, 1.0);
  glPointSize(2.0);
  stepx = 1.0/(float)nx;
  stepy = 1.0/(float)ny;
  stepz = 1.0/(float)nz;
  for(i=1; i<nx; i++){
  for(j=1; j<ny; j++){
  for(k=1; k<nz; k++){
    x = (float)i * stepx -0.5;
    y = (float)j * stepy -0.5;
    z = (float)k * stepz -0.5;
    glBegin(GL_POINTS);
      glVertex3f(x, y, z);
    glEnd();
    glColor3f (1.0, 1.0, 0.0);
    if(i==2 && j==2 && k==2) draw_box(x, x+stepx, y, y+stepy, z, z+stepz);
    glColor3f (0.0, 0.0, 1.0);
  }
  }
  }
} /* end draw_grid */

static void draw_cells(void)
{
  int i, j, k;
  float x, y, z, stepx, stepy, stepz;
  
  glColor3f (1.0, 0.0, 0.0);
  glPointSize(2.0);
  glBegin(GL_LINES);
    stepx = 1.0/(float)nx;
    stepy = 1.0/(float)ny;
    stepz = 1.0/(float)nz;
    for(i=0; i<nx+1; i++){
    for(j=0; j<ny+1; j++){
      x = (float)i * stepx -0.5;
      y = (float)j * stepy -0.5;
      z = (float)0 * stepz -0.5;
      glVertex3f(x, y, z);
      glVertex3f(x, y, z+1.0);
    }
    }
    for(i=0; i<nx+1; i++){
    for(k=0; k<nz+1; k++){
      x = (float)i * stepx -0.5;
      y = (float)0 * stepy -0.5;
      z = (float)k * stepz -0.5;
      glVertex3f(x, y, z);
      glVertex3f(x, y+1.0, z);
    }
    }
    for(j=0; j<ny+1; j++){
    for(k=0; k<nz+1; k++){
      x = (float)0 * stepx -0.5;
      y = (float)j * stepy -0.5;
      z = (float)k * stepz -0.5;
      glVertex3f(x, y, z);
      glVertex3f(x+1.0, y, z);
    }
    }
  glEnd();
} /* end draw_cells */

static void draw_vectors(void)
{
  int i, j, k;
  float x, y, z, stepx, stepy, stepz;
  float ang = 0.0;
  
  glColor3f (0.0, 1.0, .0);
    stepx = 1.0/(float)nx;
    stepy = 1.0/(float)ny;
    stepz = 1.0/(float)nz;
    for(i=0; i<nx; i++){
    for(j=0; j<ny; j++){
    for(k=0; k<nz; k++){
      x = (float)i * stepx -0.5 + stepx/2.0;
      y = (float)j * stepy -0.5 + stepy/2.0;
      z = (float)k * stepz -0.5 + stepz/2.0;
      glPushMatrix();
        glTranslatef(x, y, z);
        glRotatef(ang, 1.0, 0.0, 0.0);
        ang = ang+3.0; /* arbitrary rotation */
        glutSolidCone(0.03, 0.1, 6, 6);
      glPopMatrix();
    }
    }
    }
} /* end draw_vectors */

static void display_surface(void)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
  glRotatef(theta, 1.0, 0.0, 0.0);
  glRotatef(chi,   0.0, 1.0, 0.0);
  glRotatef(phi,   0.0, 0.0, 1.0);
  glColor3f (0.0, 0.0, 0.0);
  glutWireCube(1.0);
  if(grid_on) draw_grid();
  glFlush();
} /* end display_surface */

static void display_velocity(void)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
  glRotatef(theta, 1.0, 0.0, 0.0);
  glRotatef(chi,   0.0, 1.0, 0.0);
  glRotatef(phi,   0.0, 0.0, 1.0);
  glColor3f (0.0, 0.0, 0.0);
  glutWireCube(1.0);
  if(cells_on) draw_cells();
  glFlush();
} /* end display_velocity */

static void display_pressure(void)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
  glRotatef(theta, 1.0, 0.0, 0.0);
  glRotatef(chi,   0.0, 1.0, 0.0);
  glRotatef(phi,   0.0, 0.0, 1.0);
  glColor3f (0.0, 0.0, 0.0);
  glutWireCube(1.0);
  if(cells_on) draw_cells();
  if(vectors_on) draw_vectors();
  if(grid_on) draw_grid();
  glFlush();
} /* end display_pressure */

static void display_force(void)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
  glRotatef(theta, 1.0, 0.0, 0.0);
  glRotatef(chi,   0.0, 1.0, 0.0);
  glRotatef(phi,   0.0, 0.0, 1.0);
  glColor3f (0.0, 0.0, 0.0);
  glutWireCube(1.0);
  if(vectors_on) draw_vectors();
  glFlush();
} /* end display_force */

static void display_control(void)
{
  float fh;
  
  fh = (float)height;
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity();
  glColor3f(0.0, 0.0, 0.0);
  writeText(5.0, fh- 25.0, 0.0, 100.0, "key commands");
  writeText(5.0, fh- 45.0, 0.0, 100.0, "x,X,y,Y,z,Z rotate");
  writeText(5.0, fh- 65.0, 0.0, 100.0, "c  outlines cells");
  writeText(5.0, fh- 85.0, 0.0, 100.0, "v  vector direction");
  writeText(5.0, fh-105.0, 0.0, 100.0, "g  grid points");

  glColor3f(0.0, 1.0, 1.0);
  glBegin(GL_POLYGON);
    glVertex3f(0.0,1.0,0.0);
    glVertex3f((float)cwidth,1.0,0.0);
    glVertex3f((float)cwidth,20.0,0.0);
    glVertex3f(0.0,20.0,0.0);
  glEnd();
  glColor3f(0.0, 0.0, 0.0);
  writeText(5.0, 5.0, 0.0, 150.0, "clear");
  glColor3f(1.0, 1.0, 0.0);
  glBegin(GL_POLYGON);
    glVertex3f(0.0,22.0,0.0);
    glVertex3f((float)cwidth,22.0,0.0);
    glVertex3f((float)cwidth,42.0,0.0);
    glVertex3f(0.0,42.0,0.0);
  glEnd();
  glColor3f(0.0, 0.0, 0.0);
  writeText(5.0, 25.0, 0.0,150.0, "exit");
  glFlush();
} /* end display_control */

static void display_report(void)
{
  char t_val[]     = "T  =         seconds";
  char dt_val[]    = "dT =         seconds";
  char theta_val[] = "X rotation =      degrees";
  char chi_val[]   = "Y rotation =      degrees";
  char phi_val[]   = "Z rotation =      degrees";
  
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity();
  glColor3f(0.0, 0.0, 0.0);
  sprintf(&t_val[5], "%7.3f", t);
  t_val[12]=' '; /* remove null from sprintf string */
  writeText(5.0, (float)(height-30), 0.0, 100.0, t_val);

  sprintf(&dt_val[5], "%7.3f", dt);
  dt_val[12]=' '; /* remove null from sprintf string */
  writeText(5.0, (float)(height-50), 0.0, 100.0, dt_val);

  sprintf(&theta_val[13], "%4.0f", theta);
  writeText(5.0, 45.0, 0.0, 100.0, theta_val);

  sprintf(&chi_val[13], "%4.0f", chi);
  writeText(5.0, 25.0, 0.0, 100.0, chi_val);

  sprintf(&phi_val[13], "%4.0f", phi);
  writeText(5.0,  5.0, 0.0, 100.0, phi_val);
  glFlush();
} /* end display_report */

static void update_displays(void)   /* all */
{
  glutSetWindow(surfacew);   /* draw surface */
  glutPostRedisplay(); 
  glutSetWindow(velocityw);  /* draw velocity */
  glutPostRedisplay();
  glutSetWindow(pressurew);  /* draw pressure */
  glutPostRedisplay(); 
  glutSetWindow(forcew);     /* draw force */
  glutPostRedisplay();
  glutSetWindow(controlw);   /* draw control */
  glutPostRedisplay(); 
} /* end update_displays */

static void myinit(void) /* shared */
{
  glClearColor (1.0, 1.0, 1.0, 1.0);
  glColor3f (0.0, 0.0, 0.0);
} /* end myinit */

static void mouse(int btn, int state, int x, int y) /* shared */
{
  if(btn==GLUT_LEFT_BUTTON && state==GLUT_DOWN)
    theta = theta + 2.0; 
  if(btn==GLUT_RIGHT_BUTTON && state==GLUT_DOWN)
    phi = phi + 2.0;
  update_displays();
} /* end mouse */

static void mouse_control(int btn, int state, int x, int y)
{
  xm = x; /* global mouse coordinates */
  ym = height-y;
  if(btn==GLUT_LEFT_BUTTON && state==GLUT_DOWN)
  {
    if(in_rect(0, 0, cwidth, 20)) /* clear */
    {
      theta = 22.0;
      chi   = 0.0;
      phi   = 16.0;
      grid_on = 0;
      cells_on = 0;
      vectors_on = 0;
    }
    else if(in_rect(0, 22, cwidth, 20)) /* exit */
    {
      finish();
    }
  }
  update_displays();
} /* end mouse_control */

static int in_rect(int x, int y, int w, int h)
{
  return xm>x && xm<x+w && ym>y && ym<y+h;
} /* end in_rect */

static void keys(unsigned char key, int x, int y)
{
  /* Use x, X, y, Y, z, Z */
  if(key == 'x') theta -= 2.0;
  if(key == 'X') theta += 2.0;
  if(key == 'y') chi   -= 2.0;
  if(key == 'Y') chi   += 2.0;
  if(key == 'z') phi   -= 2.0;
  if(key == 'Z') phi   += 2.0;
  if(key == 'g') grid_on = 1 - grid_on;
  if(key == 'c') cells_on = 1 - cells_on;
  if(key == 'v') vectors_on = 1 - vectors_on;
  if(key == 'q') finish(); /* quit */
  update_displays();
} /* end keys */

static void reshape(int w, int h) /* shared */
{
  width = w;
  height = h;
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if (w <= h) 
    glOrtho (-1.0, 1.0, -1.0*(GLfloat)h/(GLfloat)w, 
             1.0*(GLfloat)h/(GLfloat)w, -1.0, 1.0);
  else 
    glOrtho (-1.0*(GLfloat)w/(GLfloat)h, 
              1.0*(GLfloat)w/(GLfloat)h, -1.0, 1.0, -1.0, 1.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity ();
  glEnable(GL_DEPTH_TEST);
} /* end reshape */

static void controlReshape(int w, int h)
{
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(0.0, (float)cwidth, 0.0, (float)height); /* all at z=0 */
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity ();
} /* end controlReshape */

int main(int argc, char* argv[])
{
  glutInit(&argc,argv);
  glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);

  glutInitWindowPosition(25,50);
  glutInitWindowSize(cwidth,height);
  controlw=glutCreateWindow("fw");
  myinit();
  glutDisplayFunc(display_control);
  glutReshapeFunc(controlReshape);
  glutMouseFunc(mouse_control);
  glutKeyboardFunc(keys);

  glutInitWindowPosition(25,50+height+34);
  glutInitWindowSize(cwidth,height);
  reportw=glutCreateWindow("report");
  myinit();
  glutDisplayFunc(display_report);
  glutReshapeFunc(controlReshape);
  glutMouseFunc(mouse);
  glutKeyboardFunc(keys);

  glutInitWindowPosition(25+cwidth+8,50);
  glutInitWindowSize(width,height);
  surfacew=glutCreateWindow("Surface");
  myinit();
  glutDisplayFunc(display_surface); 
  glutReshapeFunc(reshape);
  glutMouseFunc(mouse);
  glutKeyboardFunc(keys);

  glutInitWindowPosition(25+cwidth+8+width+8,50);
  glutInitWindowSize(width,height);
  velocityw=glutCreateWindow("Velocity vectors");
  myinit();
  glutDisplayFunc(display_velocity);
  glutReshapeFunc(reshape);
  glutMouseFunc(mouse);
  glutKeyboardFunc(keys);

  glutInitWindowPosition(25+cwidth+8,50+height+34);
  glutInitWindowSize(width,height);
  pressurew=glutCreateWindow("Pressure");
  myinit();
  glutDisplayFunc(display_pressure); 
  glutReshapeFunc(reshape);
  glutMouseFunc(mouse);
  glutKeyboardFunc(keys);

  glutInitWindowPosition(25+cwidth+8+width+8,50+height+34);
  glutInitWindowSize(width,height);
  forcew=glutCreateWindow("Force vectors");
  myinit();
  glutDisplayFunc(display_force);
  glutReshapeFunc(reshape);
  glutMouseFunc(mouse);
  glutKeyboardFunc(keys);

  glutMainLoop();
  return 0; /* exit should be via "quit" to finish() */
} /* end main */

static void finish(void)
{
  exit(0);
} /* end finish */
/* end four_windows.c */

