import theater.*;

import java.awt.Color;


/**
 * represents a mandelbrot set (see http://en.wikipedia.org/wiki/Mandelbrot_set)
 *
 * @author Dietrich Boles, University of Oldenburg
 * @version 1.0 (29.10.2008)
 *
 */
public class Mandelbrot extends Actor {
    final static int WIDTH = 800;
    final static int HEIGHT = 600;
    final Color colAppleman = new Color(0, 0, 50); // Color of appleman
    double pixelSize;
    double yMin;
    double xMin;
    int iterations;
    TheaterImage image;

    /**
     * creates a mandelbrot image with xMin = -2.0, yMin = -1.2, pixelSize =
     * 0.004 and iterations = 256
     */
    public Mandelbrot() {
        //this(-2.0, -1.2, 0.004, 256);
        this(-0.5756875, -0.6450625, 6.25E-5, 256);
    }

    /**
     * creates a mandelbrot image
     *
     * @param xMin
     *            left coordinate
     * @param yMin
     *            upper coordinate
     * @param pixelSize
     *            size of one pixel
     * @param iterations
     *            number of iterations
     */
    public Mandelbrot(double xMin, double yMin, double pixelSize, int iterations) {
        this.pixelSize = pixelSize;
        this.yMin = yMin;
        this.xMin = xMin;
        this.iterations = iterations;
        this.image = new TheaterImage(Mandelbrot.WIDTH, Mandelbrot.HEIGHT);
        this.drawMandelbrotImage();
        setImage(this.image);
    }

    /**
     * returns the current pixel size
     *
     * @return current pixel size
     */
    public double getPixelSize() {
        return this.pixelSize;
    }

    /**
     * changes the pixel size
     *
     * @param pixelSize
     *            new pixel size
     */
    public void setPixelSize(double pixelSize) {
        this.pixelSize = pixelSize;
        this.drawMandelbrotImage();
    }

    /**
     * returns the upper coordinate
     *
     * @return current upper coordinate
     */
    public double getYMin() {
        return this.yMin;
    }

    /**
     * changes the upper coordinate
     *
     * @param min
     *            new upper coordinate
     */
    public void setYMin(double min) {
        this.yMin = min;
        this.drawMandelbrotImage();
    }

    /**
     * returns the left coordinate
     *
     * @return current left coordinate
     */
    public double getXMin() {
        return this.xMin;
    }

    /**
     * changes the left coordinate
     *
     * @param min
     *            new left coordinate
     */
    public void setXMin(double min) {
        this.xMin = min;
        this.drawMandelbrotImage();
    }

    /**
     * returns the current number of iterations
     *
     * @return current number of iterations
     */
    public int getIterations() {
        return this.iterations;
    }

    /**
     * changes the number of iterations
     *
     * @param iter
     *            new number of iterations
     */
    public void setIterations(int iter) {
        this.iterations = iter;
        this.drawMandelbrotImage();
    }

    /**
     * Pressing the left button you can zoom into the mandelbrot set; pressing
     * any other button you can zoom out of the mandelbrot set. The clicked
     * point is the new center of the image.
     */
    public void mousePressed(MouseInfo info) {
        this.xMin += ((info.getColumn() - (Mandelbrot.WIDTH / 2)) * this.pixelSize);
        this.yMin += ((info.getRow() - (Mandelbrot.HEIGHT / 2)) * this.pixelSize);

        if (info.getButton() == 1) {
            this.xMin += ((Mandelbrot.WIDTH * this.pixelSize) / 4);
            this.yMin += ((Mandelbrot.HEIGHT * this.pixelSize) / 4);
            this.pixelSize /= 2;
        } else {
            this.xMin -= ((Mandelbrot.WIDTH * this.pixelSize) / 2);
            this.yMin -= ((Mandelbrot.HEIGHT * this.pixelSize) / 2);
            this.pixelSize *= 2;
        }

        this.drawMandelbrotImage();
    }

    /**
     * draws a mandelbrot image using the current values for xMin, yMin,
     * pixelSize and iterations
     */
    private void drawMandelbrotImage() {
        this.image.clear();

        Complex c = new Complex(0.0, this.yMin);

        for (int y = 0; y < Mandelbrot.HEIGHT; y++) {
            //System.out.println("y = " + y);
            c = new Complex(this.xMin, c.getImaginaryPart());

            for (int x = 0; x < Mandelbrot.WIDTH; x++) {
                int iterationsC = this.checkC(c);

                if (iterationsC == this.iterations) {
                    this.image.setColorAt(x, y, this.colAppleman);
                } else {
                    Color colPeriphery = new Color(255 -
                            (iterationsC % 2 * 125),
                            255 - (iterationsC % 7 * 36),
                            255 - (iterationsC % 3 * 85));
                    this.image.setColorAt(x, y, colPeriphery);
                }

                c.add(new Complex(this.pixelSize, 0));
            }

            c.add(new Complex(0.0, this.pixelSize));
        }
    }

    /**
     * performs the mandelbrot calculations
     *
     * @param c
     *            current pixel as complex number
     * @return number of iterations
     */
    private int checkC(Complex c) {
        Complex z = new Complex(0.0, 0.0);
        Complex z_minus1 = new Complex();
        int i;

        for (i = 0; i < this.iterations; i++) {
            z = Complex.add(Complex.mult(z_minus1, z_minus1), c);

            if (((z.getRealPart() * z.getRealPart()) +
                    (z.getImaginaryPart() * z.getImaginaryPart())) > 4) {
                return i;
            }

            z_minus1 = new Complex(z);
        }

        return i;
    }
}
