#ifdef USE_LIGHTS

#define LIGHT_OMNI 0
#define LIGHT_OMNINORMAL 1
#define LIGHT_SPOT 2
#define LIGHT_SPOTNORMAL 3
#define LIGHT_SHADOW 4

// lights
uniform int numLights;
uniform vec3 lightPos[MAX_LIGHTS];
uniform vec3 lightColor[MAX_LIGHTS];
uniform vec3 lightParams[MAX_LIGHTS];
uniform mat3 lightDir[MAX_LIGHTS];

#ifdef SHADOW_BOX
// shadow box
uniform bool modelShadow;
uniform vec3 shadowColor;
#endif  // SHADOW_BOX

#ifdef SHADOW_BOX
void ProcessLight(vec3 lightPos, vec3 lightColor, vec3 lightParams, mat3 lightDir, inout bool inShadow)
#else
void ProcessLight(vec3 lightPos, vec3 lightColor, vec3 lightParams, mat3 lightDir)
#endif  // SHADOW_BOX
{
  int type = int(lightParams.x);
  vec3 delta = lightPos - inPosition.xyz;

  #ifdef SHADOW_BOX
  // special case for shadow box
  if (type == LIGHT_SHADOW) {
    vec3 dist = delta * lightDir;
    vec3 lightMin = -lightColor;
    vec3 lightMax = lightColor;
    if (all(greaterThan(dist, lightMin)) && 
        all(lessThan(dist, lightMax))) {
      inShadow = true;
    }

  // other lights
  } else
  #endif  // SHADOW_BOX
  {
    float reach = lightParams.y;
    float dist = dot(delta, delta);
    if (dist < reach) {
      float scale = 1.0 - (dist / reach);
      dist = sqrt(dist);

      // calc angle from normal if needed
      if (type == LIGHT_OMNINORMAL || type == LIGHT_SPOTNORMAL) {
        float ang = dot(delta, inNormal);
        ang = clamp(ang / dist, 0.0, 1.0);
        scale *= ang;
      }

      // calc cone adjustment if needed
      if (type == LIGHT_SPOT || type == LIGHT_SPOTNORMAL) {
        float conemul = lightParams.z;
        float cone = -dot(lightDir[2], delta) / dist - 1.0;
        cone = cone * conemul + 1.0;
        cone = clamp(cone, 0.0, 1.0);
        scale *= cone;
      }

      varColor.rgb += mix(vec3(0.0), lightColor, scale);
    }
  }
}

void ProcessLights(void)
{
  #ifdef SHADOW_BOX
  bool inShadow = modelShadow;
  #endif  // SHADOW_BOX

  for (int i = 0; i < MAX_LIGHTS; ++i) {
    if (i < numLights) {
      #ifdef SHADOW_BOX
      ProcessLight(lightPos[i], lightColor[i], lightParams[i], lightDir[i], inShadow);
      #else
      ProcessLight(lightPos[i], lightColor[i], lightParams[i], lightDir[i]);
      #endif  // SHADOW_BOX
    }
  }

  #ifdef SHADOW_BOX
  if (inShadow) {
    varColor.rgb += shadowColor;
  }
  #endif  // SHADOW_BOX
}
#endif  // USE_LIGHTS
