
import java.util.Scanner;
import java.awt.*;
import javax.swing.*;

/**
 * A collection of methods for drawing ferns.
 *
 * @author Mark Young
 */
public class FernWindow extends JFrame {

    // ---------- Here is the program part -------------------------------- //

    /** default width for a fern window */
    public static final int DEFAULT_WIDTH = 800;
    /** default height for a fern window */
    public static final int DEFAULT_HEIGHT = 600;

    // here is the program -- the class is below
    public static void main(String[] args) {
        Scanner kbd = new Scanner(System.in);

        // introduce yourself
        System.out.println("\n\n"
                + "Fern Drawing Program\n"
                + "--------------------\n\n");

        // get window parameters (all windows the same size)
        System.out.print("How big should the windows be (width height)? ");
        int width = kbd.nextInt();
        int height = kbd.nextInt();
        kbd.nextLine();
        if (width <= 0) width = DEFAULT_WIDTH;
        if (height <= 0) height = DEFAULT_HEIGHT;

        // repeat
        String resp;
        do
        {
            // get root location
            System.out.println();
            System.out.print("Where should the root of the fern be? ");
            int x = kbd.nextInt();
            int y = kbd.nextInt();
            kbd.nextLine();

            // get angle of growth
            System.out.print("What angle should the fern grow at "
                    + "(180 = straight up)? ");
            int angle = kbd.nextInt();
            kbd.nextLine();

            // get fern size
            System.out.print("And what height should the fern be? ");
            int size = kbd.nextInt();
            kbd.nextLine();

            // draw that fern in a new window
            FernWindow win = new FernWindow(width, height, x, y, angle, size);
            win.setVisible(true);

            // ask if there's more
            System.out.print("another (y/n)> ");
            resp = kbd.nextLine().toUpperCase();
        } while (resp.startsWith("Y"));
        System.exit(0);
    }

    // ---------- below is the class -------------------------------------- //

    /** the part of the window we draw in */
    private Graphics canvas;
    /** the x-coordinate of the fern's root */
    private double rootX;
    /** the y-coordinate of the fern's root */
    private double rootY;
    /** the angle the fern grows away from its root (180 = straight up) */
    private double rootAngle;
    /** the root-to-tip length of the fern (it'll actually be a bit shorter) */
    private double fullSize;

    /**
     * Create a window for a fern to be drawn in.
     *
     * @param width  the outer width of the window
     * @param height the outer height of the window
     * @param x      the x-coordinate of the fern's root
     * @param y      the y-coordinate of the fern's root
     * @param angle  the angle the fern grows from its root
     * @param size   the nominal size of the fern
     */
    public FernWindow(int width, int height, 
            double x, double y, double angle, double size) {
        super("A Fern by MYoung");
        setSize(width, height);
        setBackground(Color.BLACK);
        rootX = x;
        rootY = y;
        rootAngle = angle;
        fullSize = size;
    }

    /**
     * Respond to the computer's request to paint the window.
     * This method called by the computer when the window gets shown.
     *
     * @param g     the canvas to draw on -- provided by the computer.
     */
    public void paint(Graphics g) {
        // use the canvas the computer provides
        canvas = g;

        // choose a darkish green pen
        canvas.setColor(new Color(0, 127, 0));

        // draw the fern
        drawFern(rootX, rootY, rootAngle, fullSize);
    }

    /**
     * Draw a fern on my own canvas.
     * This is a recursive method. You should understand how it works.
     *
     * @param x     the x-coordinate of the fern's root
     * @param y     the y-coordinate of the fern's root
     * @param angle the angle the fern grows from its root
     * @param size  the nominal size of the fern
     */
    public void drawFern(double x, double y, double angle, double size) {
        // draw the stem and note where it ends
        double length = size * 0.5;
        double[] end = drawStem(x, y, angle, length);

        // if the fern is big...
        if (size > 1.0) {
            // ...draw smaller ferns for the fronds
            double smaller = size * 0.4;
            drawFern(end[0], end[1], angle + 60, smaller);
            drawFern(end[0], end[1], angle, smaller);
            drawFern(end[0], end[1], angle - 60, smaller);
        }
    }

    /**
     * Draw the stem of a fern, returning the coordinates of its upper end.
     * (You do not need to understand how this method works.)
     *
     * @param x         the x-coordinate of the stem's root
     * @param y         the y-coordinate of the stem's root
     * @param angle     the angle the stem grows from its root
     * @param length    the length of the stem
     * @return  an array with the x and y coordinates of the stem's top
     *          in its two cells
     */
    public double[] drawStem(double x, double y, double angle, double length) {
        // convert from degrees to radians for the sin & cos functions
        double radians = Math.toRadians(angle);

        // figure out (and remember) where the line should end
        double[] end = new double[2];
        end[0] = x + Math.sin(radians) * length;
        end[1] = y + Math.cos(radians) * length;

        // draw the line
        drawLine(x, y, end[0], end[1]);

        // return the end-point
        return end;
    }

    /**
     * Draw a line on my convas.
     * (You do not need to understand how this method works.)
     *
     * @param x1    the x-coordinate of the start of the line
     * @param y1    the y-coordinate of the start of the line
     * @param x2    the x-coordinate of the end of the line
     * @param y2    the y-coordinate of the end of the line
     */
    public void drawLine(double x1, double y1, double x2, double y2) {
        // round off the start/end coordinates...
        int startX = (int)Math.round(x1);
        int startY = (int)Math.round(y1);
        int endX = (int)Math.round(x2);
        int endY = (int)Math.round(y2);

        // ... because the canvas expects to be given integer coordinates
        canvas.drawLine(startX, startY,  endX, endY);
    }

}

