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

public class Distort extends TwoArgumentOperator implements ELOperator
{
  public void apply(ELMatrix argument[], ELMatrix out, ELEnvironment environment)
  {
    checkArguments(argument);

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

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

    // 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;

    ELMatrix in = argument[0];
    float inRed[] = in.red;
    float inGrn[] = in.grn;
    float inBlu[] = in.blu;

    // Displacement amounts
    ELMatrix dis = argument[1];
    float disX[] = dis.red;
    float disY[] = dis.grn;
    float disScl[] = dis.blu;
    
    double s = Math.max(width, height) / 3;

    if ((in.getWidth() != width) || (in.getHeight() != height)) {
      throw new Error("in width = " + in.getWidth() + " out width = " + width + 
                      " in height = " + in.getHeight() + " out height = " + height);
    }

    for(int y = height - 1; y >= 0; y--) {
      for(int x = width - 1; x >= 0; x--) {
        int i = x + (y * width);

        float scale = disScl[i];
        int xx = (int)(disX[i] * scale * s + x + width * 4) % width;
        int yy = (int)(disY[i] * scale * s + y + height * 4) % height;
      
        int j = xx + (yy * width);

        red[i] = inRed[j];
        grn[i] = inGrn[j];
        blu[i] = inBlu[j];
      }
    }
  }

  public void fastApply(ELMatrix argument[], ELMatrix out, ELEnvironment environment)
  {
    checkArguments(argument);

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

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

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

    ELMatrix in = argument[0];
    final float inRed[] = in.red;
    final float inGrn[] = in.grn;
    final float inBlu[] = in.blu;

    // Displacement amounts
    ELMatrix dis = argument[1];
    final float disX[] = dis.red;
    final float disY[] = dis.grn;
    final float disScl[] = dis.blu;
    
    for(int y = height - 1; y >= 0; y--) {
      int yPlus512 = y + 512;
      int yTimesWidth = y * width;
      for(int x = width - 1; x >= 0; x--) {
        int i = x + yTimesWidth;

        float scale = disScl[i] * 42;        
        int xx = (int)(disX[i] * scale + x + 512) & 127;
        int yy = (int)(disY[i] * scale + yPlus512) & 127;
      
        int j = xx + (yy << 7);

        red[i] = inRed[j];
        grn[i] = inGrn[j];
        blu[i] = inBlu[j];
      }
    }
  }

  public String name()
  {
    return "Distort";
  }

  public String description()
  {
    return "Displaces the pixels of the first argument by the " +
      "values of the second (as if refracting through glass).";
  }

  protected String iconString()
  {
    return "Dis";
  }
}
