/* Light is an abstract class which represents a light source in a scene. */

/* Copyright (C) 1999-2000 by Peter Eastman

   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. */

package artofillusion.object;

import artofillusion.*;
import artofillusion.animation.*;
import artofillusion.math.*;
import java.io.*;

public abstract class Light extends Object3D
{
  RGBColor color;
  float intensity, decayRate;
  boolean ambient;

  public Light()
  {
    super();
  }

  public Light(DataInputStream in, Scene theScene) throws IOException, InvalidObjectException
  {
    super(in, theScene);
  }
  
  // Set the parameters for this light.

  public void setParameters(RGBColor theColor, float theIntensity, boolean isAmbient, float decay)
  {
    color = theColor;
    intensity = theIntensity;
    ambient = isAmbient;
    decayRate = decay;
  }
  
  public RGBColor getColor()
  {
    return color;
  }

  public float getIntensity()
  {
    return intensity;
  }
  
  // Get the attenuated light at a given distance from the light source.

  public void getLight(RGBColor light, float distance)
  {
    double d = distance*decayRate;
    
    light.copy(color);
    light.scale(intensity/(1.0f+d+d*d));
  }
  
  public boolean isAmbient()
  {
    return ambient;
  }
  
  public float getDecayRate()
  {
    return decayRate;
  }
  
  
  /** Inner class to derive other light keyframes from. */
  
  public static abstract class LightKeyframe implements Keyframe
  {
    public RGBColor color;
    public float intensity, decayRate;
    
    public LightKeyframe(RGBColor color, float intensity, float decayRate)
    {
      this.color = color.duplicate();
      this.intensity = intensity;
      this.decayRate = decayRate;
    }
    
    /* Create a duplicate of this keyframe. */
  
    public abstract Keyframe duplicate();
    
    /* Create a duplicate of this keyframe for a (possibly different) object. */
  
    public Keyframe duplicate(Object owner)
    {
      return duplicate();
    }
  
    /* Get the list of graphable values for this keyframe. */
  
    public double [] getGraphValues()
    {
      return new double [] {intensity, decayRate};
    }
  
    /* Set the list of graphable values for this keyframe. */
  
    public void setGraphValues(double values[])
    {
      intensity = (float) values[0];
      decayRate = (float) values[1];
    }
    
    /** Return number of graphable values. */
    // TODO(MB) Add method to Keyframe?
    public int getGraphValueCount()
    {
      return 2;
    }

    /* These methods return a new Keyframe which is a weighted average of this one and one,
       two, or three others. */
  
    public Keyframe blend(Keyframe o2, double weight1, double weight2)
    {
       return blend(o2, null, null, weight1, weight2, 0.0, 0.0);
    }

    public Keyframe blend(Keyframe o2, Keyframe o3, double weight1, double weight2, double weight3)
    {
      return blend(o2, o3, null, weight1, weight2, weight3, 0.0);
    }

    public Keyframe blend(Keyframe o2, Keyframe o3, Keyframe o4, double weight1, double weight2, double weight3, double weight4)
    {
      LightKeyframe result = (LightKeyframe) duplicate();
      result.blendInplace(o2, o3, o4, weight1, weight2, weight3, weight4);
      return result;
    }
    
    /** Blend this Keyframe with up to three others and modify it inplace to
      contain the result.
      o2, o3 and o4 are the Keyframes where o3 or o4 may be null
     (and are ignored then). If o3 is null, o4 is ignored!
    */
    protected void blendInplace(Keyframe o2, Keyframe o3, Keyframe o4, double weight1, double weight2, double weight3, double weight4)
    {
      LightKeyframe k2 = (LightKeyframe) o2, k3 = (LightKeyframe) o3, k4 = (LightKeyframe) o4;
      // RGBColor c = color.duplicate();
      RGBColor c2 = k2.color;
      RGBColor c3 = (k3 == null) ? null : k3.color;
      RGBColor c4 = (k4 == null) ? null : k4.color;  
      color.blendInplace(c2, c3, c4, weight1, weight2, weight3, weight4);
      
      double intensity = weight1*this.intensity + weight2*k2.intensity;
      double decayRate = weight1*this.decayRate + weight2*k2.decayRate;
      
      if (k3 != null)
      {
        intensity += weight3*k3.intensity;
        decayRate += weight3*k3.decayRate;

        if (k4 != null)
        {
          intensity += weight4*k4.intensity;
          decayRate += weight4*k4.decayRate;
        }
      }
      
      this.intensity = (float) intensity;
      this.decayRate = (float) decayRate;
    }
    
    /** Determine whether this keyframe is identical to another one. */
  
    public boolean equals(Keyframe k)
    {
      if (!(k instanceof LightKeyframe))
        return false;
      LightKeyframe key = (LightKeyframe) k;
      return (key.color.equals(color) && key.intensity == intensity && key.decayRate == decayRate);
    }
  
    /* Write out a representation of this keyframe to a stream. */
  
    public void writeToStream(DataOutputStream out) throws IOException
    {
      color.writeToFile(out);
      out.writeFloat(intensity);
      out.writeFloat(decayRate);
    }

    /* Reconstructs the keyframe from its serialized representation. */
    
    // TODO(MB) Solve problem with stream construction
//    public LightKeyframe(DataInputStream in, Object parent) throws IOException
//    {
//      this(new RGBColor(in), in.readFloat(), in.readFloat());
//    }
  }
  
}