package graphics;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

/** 
 * Class for simple graphics example.  The program provides a surface on 
 * which the user can draw rectangles by clicking and dragging.
 *
 * @author B.L. Massingill
 */
public class SimpleDrawX {

    /** 
     * Creates and displays GUI.  
     * (I could also have put this code in a constructor.)
     */
    private static void createAndShowGUI() {

        // basic GUI setup
        JFrame theFrame = new JFrame("Drawing Example");

        // set what should happen when window is closed
        theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // add panel that supports drawing as described
        final DrawingPanel drawPanel = new DrawingPanel();
        theFrame.add(drawPanel, BorderLayout.CENTER);

        // add a "clear" button
        JPanel buttonPanel = new JPanel(new FlowLayout());
        JButton clearButton = new JButton("Clear");
        clearButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                drawPanel.clear();
                drawPanel.repaint();
            }
        });
        buttonPanel.add(clearButton);
        theFrame.add(buttonPanel, BorderLayout.SOUTH);

        // set size
        theFrame.setSize(600, 400);

        // make visible
        theFrame.setVisible(true);
    }

    /** 
     * Main method.
     */
    public static void main(String[] args) {
        // do this in the way recommended as thread-safe
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }

    // ---- classes ----

    /**
     * Class for rectangle.
     */
    private static class SimpleRectangle {
        public final int left;
        public final int top;
        public final int width;
        public final int height;
        public SimpleRectangle(int l, int t, int w, int h) {
            left = l;
            top = t;
            width = w;
            height = h;
        }
    }

    /** 
     * Class for "drawing panel". 
     */
    private static class DrawingPanel extends JPanel {

        /** 
         * Constructor.
         */
        public DrawingPanel() {

            // add listeners for various mouse events
            addMouseListener(new MouseAdapter() {
                public void mousePressed(MouseEvent e) {
                    onMousePressed(e);
                }
                public void mouseReleased(MouseEvent e) {
                    onMouseReleased(e);
                }
            });
            addMouseMotionListener(new MouseMotionAdapter() {
                public void mouseDragged(MouseEvent e) {
                    onMouseDragged(e);
                }
            });

            // reset everything
            clear();
        }

        /** 
         * Clear panel. 
         */
        public void clear() {
            rectangles = new ArrayList<SimpleRectangle>();
            selectedPoint = null;
            dragPoint = null;
        }

        /** 
         * "Paint" panel.  This is where "custom graphics" is done. 
         * @param g graphics context on which to perform drawing
         */
        public void paintComponent(Graphics g) {

            // paint background and get Graphics2D version of g
            super.paintComponent(g); 

            // paint completed rectangles
            int nextColor = 0;
            for (SimpleRectangle r : rectangles) {
                g.setColor(colors[nextColor]);
                g.fillRect(r.left, r.top, r.width, r.height);
                nextColor = (nextColor + 1) % colors.length;
            }

            // paint something indicating shape-in-progress
            if ((selectedPoint != null) && (dragPoint != null)) {
                g.setColor(Color.black);
                SimpleRectangle outline = 
                    makeRectangle(selectedPoint, dragPoint);
                g.drawRect(outline.left, outline.top, 
                        outline.width, outline.height);
            }
        }

        /** 
         * Respond to "mouse pressed" event (by saving the clicked-on
         * point so we can use it later to construct a rectangle).
         * @param e object describing the event
         */
        private void onMousePressed(MouseEvent e) {
            selectedPoint = e.getPoint();
        }

        /** 
         * Respond to "mouse released" event (by constructing a rectangle
         * using the point saved on a "mouse pressed" event).
         * @param e object describing the event
         */
        private void onMouseReleased(MouseEvent e) {
            if (selectedPoint != null) {
                SimpleRectangle r = makeRectangle(selectedPoint, e.getPoint());
                rectangles.add(r);
                selectedPoint = null;
                dragPoint = null;
            }
            repaint();
        }

        /** 
         * Respond to "mouse dragged" event (by showing an outline of the
         * rectangle that would be added if the mouse were released).
         * @param e object describing the event
         */
        private void onMouseDragged(MouseEvent e) {
            dragPoint = e.getPoint();
            repaint();
        }

        /** 
         * Construct shape using p1 and p2 as diagonally-opposite corners.
         * @param p1 one point
         * @param p2 the other point 
         * @return rectangle 
         */
        private SimpleRectangle makeRectangle(Point p1, Point p2) {
            return new SimpleRectangle(
                    Math.min(p1.x, p2.x),
                    Math.min(p1.y, p2.y),
                    Math.abs(p1.x - p2.x),
                    Math.abs(p1.y - p2.y));
        }

        // ---- variables ----

        // colors to cycle through
        private static Color[] colors = new Color[] {
            Color.yellow, Color.red, Color.blue, Color.green 
        };

        private ArrayList<SimpleRectangle> rectangles;
        private Point selectedPoint;
        private Point dragPoint;
    }
}

