import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.lang.*;

public class MandelBrot extends Applet implements ActionListener, MouseListener, ItemListener {
	private double m_dScale;
	private int m_nMaxTry;
	private double m_dOffX,m_dOffY;
	private TextField m_tOffX,m_tOffY,m_tScale,m_tMaxTry;
	private Choice m_cColor;
	private Button m_bReset;
	private int m_nColor;
	
	public void init() {
		//De initiele waarden
		m_nMaxTry=100;
		m_dScale=0.01;
		m_dOffX=m_dOffY=0.0;
		m_nColor=0;
		//Lees de HTML parameters in
		readParameters();
		//Creeer de textvelden
		m_tOffX=new TextField(Double.toString(m_dOffX),20);
		m_tOffY=new TextField(Double.toString(m_dOffY),20);
		m_tScale=new TextField(Double.toString(m_dScale),20);
		m_tMaxTry=new TextField(Integer.toString(m_nMaxTry),20);
		m_cColor=new Choice();
		m_cColor.add("Fire");
		m_cColor.add("Ice");
		m_cColor.add("Black 'n White");
		m_cColor.add("LSD");
		m_cColor.add("Greyscale");
		m_cColor.add("Google");
		m_cColor.select(m_nColor);
		m_bReset=new Button("Reset");
		//Voeg actionlisteners toe (zodat we de waarden opnieuw kunnen uitlezen zodra de gebruiker op enter drukt)
		m_tOffX.addActionListener(this);
		m_tOffY.addActionListener(this);
		m_tScale.addActionListener(this);
		m_tMaxTry.addActionListener(this);
		m_cColor.addItemListener(this);
		m_bReset.addActionListener(this);
		//Voeg alle textvelden toe aan de interface
		this.add(m_tOffX);
		this.add(m_tOffY);
		this.add(m_tScale);
		this.add(m_tMaxTry);
		this.add(m_cColor);
		this.add(m_bReset);
		
		//Voeg een mouselistener toe voor het inzoomen
		this.addMouseListener(this);
	}
	
	private void readParameters() {
		if (getParameter("x")!=null) m_dOffX=Double.parseDouble(this.getParameter("x"));
		else m_dOffX=0;
		if (getParameter("y")!=null) m_dOffY=Double.parseDouble(this.getParameter("y"));
		else m_dOffY=0;
		if (getParameter("schaal")!=null) m_dScale=Double.parseDouble(this.getParameter("schaal"));
		else m_dScale=0.01;
		if (getParameter("max")!=null) m_nMaxTry=Integer.parseInt(this.getParameter("max"));
		else m_nMaxTry=100;
		if (getParameter("kleur")!=null) m_nColor=Integer.parseInt(this.getParameter("kleur"));
		else m_nColor=0;
	}
	
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == m_bReset) {
			readParameters();
			//Update de textvelden met de nieuwe waarden
			m_tOffX.setText(Double.toString(m_dOffX));
			m_tOffY.setText(Double.toString(m_dOffY));
			m_tScale.setText(Double.toString(m_dScale));
			m_tMaxTry.setText(Integer.toString(m_nMaxTry));
			m_cColor.select(m_nColor);
			this.repaint();
			return;
		}
		//Indien geen knop, lekker alle velden uitlezen :)
		//Lees alle textvelden uit
		m_dOffX=Double.parseDouble(m_tOffX.getText());
		m_dOffY=Double.parseDouble(m_tOffY.getText());
		m_dScale=Double.parseDouble(m_tScale.getText());
		m_nMaxTry=Integer.parseInt(m_tMaxTry.getText());
		//Repaint
		this.repaint();
	}
	
	public void itemStateChanged(ItemEvent e) {
		m_nColor=m_cColor.getSelectedIndex();
		this.repaint();
	}

	 public void mouseClicked(MouseEvent e) {
		 //Zoek uit op welk punt de gebruiker heeft gedrukt ten opzichte van het midden van het scherm, geschaald
		 double mx=(e.getX() - (this.getWidth()/2.0))*m_dScale;
		 double my=(e.getY() - (this.getHeight()/2.0))*m_dScale;
		 //Verschuif de offsets
		 m_dOffX+=mx;
		 m_dOffY+=my;
		 
		 //Bij knop1 inzoomen, anders uitzoomen
		 if (e.getButton() == MouseEvent.BUTTON1)
			 m_dScale/=2;	//Delen door 2 ipv vermenigvuldigen met 0.5, dit laatste levert namelijk rekenfoutjes op!
		 else
			 m_dScale*=2;
		 
		 //Update de textvelden met de nieuwe waarden
		 m_tOffX.setText(Double.toString(m_dOffX));
		 m_tOffY.setText(Double.toString(m_dOffY));
		 m_tScale.setText(Double.toString(m_dScale));
		 //Repaint
		 this.repaint();
	 }
	 //Nutteloze functies
	 public void mouseEntered(MouseEvent e) {}
	 public void mouseExited(MouseEvent e) {}
	 public void mousePressed(MouseEvent e) {}
	 public void mouseReleased(MouseEvent e) {}
	
	 private int getMandelGetal(double x, double y) {
		 double a=0, b=0;
		 double ouda;
		 for (int i=0; i<m_nMaxTry; i++) {
			 double bsq=b*b;
			 double asq=a*a;
			 if (bsq+asq > 4*4) //ipv de wortel van (a^2 + b^2) te nemen, squaren we de 4 = sneller
				 return i;
			 //f (a,b) = (a*a-b*b+x, 2*a*b+y)
			 ouda=a;
			 a=asq-bsq+x;
			 b=2*ouda*b+y;
		 }
		 return -1;	//Als er nog geen oplossing is gevonden, returnen we -1. Dit staat dan voor oneindig ;)
	 }
	
	 private int getMandelKleur(int getal) {
		 int red,green,blue;
		 switch (m_nColor) {
		 case 0: //Fire
			 if (getal==-1)
				 return 0xFFFFFFFF;
			 red=Math.min((getal*8),255);
			 green=Math.min(getal*5,255);
			 blue=Math.min(getal*3,255);
			 break;
		 case 1: //Ice
			 if (getal==-1)
				 return 0xFFFFFFFF;
			 blue=Math.min((getal*8),255);
			 green=Math.min(getal*5,255);
			 red=Math.min(getal*3,255);
			 break;
		 case 2: //Black 'n White
			 if (getal==-1)
				 return 0xFF000000;
			 if (getal%2 == 0)
				 return 0xFF000000;
			 else
				 return 0xFFFFFFFF;
		 case 3: //LSD
			 if (getal==-1)
				 return 0xFFFFFFFF;
			 red=(getal*15)%255;
			 green=(getal*10)%255;
			 blue=(getal*5)%255;
			 break;
		 case 4: //Grayscale
			 if (getal==-1)
				 return 0xFFFFFFFF;
			 red=green=blue=(getal*255) / m_nMaxTry;
			 break;
		 case 5:
			 if (getal==-1)
				 return 0xFFFF0000;
			 int nKleurtjes[]=new int[6];
			 nKleurtjes[0]=0xFF0000FF;
			 nKleurtjes[1]=0xFFFF0000;
			 nKleurtjes[2]=0xFFFFFF00;
			 nKleurtjes[3]=0xFF0000FF;
			 nKleurtjes[4]=0xFF00FF00;
			 nKleurtjes[5]=0xFF0000FF;
			 return nKleurtjes[getal%6]; //Break hoeft hierna niet meer ;)
		 default:
			//Bij geen geldige kleur nemen we zo'n heerlijk giftig paars kleurtje :)
		 	red=255;
		 	blue=255;
		 	green=0;
		 	break;
		 }
		 return 0xFF<<24 | red<<16 | green<<8 | blue;
	 }
	
	 private void drawMandel(Graphics g, double startx, double starty, int w, int h, double scale) {
		 int x,y;
		 //Maak eerst een doodnormale array om het plaatje in op te slaan
		 int kleurtjes[]=new int[w*h];
		 //Ga plaatje maken :)
		 for (x=0; x<w; x++) {
			 for (y=0; y<h; y++) {
				 //Bereken de x en y waardes, geschaald en wel (en als double)
				 double rx=((double)x * scale)-startx;
				 double ry=((double)y * scale)-starty;
				 //Verkrijg ons mandel getal
				 int getal=getMandelGetal(rx,ry);
				 //Kleur genereren en pslaan in het tabelletje
				 kleurtjes[(y*w)+x]=getMandelKleur(getal);
			 }
		 }
		 //Maak even een plaatje aan
		 BufferedImage image=new BufferedImage(w,h,BufferedImage.TYPE_INT_ARGB);
		 //Donder ons kleurentabelletje erin
		 image.setRGB(0,0,w,h,kleurtjes,0,w);
		 //En tekenen maar!
		 g.drawImage(image,0,0,this);
	 }
	
	 public void paint(Graphics g) {
		 drawMandel(g,
				 ((this.getWidth()*m_dScale)/2.0)-m_dOffX,
				 ((this.getHeight()*m_dScale)/2.0)-m_dOffY,
				 this.getWidth(),
				 this.getHeight(),
				 m_dScale);
	 }
}

