/* Lighting control functions. */

#include <stdlib.h>
#include <math.h>
#include "polygon.h"

/* Ambient lighting intensity */
ModelIntensity AmbientIntensity;

/* 1 if ambient shading on, 0 if off */
int AmbientOn;

/* Spotlight direction vectors in world and view coordinates. Spotlights are
   defined in world coordinates, but used in view coordinates.  Spotlight
   directions are reversed in view coordinates, to facilitate incident angle
   calculations via the dot product */
Point3 SpotDirectionWorld[MAX_SPOTS];
Point3 SpotDirectionView[MAX_SPOTS];

/* Spotlight intensities */
ModelIntensity SpotIntensity[MAX_SPOTS];

/* Spotlight on/off statuses, defaulting to off */
int SpotOn[MAX_SPOTS] = {0, 0, 0};

/* Sets the ambient lighting level. */
void SetAmbientIntensity(ModelIntensity * Intensity)
{
   AmbientIntensity = *Intensity;
}

/* Returns the ambient lighting level. */
ModelIntensity * GetAmbientIntensity()
{
   return(&AmbientIntensity);
}

/* Turns ambient shading on */
void TurnAmbientOn()
{
   AmbientOn = 1;
}

/* Turns ambient shading off */
void TurnAmbientOff()
{
   AmbientOn = 0;
}

/* Returns the ambient on/off status (1=on, 0=off). */
int GetAmbientState()
{
   return(AmbientOn);
}

/* Sets the unit vector defining a spotlight's direction. Spotlights are
   considered to be infinitely far away, so the light rays are parallel and
   do not vary in angle anywhere in the scene. Vectors are reversed in view
   space to facilitate angle calculations. The passed-in vector does not
   have to be a unit vector. */
void SetSpotDirection(int SpotNumber, Point3 * SpotVector)
{
   double Xlen, Ylen, Zlen, Length;

   if ((SpotNumber < 0) || (SpotNumber >= MAX_SPOTS))
      return;  /* bad spot number */

   /* Convert the direction vector to a unit vector, so we can do fast
      shading calculations. First, calculate the length of the vector */
   Xlen = FIXED_TO_DOUBLE(SpotVector->X);
   Ylen = FIXED_TO_DOUBLE(SpotVector->Y);
   Zlen = FIXED_TO_DOUBLE(SpotVector->Z);
   Length = sqrt(Xlen*Xlen + Ylen*Ylen + Zlen*Zlen);

   /* Scale it to a unit vector and offset it from the start point. Flip the
      vector's direction for use in shading calculations */
   SpotDirectionWorld[SpotNumber].X = DOUBLE_TO_FIXED(Xlen/Length);
   SpotDirectionWorld[SpotNumber].Y = DOUBLE_TO_FIXED(Ylen/Length);
   SpotDirectionWorld[SpotNumber].Z = DOUBLE_TO_FIXED(Zlen/Length);

   /* Finally, convert the vector into view space, where we'll actually apply
      the shading */
   XformVec(WorldViewXform, (Fixedpoint *) &SpotDirectionWorld[SpotNumber],
         (Fixedpoint *) &SpotDirectionView[SpotNumber]);
   /* Flip the spot direction vector in view space, for ease of shading
      calculations */
   SpotDirectionView[SpotNumber].X = -SpotDirectionView[SpotNumber].X;
   SpotDirectionView[SpotNumber].Y = -SpotDirectionView[SpotNumber].Y;
   SpotDirectionView[SpotNumber].Z = -SpotDirectionView[SpotNumber].Z;
}

/* Sets the intensity of a spotlight. */
void SetSpotIntensity(int SpotNumber, ModelIntensity * Intensity)
{
   ModelIntensity * SpotIntensityPtr;

   if ((SpotNumber < 0) || (SpotNumber >= MAX_SPOTS))
      return;  /* bad spot number */

   SpotIntensity[SpotNumber] = *Intensity;
}

/* Turns on the specified spotlight. */
void TurnSpotOn(int SpotNumber)
{
   if ((SpotNumber < 0) || (SpotNumber >= MAX_SPOTS))
      return;  /* bad spot number */

   SpotOn[SpotNumber] = 1;
}

/* Turns off the specified spotlight. */
void TurnSpotOff(int SpotNumber)
{
   if ((SpotNumber < 0) || (SpotNumber >= MAX_SPOTS))
      return;  /* bad spot number */

   SpotOn[SpotNumber] = 0;
}

/* Returns a spot's unit direction vector in world space. */
Point3 * GetSpotDirection(int SpotNumber)
{
   if ((SpotNumber < 0) || (SpotNumber >= MAX_SPOTS))
      return(NULL);  /* bad spot number */
   return(&SpotDirectionWorld[SpotNumber]);
}

/* Returns a spot's intensity. */
ModelIntensity * GetSpotIntensity(int SpotNumber)
{
   if ((SpotNumber < 0) || (SpotNumber >= MAX_SPOTS))
      return(NULL);  /* bad spot number */
   return(&SpotIntensity[SpotNumber]);
}

/* Returns a spot's on/off status (1=on, 0=off). */
int GetSpotState(int SpotNumber)
{
   if ((SpotNumber < 0) || (SpotNumber >= MAX_SPOTS))
      return(0);  /* bad spot number */
   return(SpotOn[SpotNumber]);
}

