package ip.gui.frames;

import ip.gui.dialog.DoubleLog;
import ip.gui.Points;
import ip.gui.IconComponent;

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 PaintFrame extends BoundaryFrame
        implements MouseListener, MouseMotionListener {
    IconFrame iconFrame = new IconFrame();
    Menu paintMenu = getMenu("Paint");
    MenuItem showIconFrame_mi = addMenuItem(paintMenu, "show paint bar");
    MenuItem eraseShapes_mi = addMenuItem(paintMenu, "Erase shapes");
    MenuItem resizeFrame_mi = addMenuItem(paintMenu, "[E-R]esize Frame");
    int x1, y1, x2, y2;
    Point anchor;

    Points userPoints = new Points();

    public void actionPerformed(ActionEvent e) {

        if (match(e, resizeFrame_mi)) {
            resizeFrame();
            return;
        }
        if (match(e, eraseShapes_mi)) {
            eraseShapes();
            return;
        }
        if (match(e, showIconFrame_mi)) {
            showIconFrame();
            return;
        }
        super.actionPerformed(e);
    }

    public void resizeFrame() {
        setSize(getImageWidth(), getImageHeight());
    }

    public void showIconFrame() {
        Rectangle r = getBounds();
        Dimension d = r.getSize();
        iconFrame.setLocation(d.width, d.height);
        iconFrame.setVisible(true);
    }

    public void paint(Graphics g) {
        if (iconFrame != null) iconFrame.setLabels(getImageWidth(), getImageHeight(), red, green, blue);
        super.paint(g);
        if (userPoints == null) return;
        userPoints.drawUserPoints(g);
    }

    public void eraseShapes() {
        userPoints = new Points();
        repaint();
    }


    public PaintFrame(String title) {
        super(title);
        getSpatialFilterMenu().add(paintMenu);
        showIconFrame();
        addMouseListener(this);
        addMouseMotionListener(this);
    }

    public void mousePressed(MouseEvent e) {
        setP1(e);
        anchor = new Point(e.getX(), e.getY());
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseClicked(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
        IconComponent ic = iconFrame.getSelectedIcon();
        if (ic == iconFrame.getXImageIcon())
            userPoints.addPoint(new Point(e.getX(), e.getY()));
        if (ic == iconFrame.getEyeDropperIcon()) {
            iconFrame.setPosition(x1, y1);
            getColor();
        }
        if (ic == iconFrame.getMarqeeIcon()) {
            grabFrame(anchor.x, anchor.y, e.getX(), e.getY());
        }
        if (ic == iconFrame.getMagnifyingGlassIcon()) magnify();
        repaint();
    }

    public void grabFrame(int x1, int y1, int x2, int y2) {
        int w = Math.abs(x1 - x2);
        int h = Math.abs(y1 - y2);
        short _r[][] = new short[w][h];
        short _g[][] = new short[w][h];
        short _b[][] = new short[w][h];

        for (int x = x1,ix = 0; x < x2; x++, ix++)
            for (int y = y1,iy = 0; y < y2; y++, iy++) {
                _r[ix][iy] = getR()[x][y];
                _g[ix][iy] = getG()[x][y];
                _b[ix][iy] = getB()[x][y];
            }
        setChild(new TopFrame(
                "clipped Region",
                _r, _g, _b));
    }

    public void magnify() {
        fishEye2(x1, y1, 1.0, 2);
    }

    // the old magnify...now private...
    private void magnify2() {
        int m = 0;
        int n = 0;
        short rs[][] = new short[getImageWidth() * 2][getImageHeight() * 2];
        short gs[][] = new short[getImageWidth() * 2][getImageHeight() * 2];
        short bs[][] = new short[getImageWidth() * 2][getImageHeight() * 2];

        for (int i = 0; i < getImageWidth(); i++) {
            for (int j = 0; j < getImageHeight(); j++) {
                rs[m][n] = getR()[i][j];
                gs[m][n] = getG()[i][j];
                bs[m][n] = getB()[i][j];
                rs[m + 1][n] = getR()[i][j];
                gs[m + 1][n] = getG()[i][j];
                bs[m + 1][n] = getB()[i][j];
                rs[m][n + 1] = getR()[i][j];
                gs[m][n + 1] = getG()[i][j];
                bs[m][n + 1] = getB()[i][j];
                rs[m + 1][n + 1] = getR()[i][j];
                gs[m + 1][n + 1] = getG()[i][j];
                bs[m + 1][n + 1] = getB()[i][j];
                n += 2;
            }
            m += 2;
            n = 0;
        }
        setR(rs);
        setG(gs);
        setB(bs);
        short2Image();
        resizeFrame();
    }

    private int getX(MouseEvent e) {
        return (int) (e.getX() * getImageWidth() / getSize().width);
    }

    private int getY(MouseEvent e) {
        return (int) (e.getY() * getImageHeight() / getSize().height);
    }

    private Point scalePoint(Point p) {
        return
                new Point(p.x * getSize().width / getImageWidth(),
                        p.y = p.y * getSize().height / getImageHeight());
    }

    private void clearPel(int x, int y) {
        getR()[x][y] = 0;
        getG()[x][y] = 0;
        getB()[x][y] = 0;
    }

    public void erasePoint() {
        clearPel(x1, y1);
        short2Image();
    }

    private void getColor() {
        red = getR()[x1][y1];
        green = getG()[x1][y1];
        blue = getB()[x1][y1];
    }

    private short red = 0;
    private short green = 0;
    private short blue = 0;
    private DoubleLog fishLog = null;

    private void initFishLog() {
        String title = "Fisheye Dialog";
        int fieldSize = 6;
        String prompts[] = {
            "gamma",
            "radius"};
        String defaults[] = {
            "2.1",
            "32"};
        fishLog = new
                DoubleLog(this, title, prompts, defaults, fieldSize);
        fishLog.setVisible(true);
    }

    public void handPoint() {
        if (fishLog == null) initFishLog();
        double d[] = fishLog.getUserInputAsDouble();
        fishEye2(x1, y1, d[0], d[1]);
    }

    // The following can be accelerated by precomputing
    // stuff. In the choice between clarity and speed,
    // I choose clarity!
    public void fishEye2(int xc, int yc, double gamma, double R) {
        int w = getImageWidth();
        int h = getImageHeight();
        short rn[][] = new short[getImageWidth()][getImageHeight()];
        short gn[][] = new short[getImageWidth()][getImageHeight()];
        short bn[][] = new short[getImageWidth()][getImageHeight()];
        double p[] = new double[2];
        int red, green, blue;
        int xp, yp, i, j;
        for (int x = 0; x < w; x++)
            for (int y = 0; y < h; y++) {
                double dx = x - xc;
                double dy = y - yc;
                double radius = Math.sqrt(dx * dx + dy * dy);
                // From [Holzmann] pp. 60
                double u = Math.pow(radius, gamma) / R;
                double a = Math.atan2(dy, dx);

                p[0] = u * Math.cos(a);
                p[1] = u * Math.sin(a);
                xp = (int) p[0] + xc;
                yp = (int) p[1] + yc;
                if ((xp < w) && (yp < h) && (xp >= 0) && (yp >= 0)) {
                    rn[x][y] = getR()[xp][yp];
                    gn[x][y] = getG()[xp][yp];
                    bn[x][y] = getB()[xp][yp];
                }
            }
        setR(rn);
        setG(gn);
        setB(bn);
        short2Image();
    }

    public void pencilPoint() {
        setColor(x1, y1);
        short2Image();
    }

    public void brushPoint() {
        setColor(x1, y1);
        setColor(x1 + 1, y1 + 1);
        setColor(x1 - 1, y1 - 1);
        setColor(x1 - 1, y1 + 1);
        setColor(x1 + 1, y1 - 1);
        short2Image();
    }

    private void setColor(int x, int y) {
        getR()[x][y] = red;
        getG()[x][y] = green;
        getB()[x][y] = blue;
    }

    public void mouseDragged(MouseEvent e) {
        e.consume();
        IconComponent ic = iconFrame.getSelectedIcon();
        if (ic == iconFrame.getEraserIcon()) erasePoint();
        if (ic == iconFrame.getBrushIcon()) brushPoint();
        if (ic == iconFrame.getPencilIcon()) pencilPoint();
        setP1(e);
        if (ic == iconFrame.getHandIcon()) handPoint();

        repaint();
    }

    private void setP1(MouseEvent e) {
        x1 = getX(e);
        y1 = getY(e);
    }

    public void mouseMoved(MouseEvent e) {
    }

    public static void main(String args[]) {
        PaintFrame pf = new PaintFrame("Paint Frame");
        pf.show();
    }


}