package ip.gui.frames;

import ip.gui.dialog.DoLog;
import ip.gui.*;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.FileOutputStream;
import java.io.PrintWriter;

import math.Mat;
import graphics.ImageUtils;
import futils.WriterUtil;


public class EdgeFrame extends SpatialFilterFrame
        implements Doable {

    private Menu laplacianMenu = getMenu("Laplacian");
    private Menu edgeMenu = getMenu("Edge");
    private Menu templateMenu = getMenu("Template");
    private Menu threshMenu = getMenu("Threshold");


    private MenuItem laplacian5_mi = addMenuItem(laplacianMenu, "[E-T-4] 5");
    private MenuItem laplacian3_mi = addMenuItem(laplacianMenu, "[E-T-5] 3");
    private MenuItem laplacian3Minus_mi = addMenuItem(laplacianMenu, "[E-T-6] 3 Minus");
    private MenuItem laplacian3Prewitt_mi = addMenuItem(laplacianMenu, "[E-T-p]3Prewitt");
    private MenuItem laplacian3_4_mi = addMenuItem(laplacianMenu, "[E-T-l]3_4");
    private MenuItem laplacian9_mi = addMenuItem(laplacianMenu, "[E-T-7]9");
    private MenuItem hat13_mi = addMenuItem(laplacianMenu, "[E-T-8]hat 13x13");
    private MenuItem hat13v2_mi = addMenuItem(laplacianMenu, "[E-T-8]hat 13x13 v2");

    private MenuItem pixelDifference_mi = addMenuItem(templateMenu, "Pixel Difference");
    private MenuItem roberts2_mi = addMenuItem(edgeMenu, "[E-T-r]oberts 2");
    private MenuItem mosaic_mi = addMenuItem(edgeMenu, "mosaic");
    private MenuItem medianSquare2x2_mi = addMenuItem(edgeMenu, "[E-)]medianSquare2x2 ");
    private MenuItem median2x1_mi = addMenuItem(edgeMenu, "[E-}]median2x1");
    private MenuItem median1x2_mi = addMenuItem(edgeMenu, "[E-{]median1x2");
    private MenuItem magOfDerivativeOfGauss13_mi = addMenuItem(edgeMenu, "magOfDerivativeOfGauss13");


    private MenuItem sobel3_mi = addMenuItem(templateMenu, "[E-T-2]Sobel3");
    private MenuItem separatedPixelDifference_mi =
            addMenuItem(templateMenu, "Separated Pixel Difference");
    private MenuItem prewitt_mi = addMenuItem(templateMenu, "Prewitt");

    private MenuItem freiChen_mi = addMenuItem(templateMenu, "Frei-Chen");

    private MenuItem zeroCross_mi = addMenuItem(edgeMenu, "[E-T-z]eroCross");
    private MenuItem sizeDetector_mi = addMenuItem(edgeMenu, "[E-T-s]ize detector");
    private MenuItem printVariance_mi = addMenuItem(edgeMenu, "[E-T-p]rintVariance");


    private MenuItem printSigma_mi = addMenuItem(edgeMenu, "printSigma");

    private MenuItem thresh_mi = addMenuItem(threshMenu, "[E-T-9]thresh");
    private MenuItem threshold_mi = addMenuItem(threshMenu, "[E-T]hreshold...");


    public void actionPerformed(ActionEvent e) {

        if (match(e, mosaic_mi)) {
            mosaic();
            return;
        }

        if (match(e, magOfDerivativeOfGauss13_mi)) {
            magOfDerivativeOfGauss13();
            return;
        }
        if (match(e, median2x1_mi)) {
            median2x1();
            return;
        }
        if (match(e, median1x2_mi)) {
            median1x2();
            return;
        }
        if (match(e, medianSquare2x2_mi)) {
            medianSquare2x2();
            return;
        }
        if (match(e, freiChen_mi)) {
            freiChen();
            return;
        }
        if (match(e, prewitt_mi)) {
            prewitt();
            return;
        }
        if (match(e, separatedPixelDifference_mi)) {
            separatedPixelDifference();
            return;
        }
        if (match(e, threshold_mi)) {
            threshLog();
            return;
        }

        if (match(e, pixelDifference_mi)) {
            pixelDifference();
            return;
        }
        if (match(e, roberts2_mi)) {
            roberts2();
            return;
        }
        if (match(e, printSigma_mi)) {
            printSigma();
            return;
        }
        if (match(e, printVariance_mi)) {
            printVariance();
            return;
        }
        if (match(e, laplacian3_4_mi)) {
            laplacian3_4();
            return;
        }
        if (match(e, sizeDetector_mi)) {
            sizeDetector();
            return;
        }
        if (match(e, zeroCross_mi)) {
            zeroCross();
            return;
        }
        if (match(e, laplacian3Prewitt_mi)) {
            laplacian3Prewitt();
            return;
        }
        if (match(e, laplacian3Minus_mi)) {
            laplacian3Minus();
            return;
        }
        if (match(e, thresh_mi)) {
            thresh();
            return;
        }
        if (match(e, sobel3_mi)) {
            sobel3();
            return;
        }
        if (match(e, hat13v2_mi)) {
            hat13v2();
            return;
        }
        if (match(e, hat13_mi)) {
            hat13();
            return;
        }
        if (match(e, shadowMask_mi)) {
            shadowMask1();
            return;
        }
        if (match(e, laplacian3_mi)) {
            laplacian3();
            return;
        }
        if (match(e, laplacian5_mi)) {
            laplacian5();
            return;
        }
        if (match(e, laplacian9_mi)) {
            laplacian9();
            return;
        }

        super.actionPerformed(e);

    }

    public EdgeFrame(String title) {
        super(title);
        edgeMenu.add(threshMenu);
        edgeMenu.add(laplacianMenu);
        edgeMenu.add(templateMenu);
        getSpatialFilterMenu().add(edgeMenu);
    }


    public void colorToRed() {
        for (int x = 0; x < getImageWidth(); x++)
            for (int y = 0; y < getImageHeight(); y++)
                getR()[x][y] = (short)
                        ((getR()[x][y] + getG()[x][y] + getB()[x][y]) / 3);
    }

    public void medianSquare2x2() {
        short k[][] = {
            {1, 1, 0},
            {1, 1, 0},
            {0, 0, 0}
        };
        median(k);
    }

// Note to the reader...
// the private Magnitude of Gaussian stuff
// is experimental stuff...DL
    public void magOfDerivativeOfGauss13() {
        float k[][] = Kernels.getMagnitudeOfTheDerivativeOfGauss(13, 13, 1.0);
        System.out.println("Experimental!!");
        convolve(k);
    }

    public void median2x1() {
        short[][] k = Kernels.getMedian2x1();
        median(k);
    }

    public void median1x2() {
        short[][] k = Kernels.getMedian1x2();
        median(k);
    }

    public void roberts2() {
        colorToRed();
        int p[] = new int[4];
        float delta_u = 0;
        float delta_v = 0;
        short t;
        for (int x = 0; x < getImageWidth() - 1; x++)
            for (int y = 0; y < getImageHeight() - 1; y++) {
                p[0] = getR()[x][y];
                p[1] = getR()[x + 1][y];
                p[2] = getR()[x][y + 1];
                p[3] = getR()[x + 1][y + 1];
                delta_u = p[0] - p[3];
                delta_v = p[1] - p[2];
                t = (short)
                        Math.sqrt(delta_u * delta_u + delta_v * delta_v);
                //if (t > 48) t = 255;
                //else t=0;
                getR()[x][y] = t;
                getG()[x][y] = t;
                getB()[x][y] = t;
            }
        short2Image();

    }

    public void mosaic() {
        ImageUtils.getSubBands(getImage(), this);
    }

    public void shadowMask1() {
        convolve(Kernels.getRobinson1());
    }


    public void sizeDetector() {
        setR(Kernels.getSizeDetector(getR()));
        setG(Kernels.getSizeDetector(getG()));
        setB(Kernels.getSizeDetector(getB()));
        short2Image();
    }

    public void sobel3() {
        float k1[][] = Kernels.getRobinson3();
        float k2[][] = Kernels.getRobinson1();
        templateEdge(k1, k2);
    }

    public void separatedPixelDifference() {
        float k1[][] = {
            {0, 0, 0},
            {1, 0, -1},
            {0, 0, 0}
        };
        float k2[][] = {
            {0, -1, 0},
            {0, 0, 0},
            {0, 1, 0}
        };
        templateEdge(k1, k2);
    }

    public void prewitt() {
        float k1[][] = {
            {1, 0, -1},
            {1, 0, -1},
            {1, 0, -1}
        };

        float k2[][] = {
            {-1, -1, -1},
            {0, 0, 0},
            {1, 1, 1}
        };
        Mat.scale(k1, 1 / 3.0);
        Mat.scale(k2, 1 / 3.0);
        templateEdge(k1, k2);
    }

    public void freiChen() {
        float r2 = (float) Math.sqrt(2);
        float k1[][] = {
            {1, 0, -1},
            {r2, 0, -r2},
            {1, 0, -1}
        };

        float k2[][] = {
            {-1, -r2, -1},
            {0, 0, 0},
            {1, r2, 1}
        };
        double s = 1 / (2 + r2);
        Mat.scale(k1, s);
        Mat.scale(k2, s);
        templateEdge(k1, k2);
    }

    public void pixelDifference() {
        float k1[][] = {
            {0, 0, 0},
            {0, 1, -1},
            {0, 0, 0}
        };
        float k2[][] = {
            {0, -1, 0},
            {0, 1, 0},
            {0, 0, 0}
        };
        templateEdge(k1, k2);
    }


    public void templateEdge(float k1[][], float k2[][]) {
        colorToRed();
        //printMaple(k1,"k1=");
        //printMaple(k2,"k2=");
        setG(ConvolutionUtils.convolve2(getR(), k1));
        setB(ConvolutionUtils.convolve2(getR(), k2));
        short t = 0;
        for (int x = 0; x < getImageWidth(); x++)
            for (int y = 0; y < getImageHeight(); y++) {
                t = (short) Math.sqrt(getG()[x][y] * getG()[x][y] + getB()[x][y] * getB()[x][y]);
                getR()[x][y] = t;
                getG()[x][y] = getR()[x][y];
                getB()[x][y] = getR()[x][y];
            }
        short2Image();
    }

    public void printMaple(float a[][], String prefix) {
        System.out.print(prefix);
        super.printMaple(a);
    }

    public void laplacian5() {
        float[][] k = Kernels.getLaplacian5();
        convolve(k);
    }

    public void laplacian3() {
        float[][] k = Kernels.getLaplacian3();
        convolve(k);
    }

    public void laplacian3Prewitt() {
        float[][] k = Kernels.getLaplacianPrewitt();
        //Mat.scale(k,1/8.0);
        convolve(k);
    }

    public void laplacian3_4() {
        float k[][] = {
            {1, -2, 1},
            {-2, 4, -2},
            {1, -2, 1}
        };
        convolve(k);
    }

    public void laplacian3Minus() {
        float k[][] = {
            {0, 1, 0},
            {1, -4, 1},
            {0, 1, 0}
        };
        convolve(k);
    }

    public void tGenerator(int min, int max) {
        String fn = WriterUtil.getSaveFileName("t.java generator");
        try {
            FileOutputStream fos =
                    new FileOutputStream(fn);
            PrintWriter ps = new PrintWriter(fos);
            for (int i = min; i < max; i++)
                ps.println(
                        "static double t"
                        + i + " = 0;");
            fos.close();
        } catch (Exception e) {
        }
        ;
    }

    public static void main(String args[]) {
        EdgeFrame ef = new EdgeFrame("Edge Frame");
        ef.show();
        ef.tGenerator(0, 2000);
    }

    public void thresh() {
        Mat.threshold(getR());
        Mat.threshold(getG());
        Mat.threshold(getB());
        short2Image();
    }

    public void convolveZeroCross(float k[] []) {
        // a 1kx1k image allocates not more than
        // 1 MB at a time.
        //Mat.print(k);
        setR(convolveZeroCross(getR(), k));
        setG(convolveZeroCross(getG(), k));
        setB(convolveZeroCross(getB(), k));
        short2Image();
    }

    public void zeroCross() {
        // a 1kx1k image allocates not more than
        // 1 MB at a time.
        //Mat.print(k);
        setR(zeroCross(getR()));
        setG(zeroCross(getG()));
        setB(zeroCross(getB()));
        short2Image();
    }

    public short[][] convolveZeroCross(short a[][], float k[][]) {
        a = ConvolutionUtils.convolve2(a, k);
        a = zeroCross(a);
        return a;
    }

    // p0 p1 p2
    // p3 p4 p5
    // p6 p7 p8
    //
    //
    public short[][] zeroCross(short f[][]) {
        short a[][] = new short[f.length][f[0].length];
        int p[] = new int[9];
        for (int x = 1; x < f.length - 1; x++)
            for (int y = 1; y < f[0].length - 1; y++) {
                p[1] = f[x][y + 1];
                p[3] = f[x - 1][y];
                p[5] = f[x + 1][y];
                p[7] = f[x][y - 1];
                if (((p[1] < 0) && (p[7] >= 0)) ||
                        ((p[1] >= 0) && (p[7] < 0)) ||
                        ((p[3] < 0) && (p[5] >= 0)) ||
                        ((p[3] >= 0) && (p[5] < 0)))
                    a[x][y] = 255;
            }
        return a;
    }


    public void laplacian9() {
        float[][] k = Kernels.getLaplacian9();
//sum=0.0
        convolve(k);
    }


    public void hat13v2() {
        float k[][] = Kernels.getLaplaceOfGaussianKernel(13, 13, 2.0);
        convolve(k);
        //Mat.printKernel(k,"hat13v2"+k.length);
    }

    public void hat13() {
        // Hat13 filter
        float[][] k = Kernels.getHat13();
        Timer t = new Timer();
        t.start();
        convolve(k);

        t.print("laplace convolution");
        //Mat.printKernel(k,"hat13"+k.length);
    }

    public void horizontalSegment() {
        float mask [][] = {
            {0, 0, 0},
            {1, 1, 1},
            {0, 0, 0}
        };
        Mat.normalize(mask);
        convolve(mask);
    }

    public void verticalSegment() {
        float mask [][] = {
            {0, 1, 0},
            {0, 1, 0},
            {0, 1, 0}
        };
        Mat.normalize(mask);
        convolve(mask);
    }

    protected void printVariance() {
        System.out.println("variance(r)=" + Mat.variance(getR()));
        System.out.println("variance(g)=" + Mat.variance(getG()));
        System.out.println("variance(b)=" + Mat.variance(getB()));
    }

    protected double sigma(short a[][]) {
        return Math.sqrt(Mat.variance(a));
    }

    protected void printSigma() {
        System.out.println("Sigma(r)=" + sigma(getR()));
        System.out.println("Sigma(g)=" + sigma(getG()));
        System.out.println("Sigma(b)=" + sigma(getB()));
    }

    public void threshLog() {
        String prompts[] = {
            "t1", "t2",
            "t3", "t4",
            "K=#grays, overrides above"};
        String defaults[] = {
            "60", "120",
            "180", "240",
            "0"};
        int fieldSize = 6;
        new DoLog(this, "Threshold Dialog",
                prompts, defaults, fieldSize);
    }

    public void doit(double d[]) {
        if (d[4] != 0)
            kgreyThresh(d[4]);
        else
            thresh4(d);
    }

    public void kgreyThresh(double k) {
        Histogram rh = new Histogram(getR(), "red");
        double cmf[] = rh.getCMF();
        TransformTable tt = new TransformTable(cmf.length);
        short lut[] = tt.getLut();
        int q = 1;
        short v = 0;
        short dv = (short) (255 / k);
        for (int i = 0; i < lut.length; i++) {
            if (cmf[i] > q / k) {
                v += dv;
                q++; //(k == q+1)||
                if (q == k) v = 255;
            }
            lut[i] = v;
        }
        tt.setLut(lut);
        tt.clip();
        //tt.print();
        applyLut(lut);
    }

    public void thresh4(double d[]) {
        short lut[] = new short[256];
        if (d[4] == 0)
            for (int i = 0; i < lut.length; i++) {
                if (i < d[0])
                    lut[i] = 0;
                else if (i < d[1])
                    lut[i] = (short) d[0];
                else if (i < d[2])
                    lut[i] = (short) d[1];
                else if (i < d[3])
                    lut[i] = (short) d[2];
                else
                    lut[i] = 255;
                //System.out.println(lut[i]);
            }
        applyLut(lut);
    }


}