/*
 * @(#)CircleSample5.java    15 March 2004
 *
 * Copyright 2004
 * College of Computer Science
 * Northeastern University
 * Boston, MA  02115
 *
 * This software may be used for educational purposes as long as
 * this copyright notice is retained intact at the top of all files.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either in whole
 * or in part without explicit permission.
 *
 * Contact information:
 *   Richard Rasala    rasala@ccs.neu.edu
 *   Viera Proulx      vkp@ccs.neu.edu
 * 
 * Telephone:          617-373-2462
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

import edu.neu.ccs.gui.*;
import edu.neu.ccs.util.*;

import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.*;

public class CircleSample5 extends DisplayPanel implements JPTConstants {   
    // constant: buffer size
    protected final int BUFFERSIZE = 400;
    
    // square window for painting
    protected BufferedPanel window
        = new BufferedPanel(BUFFERSIZE, BUFFERSIZE);

    // get the mouse action adapter for the window
    MouseActionAdapter adapter = window.getMouseActionAdapter();
    

    // radius text field view
    protected TextFieldView radiusTFV
        = new TextFieldView("20",  200);
    
    // center x text field view
    protected TextFieldView xTFV
        = new TextFieldView("" + (BUFFERSIZE / 2), 200);
    
    // center y text field view
    protected TextFieldView yTFV
        = new TextFieldView("" + (BUFFERSIZE / 2), 200);
    
    
    // table for input text fields
    protected TablePanel inputPanel
        = new TablePanel(
            new Object[][] {
                { "Radius:",   new Halo(radiusTFV) },
                { "Center X:", new Halo(xTFV)      },
                { "Center Y:", new Halo(yTFV)      } },
            5, 5, EAST);
    
    
    // color choice
    protected ColorView plotColor
        = new ColorView(Color.black, true);
    
    
    // random color option
    protected BooleanView randomColorOption
        = new BooleanView("Random Color", false);
    
    
    // constants to support plot task
    protected final int FILL = 0;
    protected final int DRAW = 1;
    
    // plot task
    protected OptionsView plotStyle
        = new OptionsView(new String[] { "Fill", "Draw" },
            FILL, new TableLayout(1, 2, 20, 20, CENTER));
    
    
    // constants to support plot object
    protected final int CIRCLE = 0;
    protected final int LINE = 1;
    
    // plot object
    protected OptionsView plotObject
        = new OptionsView(new String[] { "Circle", "Line" },
            CIRCLE, new TableLayout(1, 2, 20, 20, CENTER));
    
    
    // action: graph circle
    protected Action graphCircle
        = new SimpleAction("Graph Circle") {
            public void perform() { graphCircle(); }
        };
    
    // action: clear graphics window
    protected Action clear
        = new SimpleAction("Clear") {
            public void perform() { clear(); }
        };
    
    // action: clear graphics window and reset controls
    protected Action reset
        = new SimpleAction("Reset") {
            public void perform() { reset(); }
        };
    
    
    // mouse moved action
    protected MouseAction mouseMoved
        = new MouseAction("Mouse Moved") {
            public void mouseActionPerformed(MouseEvent mevt) {
                mouseMoved(mevt);
            }
        };
    
    // mouse pressed action
    protected MouseAction mousePressed
        = new MouseAction("Mouse Pressed") {
            public void mouseActionPerformed(MouseEvent mevt) {
                mousePressed(mevt);
            }
        };
    
    // mouse dragged action
    protected MouseAction mouseDragged
        = new MouseAction("Mouse Dragged") {
            public void mouseActionPerformed(MouseEvent mevt) {
                mouseDragged(mevt);
            }
        };
    
    // mouse released action
    protected MouseAction mouseReleased
        = new MouseAction("Mouse Released") {
            public void mouseActionPerformed(MouseEvent mevt) {
                mouseReleased(mevt);
            }
        };
    
    
    // button panel
    protected ActionsPanel buttonPanel
        = new ActionsPanel(new Action[] { graphCircle, clear, reset });
    
    
    // controls panel
    protected TablePanel controls
        = new TablePanel(
            new Object[] {
                inputPanel,
                plotColor,
                randomColorOption,
                plotStyle,
                plotObject,
                buttonPanel },
            VERTICAL, 15, 15, NORTH);
    
    
    // main panel
    protected TablePanel mainPanel
        = new TablePanel(
            new Object[] {
                new Display(controls, null, "Controls"),
                new Display(window,   null, "Graphics") },
            HORIZONTAL, 5, 5, NORTH);
    
    
    // data model correspoding to the input text fields
    protected double radius, x, y;
    
    
    // ellipse used to draw a circle
    protected Ellipse2D circle = new Ellipse2D.Double();
    
    
    // constants to support line graphs
    protected final int TEMPORARY = 0;
    protected final int PERMANENT = 1;
    
    // line
    protected Line2D line = new Line2D.Double();
    
    // point 1 of the line
    protected Point2D P1 = new Point2D.Double();
    
    // point 2 of the line
    protected Point2D P2 = new Point2D.Double();
    
    // stroke for line drawing
    protected Stroke stroke = null;
    
    
    // constructor
    public CircleSample5() {
        add(mainPanel);
        addActions();
        plotColor.setChooserClickCount(1);
    }
    
    
    // add various actions
    protected void addActions() {
        // add the graph circle actions for the text fields
        radiusTFV.addActionListener(graphCircle);
        xTFV.addActionListener(graphCircle);
        yTFV.addActionListener(graphCircle);
        
        // add the mouse actions for the graphics window
        adapter.addMouseMovedAction(mouseMoved);
        adapter.addMousePressedAction(mousePressed);
        adapter.addMouseDraggedAction(mouseDragged);
        adapter.addMouseReleasedAction(mouseReleased);
    }
    
    
    // method: mouseMoved
    protected void mouseMoved(MouseEvent mevt) {
        // extract mouse coordinates and show
        // these coordinates in xTFV and yTFV
        xTFV.setViewState(mevt.getX() + "");
        yTFV.setViewState(mevt.getY() + "");
    }
    
    
    // method: mousePressed
    protected void mousePressed(MouseEvent mevt) {
        mouseMoved(mevt);
        
        switch(plotObject.getSelectedIndex()) {
            case CIRCLE:
                graphCircle();
                break;
            
            case LINE:
                setLineStroke();
                setLineStart(mevt);
                setLineEnd(mevt);
                graphLine(TEMPORARY);
                break;
            
            default:
                break;
        }
    }
    
    
    // method: mouseDragged
    protected void mouseDragged(MouseEvent mevt) {
        mouseMoved(mevt);
        
        switch(plotObject.getSelectedIndex()) {
            case CIRCLE:
                graphCircle();
                break;
            
            case LINE:
                graphLine(TEMPORARY);
                setLineEnd(mevt);
                graphLine(TEMPORARY);
                break;
            
            default:
                break;
        }
    }
    
    
    // method: mouseReleased
    protected void mouseReleased(MouseEvent mevt) {
        mouseMoved(mevt);
        
        switch(plotObject.getSelectedIndex()) {
            case CIRCLE:
                break;
            
            case LINE:
                graphLine(TEMPORARY);
                setLineEnd(mevt);
                graphLine(PERMANENT);
                break;
            
            default:
                break;
        }
    }
    
    
    // extract radius from GUI
    protected void extractRadius() {
        radius = radiusTFV.demandDouble();
        
        // adjust radius if it is too small
        if (radius < 0.5)
            radius = 0.5;
    }
    
    
    // extract radius, x, y from GUI
    protected void extractRadiusXY() {
        extractRadius();
        
        x = xTFV.demandDouble();
        y = yTFV.demandDouble();
    }
    
    
    // method to select random color for swatch
    protected void selectRandomColor() {
        int r = MathUtilities.randomInt(0, 255);
        int g = MathUtilities.randomInt(0, 255);
        int b = MathUtilities.randomInt(0, 255);
        
        plotColor.setColor(new Color(r, g, b));
    }
    
    
    // method: graph circle
    protected void graphCircle() {
        // extract the user data: radius, x, y
        extractRadiusXY();
        
        // define the internal circle parameters
        double diameter = 2 * radius;
        double xcorner  = x - radius;
        double ycorner  = y - radius;
        
        // define the circle
        circle.setFrame(xcorner, ycorner, diameter, diameter);
        
        // get the graphics context of the buffered panel
        Graphics2D G = window.getBufferGraphics();
        
        // set anti-aliasing on
        G.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        
        // set random color if desired
        if (randomColorOption.getBooleanValue())
            selectRandomColor();
        
        // set the paint color
        G.setPaint(plotColor.getColor());
        
        // fill or draw the circle
        if (plotStyle.getSelectedIndex() == FILL)
            G.fill(circle);
        else
            G.draw(circle);
        
        // repaint the graphics window
        window.repaint();
    }
    
    
    // set the diameter from the radius TFV
    protected void setLineStroke() {
        float linewidth = 1;
        
        if (plotStyle.getSelectedIndex() == FILL) {
            extractRadius();
            
            double diameter = 2 * radius;
            
            linewidth = (int) diameter;
        }
        
        stroke = new BasicStroke
            (linewidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
    }
    
    
    // set the start of the line P1
    protected void setLineStart(MouseEvent mevt) {
        P1.setLocation(mevt.getX(), mevt.getY());    
    }
    
    
    // set the end of the line P2
    protected void setLineEnd(MouseEvent mevt) {
        P2.setLocation(mevt.getX(), mevt.getY());    
    }
    
    
    // method: graph line
    protected void graphLine(int mode) {
        // check if the selected plot object is a line
        if (plotObject.getSelectedIndex() != LINE)
            return;
        
        // define the line
        line.setLine(P1, P2);
        
        // get the graphics context of the buffered panel
        Graphics2D G = window.getBufferGraphics();
        
        if (mode == PERMANENT) {
            // set to normal painting mode
            G.setPaintMode();
            
            // set anti-aliasing on
            G.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            
            // set random color if desired
            if (randomColorOption.getBooleanValue())
                selectRandomColor();
            
            // set the paint color
            G.setPaint(plotColor.getColor());
        }
        else {
            // set to XOR mode
            G.setXORMode(Color.white);
            
            // temporarily use black
            G.setPaint(Color.black);
        }
        
        // fill or draw the line
        G.setStroke(stroke);
        G.draw(line);
        
        // repaint the graphics window
        window.repaint();
    }
    
    
    // method: clear graphics window
    protected void clear() {
        window.clearPanel();
        window.repaint();
    }
    
    
    // method: clear graphics window and reset controls
    public void reset() {
        clear();
        controls.reset();
    }
    
    
    // main program
    public static void main(String[] args) {
        JPTFrame.createQuickJPTFrame("Circle Sample 5", new CircleSample5());
    }
}