/*
  demo.cc
  By Wayne Piekarski - wayne@cs.unisa.edu.au
  
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  See the file LICENSE included in this directory for more information.
*/


#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "texture.h"
#include "v4l.h"
#include "1394.h"




/* Constants that control the application */
#define WIN_WIDTH  640 /* X Window Dimensions */
#define WIN_HEIGHT 480

/* Variables for the camera image size */
int cam_width, cam_height;
bool mode_v4l;

/* Variables that control our viewpoint orbit angle */
float xangle = 150;
float yangle = 130;




/* Initialisation function which is called once at start up */
void init (void)
{
  /* Set up smooth shading */
  glShadeModel (GL_SMOOTH);
  
  /* Use a black background and set a default depth */
  glClearColor (0, 0, 0, 0);
  
  /* Enable depth buffer testing */
  glEnable (GL_DEPTH_TEST);
  
  /* Activate replacement textures, if we don't do this then we don't see any textures! */
  glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}




/* If you think you have cause an OpenGL error somewhere, sprinkle check_errors() throughout your application
   and it will help you find the offending place */
#define check_errors() __check_errors (__FILE__, __LINE__)
void __check_errors (char *file, int line)
{
  glFlush ();
  GLenum glerror = glGetError ();
  if (glerror != GL_NO_ERROR)
    {
      fprintf (stderr, "OpenGL error code %d detected at %s:%d - %s\n", glerror, file, line, gluErrorString(glerror));
      exit (1);
    }
}




/* Callback which is used to render the display each time a change is made */
void handle_display (void)
{
  /* Clear the display and depth buffers */
  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  /* Setup the camera for perspective projection */
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  gluPerspective (45.0, (double)WIN_WIDTH / (double)WIN_HEIGHT, 0.2, 20); /* FOV, Aspect, Near, Far */
  
  /* Setup the model and draw the objects in the environment */
  glMatrixMode (GL_MODELVIEW);  
  
  /* Initially we start at the origin */
  glLoadIdentity();
  
  /* Move the camera so we are at the desired location */
  gluLookAt (0,   4.0, 0,  /* Eye location at origin */
	     0,   1,   0,  /* Y axis straight ahead, look toward north pole */
	     0,   0,   1); /* Z axis straight up */
  
  /* Rotate the camera along the two required angles */
  glRotatef (xangle, 0, 0, 1);
  glRotatef (yangle, 1, 0, 0);
  
  /* Draw XYZ axes at the origin (X=red, Y=green, Z=blue) */
  {
    glPushMatrix ();
    
    glScalef (1.2, 1.2, 1.2);
    glBegin (GL_LINES);
    glColor3f  (1, 0, 0);
    glVertex3f (0, 0, 0);
    glVertex3f (1, 0, 0);
    glEnd ();
    
    glBegin (GL_LINES);
    glColor3f  (0, 1, 0);
    glVertex3f (0, 0, 0);
    glVertex3f (0, 1, 0);
    glEnd ();
    
    glBegin (GL_LINES);
    glColor3f  (0, 0, 1);
    glVertex3f (0, 0, 0);
    glVertex3f (0, 0, 1);
    glEnd ();
    
    glPopMatrix ();
  }
  
  /* Draw three squares with the video texture on it at different angles */
  {
    square_texture ();
    
    glRotatef (+90, 1, 0, 0);
    square_texture ();
    glRotatef (-90, 1, 0, 0);
    
    glRotatef (-90, 0, 1, 0);
    square_texture ();
    glRotatef (+90, 0, 1, 0);
  }
  
  
  /* We have finished drawing, swap the double buffer over to display the final image */
  glutSwapBuffers ();
  
  /* Check for any errors */
  check_errors ();
}




/* This is a handler to deal with most keyboard presses */
void handle_keyboard (unsigned char key, int x, int y)
{
  /* Process the input */
  switch (key)
    {
      /* Handler for quitting */
    case 'Q':
    case 27:
      exit (0);
      break;
    default:
      /* Do nothing */
      break;
    }
  
  /* Now refresh the display */
  handle_display ();
}




/* This is a special handler to deal with arrow keys */
void handle_arrows ( int key, int x, int y )
{
  /* Process the event */
  switch (key)
    {
      /* Handle up and down */
    case GLUT_KEY_UP:
      yangle += 5;
      break;
    case GLUT_KEY_DOWN:
      yangle -= 5;
      break;
      
      /* Handle left and right */
    case GLUT_KEY_LEFT:
      xangle += 5;
      break;
    case GLUT_KEY_RIGHT:
      xangle -= 5;
      break;
      
      /* Do nothing */
    default:
      break;
    }
  
  fprintf (stderr, "X = %f, Y = %f\n", xangle, yangle);
  
  /* Now refresh the display */
  handle_display ();
}




/* Handle times when GLUT is idle and needs something to do */
void handle_idle (void)
{
  char *img;
  
  /* Capture and get a new video frame */
  if (mode_v4l)
    {
      capture_v4l ();
      img = get_v4l ();
    }
  else
    {
      capture_1394 ();
      img = get_1394 ();
    }
  
  /* Push this new image into the texture map */
  change_texture (img);
  
  /* Now refresh the display */
  handle_display ();
}




/* This is the main code which starts everything up */
int main (int argc, char *argv[])
{
  /* Print GPL copyright message */
  fprintf (stderr, "Texture Mapping Demonstration\n");
  fprintf (stderr, "Copyright (C) 2003   Wayne Piekarski - wayne@cs.unisa.edu.au\n\n");
  
  fprintf (stderr, "This program is free software; you can redistribute it and/or modify\n");
  fprintf (stderr, "it under the terms of the GNU General Public License as published by\n");
  fprintf (stderr, "the Free Software Foundation; either version 2 of the License, or\n");
  fprintf (stderr, "(at your option) any later version.\n\n");
  
  fprintf (stderr, "This program is distributed in the hope that it will be useful,\n");
  fprintf (stderr, "but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
  fprintf (stderr, "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");
  fprintf (stderr, "GNU General Public License for more details.\n\n");
  
  fprintf (stderr, "You should have received a copy of the GNU General Public License\n");
  fprintf (stderr, "along with this program; if not, write to the Free Software\n");
  fprintf (stderr, "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n\n");
  
  
  /* Initialise the GLUT libraries */
  glutInit (&argc, argv);
  
  /* Enable RGB mode with double buffering */
  glutInitDisplayMode (GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
  
  /* Configure our application to run in the desired window size */
  glutInitWindowSize (WIN_WIDTH, WIN_HEIGHT);
  glutCreateWindow ("Linux Conf Au 2004 Tutorial - By Wayne Piekarski");
  
  /* Initialise the OpenGL system once off */
  init ();
  
  /* Initialise the video capture process */
  mode_v4l = false;
  if (open_v4l ("/dev/video", &cam_width, &cam_height) == 0)
    {
      mode_v4l = true;
      start_v4l ();
    }
  else if (open_1394 ("", &cam_width, &cam_height) == 0)
    {
      mode_v4l = false;
      start_1394 ();
    }
  else
    {
      fprintf (stderr, "Could not find either a V4L or a firewire camera!\n");
      exit (1);
    }
  
  /* Initialise the texture maps */
  init_texture (cam_width, cam_height);
  
  /* Configure GLUT callback handlers for events */
  glutDisplayFunc (handle_display);
  glutKeyboardFunc (handle_keyboard);
  glutSpecialFunc (handle_arrows);
  glutIdleFunc (handle_idle);
  
  /* Now we enter the main loop and process GLUT events */
  glutMainLoop ();
  exit (0);
}
