/* -*- Mode: C; tab-width: 4 -*- */
/* atunnels --- OpenGL Advanced Tunnel Screensaver */

#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)atunnels.c	5.02 2002/03/16 xlockmore";
#endif

/* Copyright (c) E. Lassauge, 2002. */

/*
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * This file is provided AS IS with no warranties of any kind.  The author
 * shall have no liability with respect to the infringement of copyrights,
 * trade secrets or any patents by this file or any part thereof.  In no
 * event will the author be liable for any lost revenue or profits or
 * other special, indirect and consequential damages.
 *
 * The original code for this mode was written by Roman Podobedov
 * Email: romka@ut.ee
 * WEB: http://romka.demonews.com
 *
 * Eric Lassauge  (March-16-2002) <lassauge@mail.dotcom.fr>
 * 				    http://lassauge.free.fr/linux.html
 *
 * REVISION HISTORY:
 *
 * E.Lassauge - 16-Mar-2002:
 *	- created based on the Roman demo.
 *	- deleted all external file stuff to use xpm textures and
 *	  hardcoded path point values.
 *
 */

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

#ifdef STANDALONE		/* xscreensaver mode */
# define PROGCLASS		"Atunnels"
# define HACK_INIT		init_atunnels
# define HACK_DRAW		draw_atunnels
# define HACK_RESHAPE		reshape_atunnels
# define atunnels_opts		xlockmore_opts
#define	DEFAULTS                "*delay:	10000	\n" \
				"*showFPS:     	False	\n" \
                                "*light:	True	\n" \
                                "*wire:		False	\n" \
                                "*texture:	True	\n"

# include "xlockmore.h"		/* from the xscreensaver distribution */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#else /* !STANDALONE */
# include "xlock.h"		/* from the xlockmore distribution */
# include "visgl.h"
#endif /* !STANDALONE */

#ifdef USE_GL /* whole file */

#include "tunnel_draw.h"

#ifdef HAVE_XPM
#include "xpm-ximage.h"

#ifdef STANDALONE
#include "../images/tunnel0.xpm"
#include "../images/tunnel1.xpm"
#include "../images/tunnel2.xpm"
#else /* !STANDALONE */
#include "pixmaps/tunnel0.xpm"
#include "pixmaps/tunnel1.xpm"
#include "pixmaps/tunnel2.xpm"
#endif /* !STANDALONE */
#endif /* HAVE_XPM */

#undef countof
#define countof(x) (sizeof((x))/sizeof((*x)))

#define DEF_LIGHT	"True"
#define DEF_WIRE   	"False"
#define DEF_TEXTURE	"True"
#define MINSIZE         32      /* minimal viewport size */

static int do_light;
static int do_wire;
static int do_texture;

static XrmOptionDescRec opts[] = {
  {(char *)"-light",   (char *)".atunnels.light",      XrmoptionNoArg, (caddr_t) "true" },
  {(char *)"+light",   (char *)".atunnels.light",      XrmoptionNoArg, (caddr_t) "false" },
  {(char *)"-wire",    (char *)".atunnels.wire",       XrmoptionNoArg, (caddr_t) "true" },
  {(char *)"+wire",    (char *)".atunnels.wire",       XrmoptionNoArg, (caddr_t) "false" },
  {(char *)"-texture", (char *)".atunnels.texture",    XrmoptionNoArg, (caddr_t) "true" },
  {(char *)"+texture", (char *)".atunnels.texture",    XrmoptionNoArg, (caddr_t) "false" },
};

static argtype vars[] = {
  {(caddr_t *) &do_light,   (char *)"light",  (char *)"Light",   DEF_LIGHT,   t_Bool},
  {(caddr_t *) &do_wire,    (char *)"wire",   (char *)"Wire",    DEF_WIRE,    t_Bool},
  {(caddr_t *) &do_texture, (char *)"texture",(char *)"Texture", DEF_TEXTURE, t_Bool},
};

static OptionStruct desc[] =
{
  {(char *)"-/+ light",   (char *)"whether to do enable lighting (slower)"},
  {(char *)"-/+ wire",    (char *)"whether to do use wireframe instead of filled (faster)"},
  {(char *)"-/+ texture", (char *)"whether to apply a texture (slower)"},
};

ModeSpecOpt atunnels_opts = {countof(opts), opts, countof(vars), vars, desc};

#ifdef USE_MODULES
ModStruct   atunnels_description =
{"atunnels", "init_atunnels", "draw_atunnels", "release_atunnels",
 "draw_atunnels", "init_atunnels", NULL, &atunnels_opts,
 25000, 1, 1, 1, 400, 1.0, "",
 "Shows an OpenGL advanced tunnel screensaver", 0, NULL};
#endif

/* structure for holding the screensaver data */
typedef struct {
  GLint WIDTH, HEIGHT;
  GLXContext *glx_context;
} atunnelsstruct;

static atunnelsstruct *Atunnels = NULL;

/* convenient access to the screen width */
static GLuint texture[3];     /* Textures */

/*=================== Load Texture =========================================*/
static Bool LoadTexture(ModeInfo * mi, char **fn, int t_num)
{
#ifdef HAVE_XPM
	XImage *teximage;    /* Texture data */
 
        if ((teximage = xpm_to_ximage(MI_DISPLAY(mi), MI_VISUAL(mi),
			 MI_COLORMAP(mi), fn)) == None) {
	    (void) fprintf(stderr, "Error reading the texture.\n");
	    glDeleteTextures(1, &texture[t_num]);
	    return False;
	}

#ifdef HAVE_GLBINDTEXTURE
	glBindTexture(GL_TEXTURE_2D, texture[t_num]);
#endif /* HAVE_GLBINDTEXTURE */
	glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
	clear_gl_error();
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, teximage->width, teximage->height, 
			0, GL_RGBA, GL_UNSIGNED_BYTE, teximage->data);
	if (check_gl_error("texture"))
		return False;

	/* Texture parameters, LINEAR scaling for better texture quality */
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 

	XDestroyImage(teximage);
	return True;
#else /* !HAVE_XPM */
	return False;
#endif /* !HAVE_XPM */
} 

/*=================== Main Initialization ==================================*/
static Bool Init(ModeInfo * mi)
{
	GLfloat light_ambient[] = {1.0, 1.0, 1.0, 1.0};
	GLfloat light_diffuse[] = {1.0, 1.0, 1.0, 1.0};
	GLfloat light_specular[] = {1.0, 1.0, 1.0, 1.0};
	GLfloat light_position[] = {0.0, 0.0, 1.0, 0.0};
	GLfloat fogColor[4] = {0.8, 0.8, 0.8, 1.0};

  	glClearColor(0, 0, 0, 0);
#ifdef HAVE_XPM
	if (do_texture)
	{
		glGenTextures(3, texture);
		if (!LoadTexture(mi, texture0,0))
			do_texture = False;
		else if (!LoadTexture(mi, texture1,1))
			do_texture = False;
		else if (!LoadTexture(mi, texture2,2))
			do_texture = False;
		else
			glEnable(GL_TEXTURE_2D);
	}
#endif
	if (!InitTunnel())
		return False;
	
	/* Set lighting parameters */
	if (do_light)
	{
		glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
		glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
		glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
		glLightfv(GL_LIGHT0, GL_POSITION, light_position);

		/* Enable light 0 */
		glEnable(GL_LIGHT0);
		glDepthFunc(GL_LESS);
	
		glEnable(GL_LIGHTING);
	}
  	if (do_wire) {
		glDisable(GL_NORMALIZE);
		glDisable(GL_CULL_FACE);
		glDisable(GL_DEPTH_TEST);
		glDisable(GL_TEXTURE_2D);
		glPolygonMode(GL_FRONT,GL_LINE);
  		glPolygonMode(GL_BACK,GL_LINE);
  	} else {
		glEnable(GL_DEPTH_TEST);
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

		/* Enable fog */
		glFogi(GL_FOG_MODE, GL_EXP);
		glFogfv(GL_FOG_COLOR, fogColor);
		glFogf(GL_FOG_DENSITY, 0.3);
		glEnable(GL_FOG);
	
		/* Cull face */
		glCullFace(GL_FRONT);
		glEnable(GL_CULL_FACE);
	}
	
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	return True;
}


/* Standard reshape function */
void
reshape_atunnels(ModeInfo *mi, int width, int height)
{
	float a;

	glViewport((MI_WIDTH(mi) - width) / 2, (MI_HEIGHT(mi) - height) / 2, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	a = (float)width/(float)height;
	glFrustum(-0.1*a, 0.1*a, -0.1, 0.1, 0.1, 10);
	glMatrixMode(GL_MODELVIEW);
}

/* draw the screensaver once */
void draw_atunnels(ModeInfo * mi)
{
  	atunnelsstruct *gp;
  	Display    *display = MI_DISPLAY(mi);
  	Window      window = MI_WINDOW(mi);

	if (Atunnels == NULL)
		return;
  	gp = &Atunnels[MI_SCREEN(mi)];
  	if (!gp->glx_context)
		return;

  	glXMakeCurrent(display, window, *(gp->glx_context));

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glLoadIdentity();

	DrawTunnel(do_texture, do_light, texture);
	SplashScreen(do_wire, do_texture, do_light);

	glFlush();  
  	if (MI_IS_FPS(mi)) do_fps (mi);
  	glXSwapBuffers(display, window);

}

void
refresh_atunnels(ModeInfo * mi)
{
}


/* xscreensaver initialization routine */
void init_atunnels(ModeInfo * mi)
{
  int screen = MI_SCREEN(mi);
  int size = MI_SIZE(mi);
  atunnelsstruct *gp;

  if (Atunnels == NULL) {
	if ((Atunnels = (atunnelsstruct *) calloc(MI_NUM_SCREENS(mi), sizeof (atunnelsstruct))) == NULL)
	  return;
  }
  gp = &Atunnels[screen];

  /* Viewport is specified size if size >= MINSIZE && size < screensize */
  if (size <= 1) {
      gp->WIDTH = MI_WIDTH(mi);
      gp->HEIGHT = MI_HEIGHT(mi);
  } else if (size < MINSIZE) {
      gp->WIDTH = MINSIZE;
      gp->HEIGHT = MINSIZE;
  } else {
      gp->WIDTH = (size > MI_WIDTH(mi)) ? MI_WIDTH(mi) : size;
      gp->HEIGHT = (size > MI_HEIGHT(mi)) ? MI_HEIGHT(mi) : size;
  }

  if ((gp->glx_context = init_GL(mi)) != NULL) {
	reshape_atunnels(mi, gp->WIDTH, gp->HEIGHT);
	glDrawBuffer(GL_BACK);
	Init(mi);
  } else {
	MI_CLEARWINDOW(mi);
  }

}

/* all sorts of nice cleanup code should go here! */
void release_atunnels(ModeInfo * mi)
{
  if (Atunnels != NULL) {
#if 0
  	int screen;
	for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
	  atunnelsstruct *gp = &Atunnels[screen];
	   FreeTunnel(gp->path);
	}
#endif
	free(Atunnels);
	Atunnels = NULL;
  }
  FreeAllGL(mi);
}
#endif

void change_atunnels(ModeInfo * mi)
{
  	atunnelsstruct *gp;

	if (Atunnels == NULL)
		return;
  	gp = &Atunnels[MI_SCREEN(mi)];
	if (!gp->glx_context)
		return;

	glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(gp->glx_context));
	Init(mi);
}

