/**
 * $Id: TST_gears.c 15444 2008-07-05 17:05:05Z lukep $
 *
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 * The ubiquitous gears, threaded as test for the plugin frame. 
 */


/*
 * 3-D gear wheels.  This program is in the public domain.
 *
 * Command line options:
 *    -info      print GL implementation information
 *
 *
 * Brian Paul
 */

/* Conversion to GLUT by Mark J. Kilgard */

/*
 * $Log$
 * Revision 1.5  2007/04/04 13:18:38  campbellbarton
 * moved source and text to american spelling
 * * colour -> color
 * * centre -> center
 * * normalise -> normalize
 * * modelling -> modeling
 *
 * Revision 1.4  2004/03/22 22:01:49  jesterking
 * [GameEngine] Commit all Kester's changes made to the gameengine to restore 2.25 like physics.
 * [SCons] Build with Solid as default when enabling the gameengine in the build process
 * [SCons] Build solid and qhull from the extern directory and link statically against them
 *
 * That was about it.
 *
 * There are a few things that needs double checking:
 *
 * * Makefiles
 * * Projectfiles
 * * All the other systems than Linux and Windows on which the build (with scons) has been successfully tested.
 *
 * Revision 1.1.1.1  2003/08/10 20:22:32  desaster
 * Import of Blender/cvs 2003/08/10
 *
 * Revision 1.3  2002/11/25 12:02:14  mein
 *
 *
 * updated .c files to include:
 * #ifdef HAVE_CONFIG_H
 * #include <config.h>
 * #endif
 *
 * Just need to finish cpp files now :)
 *
 * Kent
 * --
 * mein@cs.umn.edu
 *
 * Revision 1.2  2002/10/13 15:57:16  hans
 * - mention the GPL the right way
 * - extend NaN Holding copyright to its proper startup
 *
 * Revision 1.1.1.1  2002/10/12 11:37:45  hans
 * For the open source Blender project we start with this fresh repository.
 * These are the NaN 2.25 Blender sources, slightly cleaned up, and stripped
 * from parts that could not be open sourced. Enjoy!
 *
 * Revision 1.4  2002/01/04 17:02:55  nzc
 * - Removed double mutex unlock from plain color drawing func.
 * - Renamed functions to better reflect the grouping. See comments in
 *   UnixShell.c for details.
 *
 * Revision 1.3  2001/12/07 10:49:41  nzc
 * - Switched mime type to application/x-blender-plugin, which is the same as the
 *   windows plugin.
 * - Concentrated drawing in the game engine in a more compact chunk. You can
 *   now skip drawing for a certain frame easily. Audio is also wrapped in
 *   that chunk. Janco: maybe separate loop for the audioo scenes?
 * - Adapted plugin-application interface to match use by Blender canvases.
 * - Adapted canvas to support error reporting on acquiring canvases.
 * - Adapted tests to run with the new apis.
 *
 * Revision 1.2  2001/12/03 12:21:06  nzc
 * - Added threading to the gears test. Works on FreeBSD and Linux :)
 *
 * Revision 1.1  2001/11/29 13:39:51  nzc
 * - Added more x fixes.
 * - Split up the tests into distinct parts.
 * - Use make ptest to make a gears plugin test on linux or freebsd.
 * - Works on Linux and FreeBSD now :)
 *
 * Revision 1.5  2001/11/23 15:24:42  nzc
 * - Ironed out a dozen little details for X/glX.
 * - Added more tracing hooks.
 *
 * Revision 1.4  2001/11/23 12:41:31  nzc
 * - Changed stupid common and unix lib names to plugin_common and
 *   plugin_common_unix.
 * - Abstracted plugin to application wrapper.
 * - Adapted test to comply to wrapper interface.
 *
 * Revision 1.3  2001/11/21 18:20:26  nzc
 * - Applied some ifdef's to tracing code for ease of use.
 * - Removed some unnecessary/unused variables and code.
 *
 * Revision 1.2  2001/11/21 13:33:22  nzc
 * - Some cleanup of comments, includes and headers.
 * - Added first part of X to KX event conversion.
 *
 * Revision 1.1  2001/11/20 13:08:23  nzc
 * - Added new ns plugin frame. This one has a gears-test.
 *
 * Revision 1.2  1999/10/21 16:39:06  brianp
 * added -info command line option
 *
 * Revision 1.1.1.1  1999/08/19 00:55:40  jtg
 * Imported sources
 *
 * Revision 3.2  1999/06/03 17:07:36  brianp
 * an extra quad was being drawn in front and back faces
 *
 * Revision 3.1  1998/11/03 02:49:10  brianp
 * added fps output
 *
 * Revision 3.0  1998/02/14 18:42:29  brianp
 * initial rev
 *
 */


#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <GL/glut.h>
#include <X11/keysym.h>
#include <X11/Intrinsic.h>

#include "NZC_application_hooks.h"
#include "NZC_application_callback.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifndef M_PI
#define M_PI 3.14159265
#endif

/* if defined: generate a log file */
/*  #define NZC_GENERATE_LOG */

typedef struct _gears_data {

	int terminate;
	
	GLfloat pos[4];
	GLfloat red[4];
	GLfloat green[4];
	GLfloat blue[4];

	
	GLint Frames;

	GLfloat view_rotx, view_roty, view_rotz;
	GLint gear1, gear2, gear3;
	GLfloat angle;

	PLA_plugin_handle plugin;
	
} gears_data;


/** Draw a set of gears. */
void
gear(GLfloat inner_radius,
	 GLfloat outer_radius,
	 GLfloat width,
	 GLint teeth,
	 GLfloat tooth_depth);

/** Make progress */
void
TST_proceed(APH_application_handle handle);

/** Add an entry to the log. */
static void
TST_log_entry(char* msg);
	

/**

  Draw a gear wheel.  You'll probably want to call this function when
  building a display list since we do a lot of trig here.
 
  Input:  inner_radius - radius of hole at center
          outer_radius - radius at center of teeth
          width - width of gear
          teeth - number of teeth
          tooth_depth - depth of tooth

 **/

void
gear(GLfloat inner_radius,
	 GLfloat outer_radius,
	 GLfloat width,
	 GLint teeth,
	 GLfloat tooth_depth)
{
	GLint i;
	GLfloat r0, r1, r2;
	GLfloat angle, da;
	GLfloat u, v, len;

	TST_log_entry("gear");
	
	r0 = inner_radius;
	r1 = outer_radius - tooth_depth / 2.0;
	r2 = outer_radius + tooth_depth / 2.0;

	da = 2.0 * M_PI / teeth / 4.0;

	glShadeModel(GL_FLAT);

	glNormal3f(0.0, 0.0, 1.0);

	/* draw front face */
	glBegin(GL_QUAD_STRIP);
	for (i = 0; i <= teeth; i++) {
		angle = i * 2.0 * M_PI / teeth;
		glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
		glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
		if (i < teeth) {
			glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
			glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
		}
	}
	glEnd();

  /* draw front sides of teeth */
	glBegin(GL_QUADS);
	da = 2.0 * M_PI / teeth / 4.0;
	for (i = 0; i < teeth; i++) {
		angle = i * 2.0 * M_PI / teeth;

		glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
		glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
		glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5);
		glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
	}
	glEnd();

	glNormal3f(0.0, 0.0, -1.0);

	/* draw back face */
	glBegin(GL_QUAD_STRIP);
	for (i = 0; i <= teeth; i++) {
		angle = i * 2.0 * M_PI / teeth;
		glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
		glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
		if (i < teeth) {
			glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
			glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
		}
	}
	glEnd();

  /* draw back sides of teeth */
	glBegin(GL_QUADS);
	da = 2.0 * M_PI / teeth / 4.0;
	for (i = 0; i < teeth; i++) {
		angle = i * 2.0 * M_PI / teeth;

		glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
		glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5);
		glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
		glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
	}
	glEnd();

  /* draw outward faces of teeth */
	glBegin(GL_QUAD_STRIP);
	for (i = 0; i < teeth; i++) {
		angle = i * 2.0 * M_PI / teeth;

		glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
		glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
		u = r2 * cos(angle + da) - r1 * cos(angle);
		v = r2 * sin(angle + da) - r1 * sin(angle);
		len = sqrt(u * u + v * v);
		u /= len;
		v /= len;
		glNormal3f(v, -u, 0.0);
		glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
		glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
		glNormal3f(cos(angle), sin(angle), 0.0);
		glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5);
		glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5);
		u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
		v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
		glNormal3f(v, -u, 0.0);
		glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
		glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
		glNormal3f(cos(angle), sin(angle), 0.0);
	}

	glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5);
	glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5);

	glEnd();

	glShadeModel(GL_SMOOTH);

	/* draw inside radius cylinder */
	glBegin(GL_QUAD_STRIP);
	for (i = 0; i <= teeth; i++) {
		angle = i * 2.0 * M_PI / teeth;
		glNormal3f(-cos(angle), -sin(angle), 0.0);
		glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
		glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
	}
	glEnd();

}


void
APH_redraw(APH_application_handle handle)
{
	gears_data* h = (gears_data*) handle;

	TST_log_entry("NZC_redraw");

	if (!PLA_acquire_glx_context(h->plugin)) return;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glPushMatrix();
	glRotatef(h->view_rotx, 1.0, 0.0, 0.0);
	glRotatef(h->view_roty, 0.0, 1.0, 0.0);
	glRotatef(h->view_rotz, 0.0, 0.0, 1.0);

	glPushMatrix();
	glTranslatef(-3.0, -2.0, 0.0);
	glRotatef(h->angle, 0.0, 0.0, 1.0);
	glCallList(h->gear1);
	glPopMatrix();

	glPushMatrix();
	glTranslatef(3.1, -2.0, 0.0);
	glRotatef(-2.0 * h->angle - 9.0, 0.0, 0.0, 1.0);
	glCallList(h->gear2);
	glPopMatrix();

	glPushMatrix();
	glTranslatef(-3.1, 4.2, 0.0);
	glRotatef(-2.0 * h->angle - 25.0, 0.0, 0.0, 1.0);
	glCallList(h->gear3);
	glPopMatrix();

	glPopMatrix();

	h->Frames++;

	PLA_swap_and_release_glx_context(h->plugin);
}


void
TST_proceed(APH_application_handle handle)
{
	gears_data* h = (gears_data*) handle;	

	TST_log_entry("TST_proceed");
	h->angle += 2.0;
	APH_redraw(handle);
}

/** Accept an X key release event */
void APH_keyboard_release(APH_application_handle handle, KeySym key)
{
	gears_data* h = (gears_data*) handle;

	TST_log_entry("NZC_keyboard_release");
	h->angle += 2.0;
	APH_redraw(handle);
}

/** Accept an X mousebutton press event */
void APH_mousekey_press(APH_application_handle handle, int xbut)
{
	gears_data* h = (gears_data*) handle;

	TST_log_entry("NZC_mousekey_press");
	h->angle += 2.0;
	APH_redraw(handle);
}

/** Accept an X mousebutton release event */
void APH_mousekey_release(APH_application_handle handle, int xbut)
{
	gears_data* h = (gears_data*) handle;

	TST_log_entry("NZC_mousekey_release");
	h->angle += 2.0;
	APH_redraw(handle);
}

/** Accept an X mouse movement event */
void APH_mouse_movement(APH_application_handle handle, int x, int y)
{
	gears_data* h = (gears_data*) handle;

	TST_log_entry("NZC_mouse_movement");
	h->angle += 2.0;
	APH_redraw(handle);
}


void
APH_keyboard_press(APH_application_handle handle, KeySym key)
{
	gears_data* h = (gears_data*) handle;

	TST_log_entry("NZC_keyboard_press");
	
	switch (key) {
	case XK_Up:
		h->view_rotx += 5.0;
		break;
	case XK_Down:
		h->view_rotx -= 5.0;
		break;
	case XK_Left:
		h->view_roty += 5.0;
		break;
	case XK_Right:
		h->view_roty -= 5.0;
		break;
	case XK_z:
		h->view_rotz += 5.0;
		break;
	case XK_Z:
		h->view_rotz -= 5.0;
		break;
	case XK_Escape: 
		h->terminate = 1;
		break;
	default:
		return;
	}
	TST_proceed(handle);
	APH_redraw(handle);
	
}


/* new context: do inits  */
void
APH_reset_viewport(APH_application_handle handle, int width, int height)
{
	GLfloat h = (GLfloat) height / (GLfloat) width;
	gears_data* gd = (gears_data*) handle;

	TST_log_entry("NZC_set_viewport");

	PLA_acquire_glx_context(gd->plugin);

	glViewport(0, 0, (GLint) width, (GLint) height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(0.0, 0.0, -40.0);

	// redo the displaylists
	glLightfv(GL_LIGHT0, GL_POSITION, gd->pos);
	glEnable(GL_CULL_FACE);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_DEPTH_TEST);

	/* make the gears */
	gd->gear1 = glGenLists(1);
	glNewList(gd->gear1, GL_COMPILE);
	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gd->red);
	gear(1.0, 4.0, 1.0, 20, 0.7);
	glEndList();

	gd->gear2 = glGenLists(1);
	glNewList(gd->gear2, GL_COMPILE);
	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gd->green);
	gear(0.5, 2.0, 2.0, 10, 0.7);
	glEndList();

	gd->gear3 = glGenLists(1);
	glNewList(gd->gear3, GL_COMPILE);
	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gd->blue);
	gear(1.3, 2.0, 0.5, 10, 0.7);
	glEndList();

	glEnable(GL_NORMALIZE);

	PLA_release_glx_context(gd->plugin);
}



APH_application_handle
APH_create_application(PLA_plugin_handle p_handle)
{
	gears_data* gd = NULL;

	TST_log_entry("NZC_create_application");
	
	gd = malloc(sizeof(gears_data));
	if (!gd) return NULL;

	gd->terminate = 0;
	
	gd->plugin = p_handle;
	
	gd->Frames = 0;
	gd->view_rotx = 20.0;
	gd->view_roty = 30.0;
	gd->view_rotz = 0.0;
	gd->angle = 0.0;

	gd->pos[0]   = 5.0;
	gd->pos[1]   = 5.0;
	gd->pos[2]   = 10.0;
	gd->pos[3]   = 0.0;

	gd->red[0]   = 0.8;
	gd->red[1]   = 0.1;
	gd->red[2]   = 0.0;
	gd->red[3]   = 1.0;

	gd->green[0]   = 0.0;
	gd->green[1]   = 0.8;
	gd->green[2]   = 0.2;
	gd->green[3]   = 1.0;

	gd->blue[0]   = 0.2;
	gd->blue[1]   = 0.2;
	gd->blue[2]   = 1.0;
	gd->blue[3]   = 1.0;

	return (APH_application_handle) gd;

}

void
APH_terminate_application(APH_application_handle handle)
{
	gears_data* h = (gears_data*) handle;

	TST_log_entry("NZC_terminate_application");

	free(h);
}

static void
TST_log_entry(char* msg)
{
#ifdef NZC_GENERATE_LOG
	FILE* fp = fopen("plugin_log","a");
	if (!fp) return;
  	fprintf(fp, "--> TST_gears::%s\n", msg); 
	fflush(fp);
	fclose (fp);
#endif
}
