/* $Id: BusyWidget.cpp,v 1.9 2003/02/14 08:28:04 rjesse Exp $
 *
 * Copyright (c) 2000-2003, Roland Jesse
 * All rights reserved.
 *
 * This file is part of the Mango recipe management distribution.
 * For license and copyright information see the file COPYRIGHT or 
 * <http://www.opensource.org/licenses/bsd-license.html>.
 */

#include "BusyWidget.h"
#include "Mango.h"
#include <qtimer.h>
#include <stdlib.h>

#define COLOUR_STEP 0.002
#define NUM_FACES_FOR_M 18      // The "M" is made up with 18 faces.

/*!
  Colours for the cube and/or the "M".
  In case of the "M", only 7 out of these 8 colours are used. Therefore,
  one is free and best used for a "marching ball".
*/
GLfloat BusyWidget::m_colours[][3] = {
   {0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 1.0, 0.0},
   {0.0, 0.0, 1.0}, {1.0, 0.0, 1.0}, {1.0, 1.0, 1.0}, {0.0, 1.0, 1.0}};

/*! Normals for the cube. */
const GLfloat BusyWidget::m_normals[][3] = {
   {-1.0, -1.0, -1.0}, { 1.0, -1.0, -1.0},
   { 1.0,  1.0, -1.0}, {-1.0,  1.0, -1.0},
   {-1.0, -1.0,  1.0}, { 1.0, -1.0,  1.0},
   { 1.0,  1.0,  1.0}, {-1.0,  1.0,  1.0}};

/*! Vertices for the cube. */
const GLfloat BusyWidget::m_vertices[][3] = {
   {-1.0, -1.0, -1.0}, { 1.0, -1.0, -1.0},
   { 1.0,  1.0, -1.0}, {-1.0,  1.0, -1.0},
   {-1.0, -1.0,  1.0}, { 1.0, -1.0,  1.0},
   { 1.0,  1.0,  1.0}, {-1.0,  1.0,  1.0}};

/*! Vertices for the "M":

      17                                     19
      /¦\                                   /|
    16 ¦ -                                18 |
     |\¦   \                           __/ | |
     | -    -                       __/    | |
     | ¦ \    \                  __/       | |
     | ¦ 13    -              __/       _15| |
     | ¦ /¦ \    \         __/       __/  ¦| |
     | 12 ¦  -     -    __/       __/ __14¦| |
     | ¦|\¦    \     \ /       __/ __/  | ¦| |
     | ¦| +     -    11     __/ __/     | ¦| |
     | ¦| | \     \  /|  __/ __/        | ¦| |
     | ¦| |  -    10  | / __/           | ¦| |
     | ¦| |    \   |  9-_/              | ¦| |
     | ¦| |     -  | //                 | ¦| |
     | ¦| |       \ 8                   | ¦| |
     | ¦| |                             | ¦| |
     | ¦| |                             | ¦| |
     | ¦| |                             | ¦| |
     | 3__2                             | 7__6
     |/ |/                              |/ |/
     0__1                               4__5
*/
const GLfloat BusyWidget::m_verticesM[][3] = {
   {-1, -1, -0.25}, {-0.7, -1, -0.25}, {-0.7, -1, 0.25}, {-1, -1, 0.25}, // 0-3
   {0.7, -1, -0.25}, {1, -1, -0.25}, {1, -1, 0.25}, {0.7, -1, 0.25}, // 4-7
   {0, 0, -0.25}, {0, 0, 0.25}, {0, 0.2, -0.25}, {0, 0.2, 0.25}, // 8-11
   {-0.7, 0.7, -0.25}, {-0.7, 0.7, 0.25}, {0.7, 0.7, -0.25}, {0.7, 0.7, 0.25}, // 12-15
   {-1, 1, -0.25}, {-1, 1, 0.25}, {1, 1, -0.25}, {1, 1, 0.25}};

/*! The faces that make up the "M". */
const BusyWidget::Face BusyWidget::m_facesM[] = {
   // left
   {0, 1, 2, 3},
   {0, 16, 17, 3},
   {0, 1, 12, 16},
   {1, 2, 13, 12},
   {3, 2, 13, 17},
   // top
   {16, 12, 8, 10},
   {12, 13, 9, 8},
   {13, 17, 11, 9},
   {17, 16, 10, 11},
   {10, 8, 14, 18},
   {8, 9, 15, 14},
   {11, 9, 15, 19},
   {11, 10, 18, 19},
   // right
   {18, 14, 4, 5},
   {5, 6, 19, 18},
   {7, 6, 19, 15},
   {7, 4, 14, 15},
   {4, 5, 6, 7}};

BusyWidget::BusyWidget( QWidget* parent, const char* name)
   : QGLWidget( parent, name)
{
   m_object = 0;
   for (int a = 0; a < 2; a++) m_theta[a] = 0.0;
   m_axis = 2;

   setMinimumSize( QSize( 10, 10));

   m_spinTimer = new QTimer( this);
   connect( m_spinTimer, SIGNAL(timeout()), this, SLOT( slotSpinScene()));
   
   m_directionTimer = new QTimer( this);
   connect( m_directionTimer, SIGNAL( timeout()), this, SLOT( slotChangeCubeDirection()));
}

BusyWidget::~BusyWidget() {
   glDeleteLists( m_object, 1);
}

void
BusyWidget::start() {
   m_spinTimer->start( 0);
   m_directionTimer->start( rand() % 3000);	// wait max. three seconds
}

void
BusyWidget::stop() {
   m_spinTimer->stop();
   m_directionTimer->stop();
}

void
BusyWidget::initializeGL() {
   qglClearColor( Mango::getInstance()->paletteBackgroundColor());
   //m_object = makeObject();		// Generate display list
   glEnable( GL_DEPTH_TEST);
}

void
BusyWidget::resizeGL( int w, int h) {
   glViewport( 0, 0, (GLint)w, (GLint)h );
   glMatrixMode( GL_PROJECTION);
   glLoadIdentity();
   if (w <= h)
	  glOrtho( -2.0, 2.0, -2.0 * (GLfloat) h / (GLfloat) w,
			   2.0 * (GLfloat) h / (GLfloat) w,
			   -10.0, 10.0);
   else
	  glOrtho( -2.0 * (GLfloat) w / (GLfloat) h,
			   2.0 * (GLfloat) w / (GLfloat) h,
			   -2.0, 2.0, -10.0, 10.0);
   glMatrixMode( GL_MODELVIEW);
}

/*!
  Assemble the scene.
*/
void
BusyWidget::paintGL() {
   glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   glLoadIdentity();
   glRotatef( m_theta[0], 1.0, 0.0, 0.0);
   glRotatef( m_theta[1], 0.0, 1.0, 0.0);
   glRotatef( m_theta[2], 0.0, 0.0, 1.0);
   //cube();
   if (m_object) glDeleteLists( m_object, 1);
   m_object = makeObject();
   glCallList( m_object);
   glFlush();
   swapBuffers();
} // paintGL()

/*!
  Constructs the "M" out of 18 faces.
*/
GLuint
BusyWidget::makeObject() {
   GLuint dlist = glGenLists( 1);
   
   glNewList( dlist, GL_COMPILE);
   for (GLuint face = 0; face < NUM_FACES_FOR_M; face++) {
      GLuint colIdx = faceColourIndexForM( face);
      glColor3f( m_colours[colIdx][0], m_colours[colIdx][1], m_colours[colIdx][2]);
      glBegin( GL_POLYGON);
      for (GLuint vertex = 0; vertex < 4; vertex++) { // 4 vertices per face
         int idx = m_facesM[face][vertex];
         glVertex3f( m_verticesM[idx][0], m_verticesM[idx][1], m_verticesM[idx][2]);
      }
      glEnd();
   }
   glEndList();
   return dlist;   
} // makeObject()

/*!
  Determines the appropriate colour for a face.
  \param \c face the index into m_facesM.
  \return the colour index into m_colours to be used.
*/
GLuint
BusyWidget::faceColourIndexForM( GLuint face)
{
   GLuint ret = 0;
   switch (face) {
   case 0: ret = 0; break;
   case 1: ret = 1; break;
   case 2: ret = 2; break;      // front
   case 3: ret = 3; break;
   case 4: ret = 4; break;      // back
   case 5: ret = 2; break;      // front
   case 6: ret = 5; break;
   case 7: ret = 4; break;      // back
   case 8: ret = 6; break;
   case 9: ret = 2; break;      // front
   case 10: ret = 5; break;
   case 11: ret = 4; break;     // back
   case 12: ret = 6; break;
   case 13: ret = 2; break;     // front
   case 14: ret = 1; break;
   case 15: ret = 4; break;     // back
   case 16: ret = 3; break;
   case 17: ret = 0; break;
   }
   return ret;
} // faceColourIndexForM()

void
BusyWidget::polygon(int a, int b, int c, int d) {
   glBegin( GL_POLYGON);
     glColor3fv( m_colours[a]);
	 glNormal3fv( m_normals[a]);
	 glVertex3fv( m_vertices[a]);

     glColor3fv( m_colours[b]);
	 glNormal3fv( m_normals[b]);
	 glVertex3fv( m_vertices[b]);

     glColor3fv( m_colours[c]);
	 glNormal3fv( m_normals[c]);
	 glVertex3fv( m_vertices[c]);

     glColor3fv( m_colours[d]);
	 glNormal3fv( m_normals[d]);
	 glVertex3fv( m_vertices[d]);
   glEnd();
}

void
BusyWidget::cube() {
   polygon( 0, 3, 2, 1);
   polygon( 2, 3, 7, 6);
   polygon( 0, 4, 7, 3);
   polygon( 1, 2, 6, 5);
   polygon( 4, 5, 6, 7);
   polygon( 0, 1, 5, 4);
}

/*!
  Animate the cube.
*/
void
BusyWidget::slotSpinScene()
{
   m_theta[m_axis] += 2.0;
   if (m_theta[m_axis] > 360) m_theta[m_axis] = -360.0;

   for (int i = 0; i < 8; i++) { // eight corners
      // add some randomness
#ifdef Q_OS_WIN32
	   if ((rand() % (i+1)) == 0) continue;
#else
      if ((random() % (i+1)) == 0) continue;
#endif

      float r1 = m_colours[i][0], g1 = m_colours[i][1], b1 = m_colours[i][2];

      float r2 = r1;
      if (r1) r2 = (r1 + COLOUR_STEP) > 1.0 ? COLOUR_STEP : r1 + COLOUR_STEP;
      float g2 = g1;
      if (g1) g2 = (g1 + COLOUR_STEP) > 1.0 ? COLOUR_STEP : g1 + COLOUR_STEP;
      float b2 = b1;
      if (b1) b2 = (b1 + COLOUR_STEP) > 1.0 ? COLOUR_STEP : b1 + COLOUR_STEP;

      m_colours[i][0] = r2; m_colours[i][1] = g2; m_colours[i][2] = b2;
   }
   updateGL();
} // slotSpinScene()

/*!
 * Change direction in which the cube rotates. Currently, it is done randomly. 
 */
void
BusyWidget::slotChangeCubeDirection()
{
#ifdef Q_OS_WIN32
	m_axis = rand() % 3;
#else
	m_axis = random() % 3;
#endif
}
