/// Evolver Copyright 2000, Morgan McGuire
/// matrix@alum.mit.edu   http://graphics3d.com/matrix
import java.awt.*;

public abstract class ImageOperator extends ZeroArgumentOperator implements ELOperator 
{
  /**
   * Underlying image
   */
  protected ELMatrix image = null;
  protected ELMatrix image128 = null;

  protected String filename;

  synchronized protected void waitForImage()
  {
    if (image == null) {
      System.out.print("(Loading " + filename + ")");
      image = new ELMatrix(filename);
      create128();
    }
  }

  public void apply(ELMatrix argument[], ELMatrix out, ELEnvironment environment)
  {
    // Block while the image is loading
    waitForImage();

    int width = out.getWidth();
    int height = out.getHeight();

    if ((width == 128) && (height == 128)) {
      fastApply(argument, out, environment);
      return;
    }

    checkArguments(argument);
    
    // For extra performance, pull the arrays into locals so we avoid
    // excess dereferences.
    float red[] = out.red;
    float grn[] = out.grn;
    float blu[] = out.blu;

    float xspan = width;
    float yspan = height;

    // Image dimensions (cache for performance)
    int w = image.getWidth();
    int h = image.getHeight();

    int i = width * height - 1;
    for(int y = height - 1; y >= 0; y--) {
      for(int x = width - 1; x >= 0; x--) {
        int nx = (int)((x * w) / xspan);
        int ny = (int)((y * h) / yspan);
        int n = nx + w * ny;

        red[i] = image.red[n];
        grn[i] = image.grn[n];
        blu[i] = image.blu[n];
        i--;
      }      
    }
  }

  public void fastApply(ELMatrix argument[], ELMatrix out, ELEnvironment environment)
  {
    // Block while the image is loading
    waitForImage();

    int width = out.getWidth();
    int height = out.getHeight();

    if (!((width == 128) && (height == 128))) {
      throw new Error("Not available for optimization");
    }

    int length = width * height;
    for(int c = 0; c < 3; c++) {
      System.arraycopy(image128.channel[c], 0, out.channel[c], 0, length);
    }
  }

  public void paintIcon(Graphics2D graphics, int x, int y, int width, int height)
  {
      waitForImage();
      image.drawImage(graphics, x, y, width, height);
  }


  protected void create128() 
  {
      image128 = ELMatrix.alloc(128, 128);

      ELMatrix out = image128;
      int width = out.getWidth();
      int height = out.getHeight();
      
      // For extra performance, pull the arrays into locals so we avoid
      // excess dereferences.
      float red[] = out.red;
      float grn[] = out.grn;
      float blu[] = out.blu;
      
      float xspan = width;
      float yspan = height;

      // Image dimensions (cache for performance)
      int w = image.getWidth();
      int h = image.getHeight();

      int i = width * height - 1;
      for(int y = height - 1; y >= 0; y--) {
        for(int x = width - 1; x >= 0; x--) {
          int nx = (int)((x * w) / xspan);
          int ny = (int)((y * h) / yspan);
          int n = nx + w * ny;
          
          red[i] = image.red[n];
          grn[i] = image.grn[n];
          blu[i] = image.blu[n];
          i--;
        }      
      }
  }
}
