import java.awt.*;
import java.applet.*;
import java.awt.event.*;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.awt.Color;
import java.lang.*;

public class MandelbrotImageSet extends Applet implements ItemListener, ActionListener
{
   Button changeViewport;
   Button backupViewport;
   Button zoomIn;
   Button zoomOut;

   TextField centerX;
   TextField centerY;
   TextField windowSize;
   TextField detailLevel;

   public void init()
   {
      setLayout(new FlowLayout());

      changeViewport = new Button("Go!");
      backupViewport = new Button("Wait, go back!");
      zoomIn = new Button("+");
      zoomOut = new Button("-");
      centerX = new TextField("0.41865234", 9);
      centerY = new TextField("0.21152344", 9);
      windowSize = new TextField("0.1", 15);
      detailLevel = new TextField("2000", 15);

      add(changeViewport);
      add(backupViewport);
      add(zoomIn);
      add(zoomOut);
      add(new Label("Center Point  X:"));
      add(centerX);
      add(new Label("  Y:"));
      add(centerY);
      add(new Label("                                        "));
      add(new Label("                                        "));
      add(new Label("                                        "));
      add(new Label("Zoom Factor: "));
      add(windowSize);
      add(new Label("   Resolution: "));
      add(detailLevel);

      changeViewport.addActionListener(this);
      backupViewport.addActionListener(this);
      zoomIn.addActionListener(this);
      zoomOut.addActionListener(this);
   }

   public void paint(Graphics g)
   {
      Graphics2D g2 = (Graphics2D) g;

      viewportal  = Double.parseDouble(windowSize.getText());
      x0          = Double.parseDouble(centerX.getText());
      y0          = -Double.parseDouble(centerY.getText());
      iMax        = Integer.parseInt(detailLevel.getText());

      pixelSet.clear();
      for(int i = 0 ; i < height; i++)
      {
         for(int j = 0; j < width; j++)
         {
            double a = j*(viewportal/height)  + x0 - viewportal/2.;
            double b = i*(viewportal/width)   + y0 - viewportal/2.;
            pixelSet.add(iterationStep(a, b, j, i));
         }
      }

      for(int k = 0; k < pixelSet.size(); k++)
      {
         Pixel current = pixelSet.get(k);
         int j = current.getX();
         int i = current.getY();
         int redContent     = (int) current.getR();
         int greenContent   = (int) current.getG();
         int blueContent    = (int) current.getB();
         g2.setColor(new Color(redContent, greenContent, blueContent));
         if(i == 250) { g2.setColor(Color.RED); }
         if(j == 250) { g2.setColor(Color.RED); }
         g2.fill(new Rectangle(j + (600 - (int)width)/2, i + (800 - (int)height)/2, 1, 1));
      }
   }

   public Pixel iterationStep(double a, double b, int int1, int int2)
   {
      int k = 0;
      x = y = 0;
      for(k = 0; k< iMax; k++)
      {

         yn = 2.0*x*y + b;
         xn = x*x - y*y + a;

         x = xn;
         y = yn;

         double cr = 0, cg = 0, cb = 0;

         if(x*x+y*y > 4.)
         {
            if(k > .48*iMax && k%3 == 0)
            {
               cr = 183.0;
               cg = 150.0;
               cb = 97.0;
            }

            else if (k%3 == 0)
            {
               cr = 0;
               cg = 0;
               cb = 150;
            }

            else if(k%2 == 0)
            {
               cr = 0;
               cg = 200*(2.*k/iMax * (1. - k/iMax)) + 100; if(cg > 250) { cg = 250; }
               cb = 175;
            }

            return new Pixel(int1, int2, cr, cg, cb);
         }
      }

      return new Pixel(int1, int2, 0, 0, 0);
   }

   public void itemStateChanged(ItemEvent iE)
   {

   }

   public void actionPerformed(ActionEvent aE)
   {
      if(aE.getSource() == changeViewport)
      {
         this.updatePrvValues();

         repaint();
      }

      else if (aE.getSource() == backupViewport)
      {
         if(forwardUsed == true)
         {
            x0          = prvX;
            y0          = prvY;
            viewportal  = pViewportal;
            iMax        = pIMax;
         }

         updateTextFields();
         repaint();
      }

      else if (aE.getSource() == zoomIn)
      {
         this.updatePrvValues();
         viewportal *= 0.10;
         this.updateTextFields();
         forwardUsed = true;

         repaint();
      }

      else if (aE.getSource() == zoomOut)
      {
         this.updatePrvValues();
         viewportal *= 10.0;
         this.updateTextFields();
         forwardUsed = true;

         repaint();
      }
   }

   public void updateTextFields()
   {
      windowSize.setText(Double.toString(viewportal));
      centerX.setText(Double.toString(x0));
      centerY.setText(Double.toString(-y0));
      detailLevel.setText(Integer.toString(iMax));
   }

   public void updatePrvValues()
   {
         prvX        = x0;
         prvY        = y0;
         pViewportal = viewportal;
         pIMax       = iMax;
         forwardUsed = true;
   }

   private ArrayList<Pixel> pixelSet = new ArrayList<Pixel>();
   private double width    = 500;
   private double height   = 500;
   private double x0;
   private double y0;
   private double viewportal;
   private int    iMax, pIMax;
   private double x, y, xn, yn;
   private double prvX, prvY, pViewportal;
   private boolean forwardUsed = false;
}

