// --------------------------------------------------------------------
// Author: Maciej Dziemianczuk
// www.faces-of-nature.art.pl/
// maciek.ciupa at gmail com
//
// Henon map

import java.awt.*;
import java.applet.Applet;

import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;




// --------------------------------------------------------------------
// Drawing the arrangement of coordinates
class CCoordinate extends java.applet.Applet
{
    public double x0, y0;
    public double xn, yn;

    public int Width, Height;

    public int OX, OY;
    public double dx, dy;

    // -------------------------------------------
    public CCoordinate(int _width, int _height, double _x0, double _xn, double _y0, double _yn)
    {
    	Width = _width;
    	Height = _height;
    	
        x0 = _x0;
        y0 = _y0;
        xn = _xn;
        yn = _yn;

        OX = 15;
        OY = Height + 15;
        dx = (xn - x0)/(double)(Width);
        dy = (yn - y0)/(double)(Height);
    }
    // -------------------------------------------
    // return X value on the coordinate system
    public int getX(double _x)
    {
        return OX + (int)((_x - x0)/dx);
    }
    public int getY(double _y)
    {
        return OY - (int)((_y - y0)/dy);
    }
	
	// -------------------------------------------
	// return double from integer pixel coordinate
	public double getX(int _x)
	{
		return (_x - OX)*dx + x0;
	}
	public double getY(int _y)
	{
		return (OY - _y)*dy + y0;
	}
	
	public void drawPoint(Graphics g, double _x, double _y)
	{
		if (_x > x0 && _x < xn && _y > y0 && _y < yn)
		{
			int x = getX(_x);
			int y = getY(_y);
			g.drawLine(x, y, x, y);
		}
	}
	
	public void drawColumn(Graphics g, double _x, double _y, int width)
	{
		if (_x > x0 && _x < xn && _y > y0 && _y < yn)
		{
			int x = getX(_x);
			int y = getY(_y);
			g.fillRect(x, y, width, getY(0.0) - getY(_y));
		}
	}
	
    // -------------------------------------------
    public void paint(Graphics g)
    {
        // draw axis
        g.drawRect(OX,OY-Height,Width,Height);
        g.drawLine(getX(x0)-3,getY(0.0),getX(xn)+10,getY(0.0)); // X axis
        g.drawLine(getX(xn)+10,getY(0.0), getX(xn)+5,getY(0.0)-3);
        g.drawLine(getX(xn)+10,getY(0.0), getX(xn)+5,getY(0.0)+3);
        
        g.drawLine(getX(0.0),getY(y0)+3,getX(0.0),getY(yn)-10); // Y axis
		g.drawLine(getX(0.0),getY(yn)-10, getX(0.0)-3,getY(yn)-5);
		g.drawLine(getX(0.0),getY(yn)-10, getX(0.0)+3,getY(yn)-5);
		
		// draw units
		for (int i=0; i<((xn-x0)*10); ++i)
		{
			g.drawLine(getX(x0+0.1*i), getY(0.0)-2,getX(x0+0.1*i), getY(0.0)+2);
		}
		for (int i=0; i<((yn-y0)*10); ++i)
		{
			g.drawLine(getX(0.0)-2, getY(y0+0.1*i),getX(0.0)+2, getY(y0+0.1*i));
		}
		
		g.drawString (Double.toString(x0), getX(x0)+4, getY(0.0)+14 );
		g.drawString (Double.toString(xn), getX(xn)-20, getY(0.0)+14 );
		
		if (xn > 1.0)
		g.drawString (Double.toString(1.0), getX(1.0)-20, getY(0.0)+14 );
		
		g.drawString (Double.toString(y0), getX(0.0)-30, getY(y0)-10 );
		g.drawString (Double.toString(yn), getX(0.0)-30, getY(yn)+13 );
		
    }
}

//---------------------------------------------------------------------
// Main class to generate Henon map
class CHenonGraph extends java.applet.Applet
{
	public CCoordinate Coord;
	double x0,xn,y0,yn;
	double alpha,beta;
	int Iterations;
	int Mode = 0;
	
	// array for atractor's points
	int Atractor[];
	int SumPoints;
	
	int __iterPass = 1000;
	
	//-----------------------------------------------------------------
	public CHenonGraph(double _x0, double _xn, double _y0, double _yn, double _alpha, double _beta, int _Iterations, int _Mode)
	{
    	Mode = _Mode;
    	x0 = _x0;
        y0 = _y0;
        xn = _xn;
        yn = _yn;
        alpha = _alpha;
        beta = _beta;
        Iterations = _Iterations;
	}
	
	//-----------------------------------------------------------------
	public void paint ( Graphics g )
	{
		// get size of window
		Rectangle r = getBounds();
		Coord = new CCoordinate(r.width-30, r.height-30, x0, xn, y0, yn);
		
		Atractor = new int[Coord.Height * Coord.Width];
		
		Coord.paint(g);
		
		if (Mode == 0 || Mode == 1)
			ParamA( g );
		else
		if (Mode == 2 || Mode == 3)
			ParamB( g );
		else
			ParamAB( g );
		
	}
	// ----------------------------------------------------------------
	public void ParamA( Graphics g )
	{
		
		double X = 0, Y = 0;
		double nX, nY;
		
		double A = Coord.x0;
		double B = beta;
		
		// ----------------------------------------------------------
		for (int i_par=0; i_par < Coord.Width; ++i_par)
		{
			X = 0.5;
			Y = 0.5;
			for (int iter=0; iter<__iterPass+Iterations; ++iter)
			{
				nX = Y + 1 - A*X*X;
				nY = B * X;
				
				X = nX;
				Y = nY;
				
				if (iter > __iterPass)
				{
					if (Mode == 0)
						Coord.drawPoint(g, A, X);
					else if (Mode == 1)
						Coord.drawPoint(g, A, Y);
				}
			}
			
			A += Coord.dx;
		}
	}
	
	// ----------------------------------------------------------------
	public void ParamB( Graphics g )
	{
		
		double X = 0, Y = 0;
		double nX, nY;
		
		double A = alpha;
		double B = Coord.x0;
		
		
		// ----------------------------------------------------------
		for (int i_par=0; i_par < Coord.Width; ++i_par)
		{
			X = 0.5;
			Y = 0.5;
			for (int iter=0; iter<__iterPass+Iterations; ++iter)
			{
				nX = Y + 1 - A*X*X;
				nY = B * X;
				
				X = nX;
				Y = nY;
				
				if (iter > __iterPass)
				{
					if (Mode == 2)
						Coord.drawPoint(g, B, X);
					else if (Mode == 3)
						Coord.drawPoint(g, B, Y);
				}
			}
			
			B += Coord.dx;
		}
	}
	
	// ----------------------------------------------------------------
	public void ParamAB( Graphics g )
	{
		SumPoints = 0;
		double X, Y, aX = 0, aY = 0;
		double nX, nY;
		
		double A = alpha;
		double B = beta;
		
		int i_x, i_y, iter;
		
		// ----------------------------------------------------------
		X = 0.0;
		Y = 0.0;
		
		for (iter=0; iter<__iterPass+Iterations; ++iter)
		{
			nX = aY + 1 - A*aX*aX;
			nY = B * aX;
					
			aX = nX;
			aY = nY;
			int idx, idy, id;
			
			if (iter > __iterPass)
			{
				Coord.drawPoint(g, aX, aY);
				
				if (aX > Coord.x0 && aX < Coord.xn && aY > Coord.y0 && aY < Coord.yn)
				{
					idy = (Coord.getY(Coord.y0) - Coord.getY(aY));
					idx = (Coord.getX(aX) - Coord.getX(Coord.x0));
					id = idy*Coord.Width + idx;
					
					if (id < Coord.Width*Coord.Height)
					{
						if (Atractor[id] != 1)
						{
							Atractor[id] = 1;
							++ SumPoints;
						}
					}
				}
			}
		}
		double value = Math.log(SumPoints) / Math.log(Coord.Width);		
		System.out.println("Hausdorf: dx = " + Coord.Width + " dy = " + Coord.Height +" max = " + (Coord.Width*Coord.Height) + " ok = " + SumPoints + " value = " + value );

	}
	
};

//---------------------------------------------------------------------
//---------------------------------------------------------------------
public class CHenon extends Applet implements ItemListener, MouseListener
{
        Choice mode, examples;
        TextField tf1,  tf2, tf3, tf4, tf5, tf6, tf7, tfmx,tfmy;
        Button but1, but2;

        CHenonGraph tablet;
        
        int Mode = 4;
        
        
        double x0 = -1.4,
        	   xn = 1.4,
        	   y0 = -0.4,
        	   yn = 0.4;
        
        double alpha = 1.4, 
        	   beta=0.3;
        
        int Iterations = 100000;

        //-----------------------------------------------------------------
        public void init()
        {
                //getSize().width
                tablet = new CHenonGraph(x0,xn,y0,yn,alpha,beta,Iterations,Mode);
				tablet.addMouseListener(this);
				
                tf1 = new TextField(Double.toString(x0), 2);
                tf2 = new TextField(Double.toString(xn), 2);
                tf3 = new TextField(Double.toString(y0), 2);
                tf4 = new TextField(Double.toString(yn), 2);
                tf5 = new TextField(Integer.toString(Iterations), 6);
                tf6 = new TextField(Double.toString(alpha), 2);
                tf7 = new TextField(Double.toString(beta), 2);
                tfmx = new TextField(Double.toString(0.0), 5);
                tfmy = new TextField(Double.toString(0.0), 5);
                
                but1 = new Button("Draw");
				but2 = new Button("Draw");
				

                mode = new Choice();
				mode.addItem( "X(A)" );
				mode.addItem( "Y(A)" );
				mode.addItem( "X(B)" );
				mode.addItem( "Y(B)" );
				mode.addItem( "Y(X)" );
				
				mode.select(Mode);
                
                setLayout( new BorderLayout() );

                Panel sPanel = new Panel();
                sPanel.setLayout(new GridLayout(2,1));
                
                Panel sNPanel = new Panel();
                Panel sSPanel = new Panel();
                Panel nPanel = new Panel();

				
				
                sNPanel.setLayout( new FlowLayout() );

                // Labels  in South panel

                Label L1 = new Label("x:");
                L1.setBackground(Color.gray);
                sNPanel.add( L1 );
                sNPanel.add( tf1 );
                L1 = new Label(" -");
                L1.setBackground(Color.gray);
                sNPanel.add( L1 );
                sNPanel.add( tf2 );

                L1 = new Label("y:");
                L1.setBackground(Color.gray);
                sNPanel.add( L1 );
                sNPanel.add( tf3);
                L1 = new Label(" -");
                L1.setBackground(Color.gray);
                sNPanel.add( L1 );
                sNPanel.add( tf4);
                
                L1 = new Label("A:");
                L1.setBackground(Color.gray);
                sNPanel.add( L1 );
                sNPanel.add( tf6);
                
                L1 = new Label("B:");
                L1.setBackground(Color.gray);
                sNPanel.add( L1 );
                sNPanel.add( tf7);
                
                L1 = new Label("Mode:");
                L1.setBackground(Color.gray);
                sNPanel.add( L1 );
                sNPanel.add( mode );
                
                L1 = new Label("Iter.:");
                L1.setBackground(Color.gray);
                sNPanel.add( L1 );
                sNPanel.add( tf5 );

                sNPanel.add( but1 );

                // Labels and others in north panel
                L1 = new Label("Henon Map");

                Font ft = new Font ( "Tahoma", Font.BOLD, 12 );
                L1.setFont(ft);

                L1.setBackground(Color.white);
                nPanel.add( L1 );


				

                add( "North", nPanel );
                add( "South", sPanel );
                	
                add( "Center", tablet );
				
				sPanel.add("North", sNPanel);
				sPanel.add("South", sSPanel);
				
				// south menu panel
				L1 = new Label("Example parameters:");
				L1.setBackground(Color.gray);
				sSPanel.add(L1);
				
				examples = new Choice();
				examples.addItem( "Attractor" );
				examples.addItem( "Bifurcation diagram 1" );
				examples.addItem( "Bifurcation diagram 2 (enlarged)" );
				examples.addItem( "Bifurcation diagram 3 (enlarged)" );
				
				sSPanel.add(examples);
				
				L1 = new Label("Mouse pos:");
				L1.setBackground(Color.gray);
				sSPanel.add(L1);
				
				sSPanel.add(tfmx);
				sSPanel.add(tfmy);
				
                tablet.setBackground(Color.white);
                sPanel.setBackground(Color.gray);
                sNPanel.setBackground(Color.gray);
                sSPanel.setBackground(Color.gray);
                nPanel.setBackground(Color.white);
				

                //mode.addActionListener(itemStateChanged);
				mode.addItemListener( this );
				examples.addItemListener( this );

        }

        public boolean action(Event e, Object arg)
        {

                if (e.target == but1)
                {

                        x0 = Double.parseDouble( tf1.getText() );
                        xn = Double.parseDouble( tf2.getText() );
                        y0 = Double.parseDouble( tf3.getText() );
                        yn = Double.parseDouble( tf4.getText() );
                        Iterations = Integer.parseInt( tf5.getText() );
                        alpha = Double.parseDouble( tf6.getText() );
                        beta = Double.parseDouble( tf7.getText() );
                        
						Mode = mode.getSelectedIndex();
                }

                remove( tablet );
                tablet = new CHenonGraph(x0,xn,y0,yn,alpha,beta,Iterations,Mode);
                tablet.setBackground(Color.white);
                tablet.addMouseListener(this);
                
                add( "Center", tablet );
                validate();

                return true;
        }
        
    //-----------------------------------------------------------------
	public void itemStateChanged( ItemEvent evt )
	{
		
		
		//tf1 = new TextField(Double.toString(x0), 2);
        //tf2 = new TextField(Double.toString(xn), 2);
        //tf3 = new TextField(Double.toString(y0), 2);
        //tf4 = new TextField(Double.toString(yn), 2);
        //tf5 = new TextField(Integer.toString(Iterations), 6);
        //tf6 = new TextField(Double.toString(alpha), 2);
        //tf7 = new TextField(Double.toString(beta), 2);
                
		// example parameters
		if ( evt.getSource() == examples)
		{
			switch (examples.getSelectedIndex())
			{
				case 0: //Attractor
					tf1.setText("-1.4");
					tf2.setText("1.4");
					tf3.setText("-0.4");
					tf4.setText("0.4");
					tf6.setText("1.4");
					tf7.setText("0.3");
					mode.select(4);
					tf5.setText("100000");
				break;
				case 1: //Bifurcation diagram 1
					tf1.setText("-0.1");
					tf2.setText("1.4");
					tf3.setText("-1.0");
					tf4.setText("1.3");
					tf6.setText("1.6");
					tf7.setText("0.2");
					mode.select(0);
					tf5.setText("300");
				break;
				case 2: //Bifurcation diagram 2 (enlarged)
					tf1.setText("1.11");
					tf2.setText("1.2");
					tf3.setText("0");
					tf4.setText("0.2");
					tf6.setText("1.6");
					tf7.setText("0.2");
					mode.select(0);
					tf5.setText("2000");
				break;
				case 3: //Bifurcation diagram 3 (enlarged)
					tf1.setText("1.14");
					tf2.setText("1.153");
					tf3.setText("0.075");
					tf4.setText("0.136");
					tf6.setText("1.6");
					tf7.setText("0.2");
					mode.select(0);
					tf5.setText("2000");
				break;
			}
			
		}

	}
	
	//-----------------------------------------------------------------
	public void mouseClicked(MouseEvent e)
	{
		tfmx.setText(""+tablet.Coord.getX(e.getX()));
		tfmy.setText(""+tablet.Coord.getY(e.getY()));
		
		//System.out.println("(x,y): " + tablet.Coord.getX(e.getX()) + "," + tablet.Coord.getY(e.getY()) );
		//(0.0,e.getX(), e.getY());
		tablet.Coord.getX(1.0);
    }
	public void mousePressed(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}

};

