package ip.gui;

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.awt.event.MouseMotionListener;

public class GridImage extends ImageFrame
        implements MouseListener, MouseMotionListener {

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

    MenuItem openGif_mi = addMenuItem(SettingsMenu, "[c]hange image...");
    MenuItem default_mi = addMenuItem(SettingsMenu, "[d]efault image");
    MenuItem revert_mi = addMenuItem(SettingsMenu, "[r]evert");
    MenuItem groff_mi = addMenuItem(SettingsMenu, "[g]rid on/off");
    MenuItem applyBilinear4Points_mi = addMenuItem(SettingsMenu, "[a]pplyBilinear4Points");

    public Polygon p = new Polygon();
    Image img = null;
    Color c = Color.black;
    boolean doRevert = false;
    short rn[][] = new short[0][0];
    short gn[][] = new short[0][0];
    short bn[][] = new short[0][0];
    int xPoints = 5; //grid X
    int yPoints = 5; //grid Y
    boolean busy = false;
    int groff = 1;
    int pointToMove = -1;


    public void actionPerformed(ActionEvent e) {
        if (match(e, applyBilinear4Points_mi)) {
            revert();
            applyBilinear();
            return;
        }
        if (match(e, revert_mi)) {
            revert1();
            return;
        }
        if (match(e, groff_mi)) {
            groff = 1 - groff;
            repaint();
            return;
        }

        if (match(e, openGif_mi)) {
            openGif1();
            return;
        }

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

        super.actionPerformed(e);
    }

    public GridImage(String title, Color Clr) {
        super(title);
        default1();
        c = Clr;
        addMouseListener(this);
        addMouseMotionListener(this);
        mb.add(SettingsMenu);
        setMenuBar(mb);
        repaint();
    }

    private void init() {
        groff = 1;
        for (int i = 0; i <= yPoints; i++)
            for (int j = 0; j <= xPoints; j++) p.addPoint((int) (j * getImageWidth() / xPoints), (int) (i * getImageHeight() / yPoints));
        groff = 0;
    }


    private void openGif1() {
        openGif();
        img = getImage();
        doRevert = true;
        p = new Polygon();
        init();
        setSize(getImageWidth(), getImageHeight());
        repaint();
    }

    public void revert1() {
        revert();
        img = getImage();
        doRevert = true;
        p = new Polygon();
        init();
        setSize(getImageWidth(), getImageHeight());
        repaint();
    }

    public void default1() {
        grabNumImage();
        show();
        setSize(getImageWidth(), getImageHeight());
        img = getImage();
        doRevert = true;
        p = new Polygon();
        init();
    }

    public static void main(String args[]) {
        GridImage af = new GridImage(
                "GridImage", Color.green);
        af.setSize(150, 150);
        af.setVisible(true);
        af.default1();
    }


    public void setGrid(int x, int y) {
        xPoints = x;
        yPoints = y;
        revert1();
    }

    public void setGrid() {
        setGrid(10, 10);
    }

    public void update(Graphics g) {
        paint(g);
    }


    public void paint(Graphics g) {
        if (doRevert) {
            if ((img != null)) g.drawImage(img, 0, 0, getImageWidth(), getImageHeight(), this);
        }
        g.setColor(c);
        if (groff == 0) {
            for (int i = 0; i <= yPoints; i++)
                for (int j = 0; j <= xPoints; j++) {
                    if (j != (xPoints))
                        g.drawLine(p.xpoints[j + i * (xPoints + 1)],
                                p.ypoints[j + i * (xPoints + 1)],
                                p.xpoints[j + 1 + i * (xPoints + 1)],
                                p.ypoints[j + 1 + i * (xPoints + 1)]);
                    if (i != (yPoints))
                        g.drawLine(p.xpoints[j + i * (xPoints + 1)],
                                p.ypoints[j + i * (xPoints + 1)],
                                p.xpoints[j + (i + 1) * (xPoints + 1)],
                                p.ypoints[j + (i + 1) * (xPoints + 1)]);
                }
            for (int i = 0; i < p.npoints; i++)
                g.drawRect(p.xpoints[i] - 2, p.ypoints[i] - 2, 4, 4);
        }
    }

    public void applyBilinear() {
        rn = new short[getImageWidth()][getImageHeight()];
        gn = new short[getImageWidth()][getImageHeight()];
        bn = new short[getImageWidth()][getImageHeight()];
        Point s0,s1,s2,s3,d0,d1,d2,d3;

        for (int i = 0; i < yPoints; i++)
            for (int j = 0; j < xPoints; j++) {
                s0 = new Point((int) (j * getImageWidth() / xPoints), (int) (i * getImageHeight() / yPoints));
                s1 = new Point((int) ((j + 1) * getImageWidth() / xPoints), (int) (i * getImageHeight() / yPoints));
                s2 = new Point((int) ((j + 1) * getImageWidth() / xPoints), (int) ((i + 1) * getImageHeight() / yPoints));
                s3 = new Point((int) (j * getImageWidth() / xPoints), (int) ((i + 1) * getImageHeight() / yPoints));
                d0 = new Point(p.xpoints[j + i * (xPoints + 1)], p.ypoints[j + i * (xPoints + 1)]);
                d1 = new Point(p.xpoints[j + 1 + i * (xPoints + 1)], p.ypoints[j + 1 + i * (xPoints + 1)]);
                d2 = new Point(p.xpoints[j + 1 + (i + 1) * (xPoints + 1)], p.ypoints[j + 1 + (i + 1) * (xPoints + 1)]);
                d3 = new Point(p.xpoints[j + (i + 1) * (xPoints + 1)], p.ypoints[j + (i + 1) * (xPoints + 1)]);
                solve(s0, s1, s2, s3, d0, d1, d2, d3);
            }
        setR(rn);
        setG(gn);
        setB(bn);
        short2Image();
        img = getImage();
    }

    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 solve(Point s0, Point s1, Point s2, Point s3, Point d0, Point d1, Point d2, Point d3) {
        double xStart1, xEnd1,yStart1, yEnd1;
        double xStart2, xEnd2,yStart2, yEnd2;
        double k = 0;
        int MAX = 0;
        int MAX1 = 0;
        int xc, yc, xp, yp;

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

            for (int j = 0; j <= MAX; j++) {
                k = (double) ((double) j / (double) MAX);
                xc = (int) (linearY(xStart1, xEnd1, k));
                yc = (int) (linearY(yStart1, yEnd1, k));

                xp = (int) (linearY(xStart2, xEnd2, k));
                yp = (int) (linearY(yStart2, yEnd2, k));

                if ((xp < getImageWidth()) && (yp < getImageHeight()) && (xp >= 0) && (yp >= 0) &&
                        (xc < getImageWidth()) && (yc < getImageHeight()) && (xc >= 0) && (yc >= 0)) {
                    rn[xc][yc] = getR()[xp][yp];
                    gn[xc][yc] = getG()[xp][yp];
                    bn[xc][yc] = getB()[xp][yp];
                }
            }
        }
    }


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

    public void movePoints(MouseEvent e) {
        int i = pointToMove;
        p.xpoints[i] = getX(e);
        p.ypoints[i] = getY(e);
        repaint();
    }

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

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

    public void mousePressed(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseClicked(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
        busy = false;
        repaint();
    }

    public void mouseDragged(MouseEvent e) {
        e.consume();
        double dx = getX(e);
        double dy = getY(e);
        double sx, sy;
        double ray = 100000;
        if (!busy) {
            busy = true;
            pointToMove = -1;
            for (int i = 0; i < p.npoints; i++) {
                sx = (p.xpoints[i] - dx) * (p.xpoints[i] - dx);
                sy = (p.ypoints[i] - dy) * (p.ypoints[i] - dy);
                if ((sx + sy) < ray) {
                    ray = sx + sy;
                    pointToMove = i;
                }
            }
        }
        if (pointToMove != -1) {
            movePoints(e);
            return;
        }
        repaint();
    }

    public void mouseMoved(MouseEvent e) {
    }

}