#include <gl.h>
#include <stdio.h>
#include <stdlib.h>
#include <image.h>
#include "globals.h"
#include "states.h"
#include "util.h"
#include "lights.h"

/* creates and binds an ambient light with rgb color r, g, b
   and attenuation factors of k0, k1, and k2
   also specifies two sided lighting calculations with twoside */
void make_ambient_light(float r, float g, float b,
			float k0, float k1, float k2, float twoside)
{
  float props[14];

  /* AMBIENT LIGHT */
  props[0] = AMBIENT;
  props[1] = r; /* red */
  props[2] = g; /* green */
  props[3] = b; /* blue */

  props[4] = ATTENUATION;
  props[5] = k0;
  props[6] = k1;

  props[7] = ATTENUATION2;
  props[8] = k2;

  props[9] = LOCALVIEWER;
  props[10] = 0.0;

  props[11] = TWOSIDE;
  props[12] = twoside;

  props[13] = LMNULL;

  lmdef(DEFLMODEL, 1, 14, props);
  lmbind(LMODEL, 1);
}

/* creates and binds a directional light with rgb color
   r, g, b, and ambient rgb color ar, ag, ab, at a direction
   (x, y, z)
   NOTE: gl does light direction in the opposite sense, so I
         change direction to negative, so the direction specified
	 is exactly the direction in world coordinates */
void make_directional_light(float r, float g, float b,
			    float ar, float ag, float ab,
			    float x, float y, float z)
{
  float props[14];

  if (gstate.num_lights > MAXLIGHTS)
    return;
  else
    gstate.num_lights++;

  /* DIRECTIONAL LIGHT */
  props[0] = AMBIENT;
  props[1] = ar;
  props[2] = ag;
  props[3] = ab;
  
  props[4] = LCOLOR;
  props[5] = r; /* red */
  props[6] = g; /* green */
  props[7] = b; /* blue */

  props[8] = POSITION;
  props[9] = -x; /* x */
  props[10] = -y; /* y */
  props[11] = -z; /* z */
  props[12] = 0.0; /* at infinity */

  props[13] = LMNULL;

  lmdef(DEFLIGHT, gstate.num_lights, 14, props);
  lmbind(LIGHT0+gstate.num_lights, gstate.num_lights);
}

/* creates and binds a point light of rgb color r, g, b
   and ambient color ar, ag, ab, at point (x, y, z) */
void make_point_light(float r, float g, float b,
		      float ar, float ag, float ab,
		      float x, float y, float z)
{
  float props[14];

  if (gstate.num_lights > MAXLIGHTS)
    return;
  else
    gstate.num_lights++;

  /* POINT LIGHTS */
  props[0] = AMBIENT;
  props[1] = ar;
  props[2] = ag;
  props[3] = ab;

  props[4] = LCOLOR;
  props[5] = r; /* red */
  props[6] = g; /* green */
  props[7] = b; /* blue */

  props[8] = POSITION;
  props[9] = x; /* x */
  props[10] = y; /* y */
  props[11] = z; /* z */
  props[12] = 1.0; /* local */

  props[13] = LMNULL;

  lmdef(DEFLIGHT, gstate.num_lights, 14, props);
  lmbind(LIGHT0+gstate.num_lights, gstate.num_lights);
}

/* creates and binds a spot light with rgb color r, g, b, and
   ambient color ar, ag, ab, at position (posx, posy, posz)
   going at a direction (dirx, diry, dirz) with an exponent factor
   exp and angle of ang (angle is the angle of the spot light emitted)
   NOTE: gl does light direction in the opposite sense, so I
         change direction to negative, so the direction specified
	 is exactly the direction in world coordinates */
void make_spot_light(float r, float g, float b,
		     float ar, float ag, float ab,
		     float posx, float posy, float posz,
		     float dirx, float diry, float dirz,
		     float exp, float ang)
{
  float props[21];

  if (gstate.num_lights > MAXLIGHTS)
    return;
  else
    gstate.num_lights++;

  /* SPOT LIGHTS */
  props[0] = AMBIENT;
  props[1] = ar;
  props[2] = ag;
  props[3] = ab;
  
  props[4] = LCOLOR;
  props[5] = r; /* red */
  props[6] = g; /* green */
  props[7] = b; /* blue */

  props[8] = POSITION;
  props[9] = posx; /* x */
  props[10] = posy; /* y */
  props[11] = posz; /* z */
  props[12] = 1.0;

  props[13] = SPOTDIRECTION;
  props[14] = dirx; /* x */
  props[15] = diry; /* y */
  props[16] = dirz; /* z */

  props[17] = SPOTLIGHT;
  props[18] = exp; /* exp: 0.0 - 128.0 */
  props[19] = ang;  /* ang: 0.0 - 90.0 */

  props[20] = LMNULL;

  lmdef(DEFLIGHT, gstate.num_lights, 21, props);
  lmbind(LIGHT0+gstate.num_lights, gstate.num_lights);
}

/* creates a material with the given parameters */
void make_material(int id, float r, float g, float b,
		   float ambient, float diffuse, float specular,
		   float shininess, float emission, float alpha)
{
  float props[30];

  props[0] = ALPHA; /* 0.0 - 1.0; transparency */
  props[1] = alpha;

  props[2] = AMBIENT; /* 0.0 - 1.0 */
  props[3] = ambient * r;
  props[4] = ambient * g;
  props[5] = ambient * b;

  props[6] = COLORINDEXES; /* 0.0 - 1.0 */
  props[7] = ambient;
  props[8] = diffuse;
  props[9] = specular;

  props[10] = DIFFUSE; /* 0.0 - 1.0 */
  props[11] = diffuse * r;
  props[12] = diffuse * g;
  props[13] = diffuse * b;

  props[14] = EMISSION; /* 0.0 - 1.0 */
  props[15] = emission * r;
  props[16] = emission * g;
  props[17] = emission * b;

  props[18] = SHININESS;
  props[19] = shininess; /* 0.0 - 128.0 */

  props[20]  = SPECULAR; /* 0.0 - 1.0 */
  props[21]  = specular * r;
  props[22] = specular * g;
  props[23] = specular * b;

  props[24] = LMNULL;

  lmdef(DEFMATERIAL, id, 25, props);
}

void load_texture(int id, char *texfile)
{
  int xsize, ysize;
  unsigned long *data;
  float texprops[] = {TX_MINFILTER, TX_POINT, TX_MAGFILTER, TX_POINT,
			       TX_WRAP, TX_REPEAT, TX_NULL};
  sizeofimage(texfile, &xsize, &ysize);
  data = longimagedata(texfile);
  texdef2d(id, 4, xsize, ysize, data, 0, texprops);
}

/* creates a few texture arrays by reading a few rgb files */
void define_texture()
{
  float tevprops[] = {TV_MODULATE, TV_NULL};

  if (getgdesc(GD_TEXTURE) == 0) {
    fprintf(stderr, "texture mapping not available on this machine\n");
    return;
  }

  /* texture environment */
  tevdef(1, 0, tevprops);
}

/* defines a fog scene
   if density is zero, it uses the start and end of fog
   to interpolate the fog density at a distance from the
   viewer (simulates depthcue)
   otherwise it has a constant density of fog
   the fog has rgb color r, g, b */
void define_fog(float density, float start_fog, float end_fog,
		float r, float g, float b)
{
  float params[5];

  if (density == 0.0)
    {
      params[0] = start_fog;
      params[1] = end_fog;
      params[2] = r;
      params[3] = g;
      params[4] = b;

      fogvertex(FG_VTX_LIN, params);
    }
  else
    {
      params[0] = density;
      params[1] = r;
      params[2] = g;
      params[3] = b;
      
      fogvertex(FG_VTX_EXP2, params);
    }
}

long get_bytes(FILE *imgfile)
{
  long pixel_bytes;
  int i;
  char c;
  
  pixel_bytes = 0;
  for (i=0; i<4; i++)
    {
      c = fgetc(imgfile);
      pixel_bytes += ((c<<24)-(8*i));
      printf("%x ", c);
    }
  return pixel_bytes;
}