package ip.gui.dialog;

import ip.gui.frames.ColorFrame;
import ip.gui.GridImage;

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

public class MorphLog extends ColorFrame
        implements ActionListener {

    MenuBar mb = new MenuBar();
    Menu SettingsMenu = getMenu("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};
    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 an=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();
//          an.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);

        }
//      an.setSize(width,height);
//      an.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);
    }

}