package ip.raul;

import ip.gui.frames.WaveletFrame;
import ip.gui.frames.ImageFrame;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import futils.Futil;
import futils.WriterUtil;

public class MyOpenFrame extends WaveletFrame
    implements MouseListener {

  MenuBar mb = new MenuBar();
  Menu openLifting = new Menu("Lifting Open");
  Menu waveletMenu = new Menu("Hartley");
  MenuItem openLifts2_mi =
      addMenuItem(openLifting, "openLifting(2)...");
  MenuItem openLifts4_mi =
      addMenuItem(openLifting, "openLifting(4)...");
  MenuItem openLifts8_mi =
      addMenuItem(openLifting, "openLifting(8)...");
  MenuItem openLifts16_mi =
      addMenuItem(openLifting, "openLifting(16)...");
  MenuItem openLifts32_mi =
      addMenuItem(openLifting, "openLifting(32)...");
  MenuItem openLifts64_mi =
      addMenuItem(openLifting, "openLifting(64)...");
  MenuItem openLifts128_mi =
      addMenuItem(openLifting, "openLifting(128)...");
  MenuItem openLifts256_mi =
      addMenuItem(openLifting, "openLifting(256)...");

  MenuItem _3Dimage_mi =
      addMenuItem(openLifting, "View as 3D");
  MenuItem mandelbrot_mi = addMenuItem(openLifting, "Mandelbrot");

  MenuItem saveLifts_mi =
      addMenuItem(openLifting, "saveLifting...");
  MenuItem openImage_mi =
      addMenuItem(openLifting, "open Image...");
  MenuItem forwardHartley_mi = addMenuItem(waveletMenu, "Forward Hartley");
  MenuItem BackwardHartley_mi = addMenuItem(waveletMenu, "Backward Hartley");

  private double r1[][] = null;
  private double g1[][] = null;
  private double b1[][] = null;

  private short Status = 1;//fractal, 2 Hartley

  //fractal related
  public boolean busy = false;
  private Fractals fr = new Fractals();

  public MyOpenFrame(String title) {
    super(title);
    mb.add(openLifting);
    mb.add(waveletMenu);
    setMenuBar(mb);
  }

  public static void main(String args[]) {
    String title = "OpenLifting";
    MyOpenFrame of =
        new MyOpenFrame(title);
    of.setVisible(true);
    of.setSize(64, 64);

  }

  public void actionPerformed(ActionEvent e) {

    if (match(e, openImage_mi)) {
      openImage();
      getDoubles();
      return;
    }
    if (match(e, saveLifts_mi)) {
      saveLifting();
      return;
    }
    if (match(e, openLifts2_mi)) {
      openLifting(0);
      return;
    }
    if (match(e, openLifts4_mi)) {
      openLifting(1);
      return;
    }
    if (match(e, openLifts8_mi)) {
      openLifting(2);
      return;
    }
    if (match(e, openLifts16_mi)) {
      openLifting(3);
      return;
    }
    if (match(e, openLifts32_mi)) {
      openLifting(4);
      return;
    }
    if (match(e, openLifts64_mi)) {
      openLifting(5);
      return;
    }
    if (match(e, openLifts128_mi)) {
      openLifting(6);
      return;
    }
    if (match(e, openLifts256_mi)) {
      openLifting(7);
      return;
    }
    if (match(e, forwardHartley_mi)) {
      Status = 2;
      forwardHartley();
      return;
    }
    if (match(e, BackwardHartley_mi)) {
      Status = 2;
      BackwardHartley();
      return;
    }
    if (match(e, mandelbrot_mi)) {
      mandelbrot();
      return;
    }
    if (match(e, _3Dimage_mi)) {
      SnowMan.image3D(getImage(), getR());
      return;
    }
    super.actionPerformed(e);
  }

  String dir = null;


  public void mandelbrot() {
      setImageWidth(256);
      setImageHeight(256);
      Status = 1;
      setR(new short[getImageWidth()][getImageHeight()]);
      setG(new short[getImageWidth()][getImageHeight()]);
      setB(new short[getImageWidth()][getImageHeight()]);
    fr.mandelbrot(getR(), getG(), getB());
      setSize(getImageWidth(), getImageHeight());
    short2Image();
    getDoubles();
  }


  public void openLifting(int depth) {
    openShortZIP(depth);
  }

  public void openShortZIP(int depth) {
    String fn = Futil.getReadFileName();
    if (fn == null) return;
    File f = new File(fn);
    if (!f.exists()) return;
    setFileName(fn);
    try {
      readShortsZIP(fn, depth);
        setSize(getImageWidth(), getImageHeight());
      short2Image();
      liftingBackwardHaar();
    } catch (Exception e) {
      System.out.println("Read PPM Exception:" + e);
    }
    repaint();
  }


  public void saveLifting() {
    System.out.println("Saving as ForwardLiftingPPM...");
    String fn = WriterUtil.getSaveFileName("Save as ForwardLifting");
    if (fn == null) return;
    liftingForwardHaar();
    saveShortZip(fn);
    liftingBackwardHaar();
  }

  private short[][] readZipEntry(ZipInputStream zis) {
    short band[][] = null;
    ObjectInputStream ois = null;
    try {
      ZipEntry ze;
      ze = zis.getNextEntry();
      ois = new ObjectInputStream(zis);
      band = (short[][]) ois.readObject();
    } catch (Exception e) {
      System.out.println("Open getShortImageZip:" + e);
    }
    return band;
  }

  public void readShortsZIP(String fn, int depth) {
    short bandR[][] = null;
    short bandG[][] = null;
    short bandB[][] = null;
    int i = 0;
    int j = 2;
    int k = 0;
    int sw = 1;
    try {
      FileInputStream fis = new FileInputStream(fn);
      ZipInputStream zis = new ZipInputStream(fis);
      while (i <= depth) {
        ZipEntry ze;
        bandR = readZipEntry(zis);
        bandG = readZipEntry(zis);
        bandB = readZipEntry(zis);
        if (sw == 1) {
            setImageWidth(bandR.length);
            int height1 = getImageWidth();
            setImageHeight(height1);
            setR(new short[getImageWidth()][getImageHeight()]);
            setG(new short[getImageWidth()][getImageHeight()]);
            setB(new short[getImageWidth()][getImageHeight()]);
          sw = 0;
        }
          for (int x = 0; x < getImageWidth(); x++)
          for (int y = k; y < j; y++) {
            getR()[x][y] = bandR[x][y - k];
            getG()[x][y] = bandG[x][y - k];
            getB()[x][y] = bandB[x][y - k];
          }
        System.out.println("band " + i + " read.");
        i++;
        k = j;
        j = j * 2;
      }
      zis.close();
      fis.close();
      System.out.println("done!");
    } catch (Exception e) {
      System.out.println("Open getShortImageZip:" + e);
    }
  }

  private void writeZipEntry(ZipOutputStream zos, String name, short _band[][], int rowStart, int rowStop) {
      short band[][] = new short[getImageWidth()][rowStop - rowStart];
    try {
        for (int x = 0; x < getImageWidth(); x++)
        for (int y = rowStart; y < rowStop; y++)
          band[x][y - rowStart] = _band[x][y];
      ObjectOutputStream oos = null;
      ZipEntry ze = new ZipEntry(name);
      ze.setMethod(ZipEntry.DEFLATED);
      zos.putNextEntry(ze);
      oos = new ObjectOutputStream(zos);
      oos.writeObject(band);
    } catch (Exception e) {
    }
  }

  public void saveShortZip(String fn) {
    int i = 0;
    int j = 2;
    int k = 0;
    try {
      FileOutputStream fos = new FileOutputStream(fn);
      ZipOutputStream zos = new ZipOutputStream(fos);
        while (j <= getImageHeight()) {
        writeZipEntry(zos, "R" + i, getR(), k, j);
        writeZipEntry(zos, "G" + i, getG(), k, j);
        writeZipEntry(zos, "B" + i, getB(), k, j);
        System.out.println("band " + i + " written.");
        i++;
        k = j;
        j = j * 2;
      }
      zos.finish();
      zos.close();
      fos.close();
      System.out.println("done!.");
    } catch (Exception e) {
      System.out.println("Save As9bitZip error:" + e);
    }
  }

  public void getDoubles() {
      r1 = new double[getImageWidth()][getImageHeight()];
      g1 = new double[getImageWidth()][getImageHeight()];
      b1 = new double[getImageWidth()][getImageHeight()];
      for (int x = 0; x < getImageWidth(); x++) for (int y = 0; y < getImageHeight(); y++) {
            r1[x][y] = (double) (getR()[x][y]);
            g1[x][y] = (double) (getG()[x][y]);
            b1[x][y] = (double) (getB()[x][y]);
          }
  }

  public void fht2d(double f[][]) {
    int k,l;
      for (int i = 1; i < (getImageWidth() / 2); i++) {
          k = getImageWidth() - i;
          for (int j = 1; j < (getImageHeight() / 2); j++) {
              l = getImageHeight() - j;
        double e = ((f[i][j] + f[k][l]) - (f[i][l] + f[k][j])) / 2d;
        f[i][j] -= e;
        f[i][l] += e;
        f[k][j] += e;
        f[k][l] -= e;
      }
    }
  }


  public void swapQuads() {
    double tmp = 0;
      for (int x = 0; x < getImageWidth() / 2; x++) for (int y = 0; y < getImageHeight() / 2; y++) {
            tmp = r1[x][y];
          r1[x][y] = r1[getImageWidth() / 2 + x][getImageHeight() / 2 + y];
          r1[getImageWidth() / 2 + x][getImageHeight() / 2 + y] = tmp;
            tmp = g1[x][y];
          g1[x][y] = g1[getImageWidth() / 2 + x][getImageHeight() / 2 + y];
          g1[getImageWidth() / 2 + x][getImageHeight() / 2 + y] = tmp;
            tmp = b1[x][y];
          b1[x][y] = b1[getImageWidth() / 2 + x][getImageHeight() / 2 + y];
          b1[getImageWidth() / 2 + x][getImageHeight() / 2 + y] = tmp;
          tmp = r1[x + getImageWidth() / 2][y];
          r1[x + getImageWidth() / 2][y] = r1[x][getImageHeight() / 2 + y];
              r1[x][getImageHeight() / 2 + y] = tmp;
          tmp = g1[x + getImageWidth() / 2][y];
          g1[x + getImageWidth() / 2][y] = g1[x][getImageHeight() / 2 + y];
              g1[x][getImageHeight() / 2 + y] = tmp;
          tmp = b1[x + getImageWidth() / 2][y];
          b1[x + getImageWidth() / 2][y] = b1[x][getImageHeight() / 2 + y];
              b1[x][getImageHeight() / 2 + y] = tmp;
          }
  }

  public void getShorts() {
      setR(new short[getImageWidth()][getImageHeight()]);
      setG(new short[getImageWidth()][getImageHeight()]);
      setB(new short[getImageWidth()][getImageHeight()]);
    double cMax = 0d;
    double cMin = 0d;
    double delta = 1d;
      for (int x = 0; x < getImageWidth(); x++) for (int y = 0; y < getImageHeight(); y++) {
            if (r1[x][y] > cMax) cMax = r1[x][y];
            if (g1[x][y] > cMax) cMax = g1[x][y];
            if (b1[x][y] > cMax) cMax = b1[x][y];
            if (r1[x][y] < cMin) cMin = r1[x][y];
            if (g1[x][y] < cMin) cMin = g1[x][y];
            if (b1[x][y] < cMin) cMin = b1[x][y];
          }
    delta = cMax + cMin;
    delta = (double) (255d / delta);
    if (cMin < 0) cMin = -cMin;
      for (int x = 0; x < getImageWidth(); x++) for (int y = 0; y < getImageHeight(); y++) {
            getR()[x][y] = (short) ((double) (r1[x][y] + cMin) * delta);
            getG()[x][y] = (short) ((double) (g1[x][y] + cMin) * delta);
            getB()[x][y] = (short) ((double) (b1[x][y] + cMin) * delta);
          }
  }

  public void forwardHartley() {
    swapQuads();
    forwardHartley(r1);
    forwardHartley(g1);
    forwardHartley(b1);
    fht2d(r1);
    fht2d(g1);
    fht2d(b1);
    getShorts();
    short2Image();
  }


  public void BackwardHartley() {
      for (int x = 0; x < getImageWidth(); x++) for (int y = 0; y < getImageHeight(); y++) {
              r1[x][y] = (double) (r1[x][y] / (double) (getImageHeight()));
              g1[x][y] = (double) (g1[x][y] / (double) (getImageHeight()));
              b1[x][y] = (double) (b1[x][y] / (double) (getImageHeight()));
          }
    forwardHartley(r1);
    forwardHartley(g1);
    forwardHartley(b1);
    fht2d(r1);
    fht2d(g1);
    fht2d(b1);
    swapQuads();
    getShorts();
    short2Image();
  }

  public void forwardHartley(double in[][]) {
    for (int x = 0; x < in.length; x++)
      forwardHartley(in[x]);
    in = transpose(in);
    for (int x = 0; x < in.length; x++)
      forwardHartley(in[x]);
    in = transpose(in);
  }

  public void forwardHartley(double f[]) {
    int n = f.length;
    double fz[] = new double[n];
    ;
    int i,j,k,k1,k4,kx,l;
    double x0,x1,x2,x3,x4,x5,x6;
    double c1,s1,s2,c2,s3,c3;
    int f1,f2,f3;
    double sqrt2 = 1.414213562373095048801688724209698f;
    fz = f;
    for (k = 0; (1 << k) < n; k++) ;
    k &= 1;
    for (i = 1, j = 0; i < n; i++) {
      for (l = n >> 1; (((j ^= l) & l) == 0); l >>= 1) ;
      if (i > j) {
        x0 = fz[i];
        fz[i] = fz[j];
        fz[j] = x0;
      }
    }
    k1 = 1 << k;
    k4 = k1 << 2;
    kx = k1 >> 1;
    f1 = k1;
    f2 = f1 + k1;
    f3 = f2 + k1;
    if (k == 0)
      for (i = 0; i < n; i += k4) {
        x1 = fz[i] - fz[f1 + i];
        x0 = fz[i] + fz[f1 + i];
        x3 = fz[f2 + i] - fz[f3 + i];
        x2 = fz[f2 + i] + fz[f3 + i];
        fz[f2 + i] = x0 - x2;
        fz[i] = x0 + x2;
        fz[f3 + i] = x1 - x3;
        fz[f1 + i] = x1 + x3;
      }
    else
      for (i = 0, j = kx; i < n; i += k4, j += k4) {
        x0 = fz[i] - fz[j];
        x1 = fz[i] + fz[j];
        x2 = fz[f1 + i] - fz[f1 + j];
        x3 = fz[f1 + i] + fz[f1 + j];
        x4 = x1 - x3;
        x1 += x3;
        x3 = x0 - x2;
        x0 += x2;
        x5 = fz[f2 + i] + fz[f2 + j];
        x2 = fz[f2 + i] - fz[f2 + j];
        x2 *= sqrt2;
        fz[f2 + j] = x0 - x2;
        fz[j] = x0 + x2;
        x2 = fz[f3 + i] + fz[f3 + j];
        x0 = fz[f3 + i] - fz[f3 + j];
        x0 *= sqrt2;
        fz[f3 + j] = x3 - x0;
        fz[f1 + j] = x3 + x0;
        x0 = x5 - x2;
        x5 += x2;
        fz[f2 + i] = x1 - x5;
        fz[i] = x1 + x5;
        fz[f3 + i] = x4 - x0;
        fz[f1 + i] = x4 + x0;
      }
    while (k4 < n) {
      k += 2;
      k1 = 1 << k;
      k4 = k1 << 2;
      kx = k1 >> 1;
      f1 = k1;
      f2 = f1 + k1;
      f3 = f2 + k1;
      for (i = 0, j = kx; i < n; i += k4, j += k4) {
        x1 = fz[i] - fz[f1 + i];
        x0 = fz[i] + fz[f1 + i];
        x3 = fz[f2 + i] - fz[f3 + i];
        x2 = fz[f2 + i] + fz[f3 + i];
        fz[f2 + i] = x0 - x2;
        fz[i] = x0 + x2;
        fz[f3 + i] = x1 - x3;
        fz[f1 + i] = x1 + x3;
        x1 = fz[j] - fz[f1 + j];
        x0 = fz[j] + fz[f1 + j];
        x3 = sqrt2 * fz[f3 + j];
        x2 = sqrt2 * fz[f2 + j];
        fz[f2 + j] = x0 - x2;
        fz[j] = x0 + x2;
        fz[f3 + j] = x1 - x3;
        fz[f1 + j] = x1 + x3;
      }

      double angle = Math.PI / 2d / (double) (1 << k);
      double angle_index = 0;
      for (l = 1; l < kx; l++) {
        angle_index++;
        c1 = Math.cos(angle * angle_index);
        s1 = Math.sin(angle * angle_index);
        c2 = Math.cos(angle * (angle_index * 2d));
        s2 = Math.sin(angle * (angle_index * 2d));
        c3 = Math.cos(angle * (angle_index * 3d));
        s3 = Math.sin(angle * (angle_index * 3d));
        for (i = l, j = k1 - l; i < n; i += k4, j += k4) {
          x0 = fz[f1 + i] * c2 + fz[f1 + j] * s2;
          x1 = fz[f1 + i] * s2 - fz[f1 + j] * c2;
          x2 = fz[f2 + i] * c1 + fz[f2 + j] * s1;
          x3 = fz[f2 + i] * s1 - fz[f2 + j] * c1;
          x4 = fz[f3 + i] * c3 + fz[f3 + j] * s3;
          x5 = fz[f3 + i] * s3 - fz[f3 + j] * c3;
          x6 = x2 - x4;
          x4 += x2;
          x2 = x3 - x5;
          x5 += x3;
          x3 = fz[i] - x0;
          fz[f3 + i] = x3 + x2;
          fz[f1 + i] = x3 - x2;
          x3 = fz[i] + x0;
          fz[f2 + i] = x3 - x4;
          fz[i] = x3 + x4;
          x3 = fz[j] - x1;
          fz[f3 + j] = x3 - x5;
          fz[f1 + j] = x3 + x5;
          x3 = fz[j] + x1;
          fz[f2 + j] = x3 - x6;
          fz[j] = x3 + x6;
        }
      }
    }
    f = fz;
  }

  private static double[][] transpose(double in[][]) {
    int width = in.length;
    int height = in[0].length;
    double[][] output = new double[height][width];
    for (int j = 0; j < width; j++)
      for (int i = 0; i < height; i++)
        output[j][i] = in[i][j];
    return output;
  }

  private int getX(MouseEvent e) {
    return (int) (e.getX());
  }

  private int getY(MouseEvent e) {
    return (int) (e.getY());
  }

  public void mouseReleased(MouseEvent e) {
  }

  public void mousePressed(MouseEvent e) {
  }

  public void mouseClicked(MouseEvent e) {
    if (!busy) {
      busy = true;
      if (Status == 1) {
        float x = (float) getX(e);
        float y = (float) getY(e);
          float dx = (float) ((x - getImageWidth() / 2) / getImageWidth());
        dx = dx * (fr.xMax - fr.xMin);
          float dy = (float) ((y - getImageHeight() / 2) / getImageHeight());
        dy = dy * (fr.yMax - fr.yMin);
        fr.xMin = fr.xMin + dx;
        fr.yMin = fr.yMin + dy;
        fr.xMax = fr.xMax + dx;
        fr.yMax = fr.yMax + dy;

        float dx1 = fr.xMax - fr.xMin;
        dx1 = dx1 / 5f;
        float dy1 = fr.yMax - fr.yMin;
        dy1 = dy1 / 5f;
        fr.xMin = fr.xMin + dx1;
        fr.xMax = fr.xMax - dx1;
        fr.yMin = fr.yMin + dy1;
        fr.yMax = fr.yMax - dy1;

        fr.mandelbrot(getR(), getG(), getB());
        short2Image();
        System.out.println("ready for new click!");
      }
      if (Status == 2) {
        int x = (int) getX(e);
        int y = (int) getY(e);
        for (int i = 0; i < 10; i++)
          for (int j = 0; j < 10; j++) {
            int x1 = x - 5 + i;
            int y1 = y - 5 + j;
              if ((x1 >= 0) && (y1 >= 0) && (x1 < getImageWidth()) && (y1 < getImageHeight())) {
              getR()[x1][y1] = getG()[x1][y1] = getB()[x1][y1] = 255;
              r1[x1][y1] = g1[x1][y1] = b1[x1][y1] = 255d;
            }
          }
        short2Image();
      }

      busy = false;
    }
  }

}