package ip.raul;

import ip.gui.frames.AnimateFrame;
import ip.gui.dialog.ExpandoLog;
import ip.gui.frames.XformFrame;
import ip.gui.frames.ImageFrame;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MorphLog extends XformFrame
    implements ActionListener {

  MenuBar mb = new MenuBar();
  Menu SettingsMenu = new Menu("Settings");

  MenuItem props_mi = addMenuItem(SettingsMenu, "[p]roperties");
  MenuItem default_mi = addMenuItem(SettingsMenu, "[d]efault images");
  MenuItem morph_mi = addMenuItem(SettingsMenu, "[m]orph");

  short rm[][] = new short[0][0];
  short gm[][] = new short[0][0];
  short bm[][] = new short[0][0];

  short rc[][] = new short[0][0];
  short gc[][] = new short[0][0];
  short bc[][] = new short[0][0];
  Image img = null;
  Polygon p = new Polygon();
  boolean doMorph = false;
  GridImage SourceImage = null;
  GridImage StopImage = null;
  int w = 128;
  int h = 128;
  int nPoints = 25;
  int gridX = 5;
  int gridY = 5;
  int NumberOfFrames = 50;
  boolean busy = false;
  String prompts[] = {
    "Grid X Elements=",
    "Grid Y Elements=",
    "Number Of Frames="};
  String defaults[] = {
    "" + gridX,
    "" + gridY,
    "" + NumberOfFrames};
  ip.gui.dialog.ExpandoLog el = null;


  public void actionPerformed(ActionEvent e) {

    if (el != null) {
      String s[] = el.getUserInput();
      gridX = Integer.parseInt(s[0]);
      gridY = Integer.parseInt(s[1]);
      NumberOfFrames = Integer.parseInt(s[2]);
      SourceImage.setGrid(gridX, gridY);
      StopImage.setGrid(gridX, gridY);
      el = null;
    }

    if (match(e, morph_mi)) {
      Morph();
      return;
    }

    if (match(e, props_mi)) {
      Properties();
      return;
    }

    if (match(e, default_mi)) {
      default1();
      return;
    }

    super.actionPerformed(e);
  }

  MorphLog(String title) {
    super(title);
    init();
    mb.add(SettingsMenu);
    setMenuBar(mb);
    SourceImage = new GridImage("Source Image", Color.red);
    StopImage = new GridImage("Stop Image", Color.green);
    repaint();
  }

  private void init() {
  }

  public void Morph() {
    int maxW = 0;
    int maxH = 0;
    int sourceX, sourceY;
    int stopX, stopY;
    AnimateFrame af = new AnimateFrame();
    double k = 0;
      if (getImageWidth() > maxW) maxW = getImageWidth();
      if (getImageHeight() > maxH) maxH = getImageHeight();
      if (getImageWidth() > maxW) maxW = getImageWidth();
      if (getImageHeight() > maxH) maxH = getImageHeight();
    setSize(maxW, maxH);
    repaint();
    rm = new short[maxW][maxH];
    gm = new short[maxW][maxH];
    bm = new short[maxW][maxH];
    rc = new short[maxW][maxH];
    gc = new short[maxW][maxH];
    bc = new short[maxW][maxH];
      setImageHeight(maxH);
      setImageWidth(maxW);
      setR(rm);
    setG(gm);
    setB(bm);
    short2Image();
    img = getImage();

    nPoints = (gridX + 1) * (gridY + 1);
    p = new Polygon();
    for (int j = 0; j < nPoints; j++) {
      p.addPoint(SourceImage.p.xpoints[j],
                 SourceImage.p.ypoints[j]);
    }

    String files = "seq";//getFileNames();
    busy = true;
    for (int i = 0; i < NumberOfFrames; i++) {
      k = (double) ((double) i / (double) (NumberOfFrames - 1));
      for (int j = 0; j < nPoints; j++) {
        sourceX = SourceImage.p.xpoints[j];
        sourceY = SourceImage.p.ypoints[j];
        stopX = StopImage.p.xpoints[j];
        stopY = StopImage.p.ypoints[j];
        p.xpoints[j] = (int) linearY(sourceX, stopX, k);
        p.ypoints[j] = (int) linearY(sourceY, stopY, k);
      }
      applyBilinearMorph(k);
      setR(rm);
      setG(gm);
      setB(bm);
      medianCut(256);

      img = getImage();
      af.addImage(img);
      System.out.println((NumberOfFrames - i - 1) + " steps left...");

      int outFileNumber = i;
//          String outFileName = "d:\\cadgfx\\pics\\morph\\"+outFileNumber+".GIF";
//          System.out.println("writing:"+outFileName);
//          saveAsGif(outFileName);

    }
    af.saveImages();
      af.setSize(getImageWidth(), getImageHeight());
    af.setVisible(true);
  }


  public void applyBilinearMorph(double k) {
    Point s0,s1,s2,s3,m0,m1,m2,m3,d0,d1,d2,d3;
    for (int i = 0; i < gridY; i++)
      for (int j = 0; j < gridX; j++) {
        s0 = new Point(SourceImage.p.xpoints[j + i * (gridX + 1)], SourceImage.p.ypoints[j + i * (gridX + 1)]);
        s1 = new Point(SourceImage.p.xpoints[j + 1 + i * (gridX + 1)], SourceImage.p.ypoints[j + 1 + i * (gridX + 1)]);
        s2 = new Point(SourceImage.p.xpoints[j + 1 + (i + 1) * (gridX + 1)], SourceImage.p.ypoints[j + 1 + (i + 1) * (gridX + 1)]);
        s3 = new Point(SourceImage.p.xpoints[j + (i + 1) * (gridX + 1)], SourceImage.p.ypoints[j + (i + 1) * (gridX + 1)]);
        m0 = new Point(p.xpoints[j + i * (gridX + 1)], p.ypoints[j + i * (gridX + 1)]);
        m1 = new Point(p.xpoints[j + 1 + i * (gridX + 1)], p.ypoints[j + 1 + i * (gridX + 1)]);
        m2 = new Point(p.xpoints[j + 1 + (i + 1) * (gridX + 1)], p.ypoints[j + 1 + (i + 1) * (gridX + 1)]);
        m3 = new Point(p.xpoints[j + (i + 1) * (gridX + 1)], p.ypoints[j + (i + 1) * (gridX + 1)]);
        d0 = new Point(StopImage.p.xpoints[j + i * (gridX + 1)], StopImage.p.ypoints[j + i * (gridX + 1)]);
        d1 = new Point(StopImage.p.xpoints[j + 1 + i * (gridX + 1)], StopImage.p.ypoints[j + 1 + i * (gridX + 1)]);
        d2 = new Point(StopImage.p.xpoints[j + 1 + (i + 1) * (gridX + 1)], StopImage.p.ypoints[j + 1 + (i + 1) * (gridX + 1)]);
        d3 = new Point(StopImage.p.xpoints[j + (i + 1) * (gridX + 1)], StopImage.p.ypoints[j + (i + 1) * (gridX + 1)]);


        solveMorph(s0, s1, s2, s3, m0, m1, m2, m3, d0, d1, d2, d3, k);
      }
  }

  int Dist(Point a, Point b) {
    return (int) (Math.sqrt(((b.x - a.x) * (b.x - a.x)) + ((b.y - a.y) * (b.y - a.y))));
  }

  public void solveMorph(Point s0, Point s1, Point s2, Point s3, Point m0, Point m1, Point m2, Point m3, Point d0, Point d1, Point d2, Point d3, double k1) {
    double xStart1, xEnd1,yStart1, yEnd1;
    double xStart2, xEnd2,yStart2, yEnd2;
    double xStart3, xEnd3,yStart3, yEnd3;
    double k = 0;
    short rS, gS, bS, rD, gD, bD;

    int MAX = 0;
    int MAX1 = 0;
    int xM, yM, xS, yS, xD, yD;

    MAX = Dist(d0, d1);
    MAX1 = Dist(d1, d2);
    if (MAX1 > MAX) MAX = MAX1;
    MAX1 = Dist(d2, d3);
    if (MAX1 > MAX) MAX = MAX1;
    MAX1 = Dist(d3, d0);
    if (MAX1 > MAX) MAX = MAX1;

    MAX1 = Dist(s0, s1);
    if (MAX1 > MAX) MAX = MAX1;
    MAX1 = Dist(s1, s2);
    if (MAX1 > MAX) MAX = MAX1;
    MAX1 = Dist(s2, s3);
    if (MAX1 > MAX) MAX = MAX1;
    MAX1 = Dist(s3, s0);
    if (MAX1 > MAX) MAX = MAX1;

    MAX1 = Dist(m0, m1);
    if (MAX1 > MAX) MAX = MAX1;
    MAX1 = Dist(m1, m2);
    if (MAX1 > MAX) MAX = MAX1;
    MAX1 = Dist(m2, m3);
    if (MAX1 > MAX) MAX = MAX1;
    MAX1 = Dist(m3, m0);
    if (MAX1 > MAX) MAX = MAX1;

    MAX = MAX + 1;

    for (int i = 0; i <= MAX; i++) {
      k = (double) ((double) i / (double) MAX);
      xStart1 = linearY(d0.x, d3.x, k);
      yStart1 = linearY(d0.y, d3.y, k);
      xEnd1 = linearY(d1.x, d2.x, k);
      yEnd1 = linearY(d1.y, d2.y, k);
      xStart2 = linearY(s0.x, s3.x, k);
      yStart2 = linearY(s0.y, s3.y, k);
      xEnd2 = linearY(s1.x, s2.x, k);
      yEnd2 = linearY(s1.y, s2.y, k);

      xStart3 = linearY(m0.x, m3.x, k);
      yStart3 = linearY(m0.y, m3.y, k);
      xEnd3 = linearY(m1.x, m2.x, k);
      yEnd3 = linearY(m1.y, m2.y, k);
      for (int j = 0; j <= MAX; j++) {
        k = (double) ((double) j / (double) MAX);
        xM = (int) (linearY(xStart3, xEnd3, k));
        yM = (int) (linearY(yStart3, yEnd3, k));
        xS = (int) (linearY(xStart2, xEnd2, k));
        yS = (int) (linearY(yStart2, yEnd2, k));
        xD = (int) (linearY(xStart1, xEnd1, k));
        yD = (int) (linearY(yStart1, yEnd1, k));

          if ((xS < getImageWidth()) && (yS < getImageHeight()) && (xS >= 0) && (yS >= 0) &&
            (xD < getImageWidth()) && (yD < getImageHeight()) && (xD >= 0) && (yD >= 0) &&
            (xM < getImageWidth()) && (yM < getImageHeight()) && (xM >= 0) && (yM >= 0)) {

          rS = (short) SourceImage.getR()[xS][yS];
          gS = (short) SourceImage.getG()[xS][yS];
          bS = (short) SourceImage.getB()[xS][yS];
          rD = (short) StopImage.getR()[xD][yD];
          gD = (short) StopImage.getG()[xD][yD];
          bD = (short) StopImage.getB()[xD][yD];

          rm[xM][yM] = (short) linearY(rS, rD, k1);
          gm[xM][yM] = (short) linearY(gS, gD, k1);
          bm[xM][yM] = (short) linearY(bS, bD, k1);
        }
      }
    }
  }


  double linearY(double x1, double x2, double t) {
    double dx = 0;
    dx = (double) (x2 - x1);
    return (double) (x1 + (double) (dx * t));
  }

  public void Properties() {
    el = new ExpandoLog(new Frame(),
                        "Properties", prompts, defaults, 9);
    el.setVisible(true);
    el.setButton.addActionListener(this);
  }


  private void default1() {
    gridX = 5;
    gridY = 5;
      setSize(getImageWidth(), getImageHeight());
    SourceImage.default1();
    StopImage.default1();
    repaint();
  }

  public static void main(String args[]) {
    MorphLog mL = new MorphLog(
        "MorphLog");
    mL.setSize(150, 150);
    mL.setVisible(true);
  }

}