/**
 * sp2.java
 *
 * La symétrie perturbée, d'après l'ouvert n°49 de décembre 1987
 * fabrique une chaîne de 0 et de 1 (1 = g 0 = d) dans un byte[]
 * pas de récursivité
 * le calcul de la chaîne des déplacement ne se fait que si Niter, le nombre d'itérations a augmenté
 * pour éviter des calculs inutiles on copie le byte[] dans un byte[] plus grand et on poursuit les calculs
 *
 * Jean-Paul Quelen 18/04/20
 */

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class sp2 extends JPanel implements MouseListener {
	static final long serialVersionUID = 200418L;
// dir =	0	1	2	3	4	5	6	7
//			N	NE	E	SE	S	SO	O	NO
	int xmax, ymax, Niter, X0, Y0, dir, Xp, Yp, NiterOld;
	double l, x, y, ls2;
	char or;
	TextField tfn, tfl, tor, lcalc;
	byte[] bits = {-128, 64, 32, 16, 8, 4, 2, 1};
	int fin;
	byte[] mvt;

	public sp2() {
		Font f = new Font (Font.SANS_SERIF, Font.PLAIN, 10);
		Label lbl;

		add (lbl = new Label ("Orientation initiale (N, S, O, E)"));
		nlab (lbl, f);
		or = 'E';
		add (tor = new TextField ("E", 1));

		add (lbl = new Label ("Itérations : "));
		nlab (lbl, f);
		Niter = 2;
		add (tfn = new TextField ("2", 10));
		NiterOld = 1;
		mvt = new byte [1];
		mvt[0] = -128;
		add (lbl = new Label ("Longueur d'un pas (en pixels)"));
		nlab (lbl, f);
		l = 8.0;
		add (tfl = new TextField ("8.0", 10));
		ls2 = 0.5 * l;

		add (lcalc = new TextField (20));
		lcalc.setFont (f);
		lcalc.setBackground (Color.black);
		lcalc.setForeground (Color.white);
		addMouseListener (this);
	}

	private void nlab (Label lbl, Font f) {
		lbl.setFont (f);
		lbl.setBackground (Color.black);
		lbl.setForeground (Color.white);
	}

	public void init() {
		x = Xp = X0;
		y = Yp = Y0;
		dir = "NESO".indexOf (or) * 2;
	}

	public void avance (Graphics g, double l) {
		switch (dir) {
			case 1, 2, 3 :
				x += l;
				break;
			case 5, 6, 7 :
				x -= l;
		}
		switch (dir) {
			case 3, 4, 5 :
				y += l;
				break;
			case 7, 0, 1 :
				y -= l;
		}
		int X = (int) x;
		int Y = (int) y;
		if (X != Xp || Y != Yp) {
			g.drawLine (Xp, Yp, X, Y);
			Xp = X;
			Yp = Y;
		}
	}

	public void trace (Graphics g) {
		avance (g, l);
		int imvt = 0;
		int iimvt = 0;
		fin = (1 << Niter) - 1;						// nbre de bits à lire
		for (int i = 0; i < fin; i++) {
			int dg = ((mvt [imvt] & bits [iimvt]) == 0) ? 1 : 7;
			avance (g, l);
			dir = (dir + dg) & 7;			// ne pas faire (dir - 1) % 8
			avance (g, ls2);
			dir = (dir + dg) & 7;
			avance (g, l);			
			iimvt++;
			if (iimvt >= 8) {
				iimvt = 0;
				imvt++;
			}
		}
		avance (g, l);
	}

	public void update (Graphics g) {
		paint (g);
	}

	private int dimbyte (int n) {
			if (n <= 3)
				return 1;
			return n = 1 << (n - 3);
	}
	
	private void calc (int Ndebut, int Nfin) {
		for (int i = Ndebut; i < Nfin; i++) {
				fin = (1 << i) - 1;			// nbre de bits à lire
				int cop = (2 << i) - 2;		// index du début de la zone de copie
				int imvt = 0;
				int iimvt = 0;
				int icop = cop / 8;
				int iicop = cop % 8;
				for (int j = 0; j < fin; j++) {
					if ((mvt [imvt] & bits [iimvt]) == 0) {
						mvt [icop] |= bits [iicop];
					}
					iimvt++;
					if (iimvt >= 8) {
						iimvt = 0;
						imvt++;
					}
					iicop--;
					if (iicop < 0) {
						iicop = 7;
						icop--;
					}
				}
				mvt [icop] |= bits [iicop];	// 1 central
			}
	}


	public void paint (Graphics g) {
		if (xmax != getWidth() || ymax != getHeight()) {
			xmax = getWidth();
			ymax = getHeight();
			X0 = xmax / 2;
			Y0 = ymax / 2;
		}
		init();

		if (Niter > NiterOld) {
			lcalc.setText ("Calcul en cours");
			int dim = dimbyte (Niter);
			int mvtl = mvt.length;
			int dimcopy = dimbyte (NiterOld);
			if (dim > mvtl) {
				byte[] mvt1 = new byte [dim];
				System.arraycopy (mvt, 0, mvt1, 0, dimcopy);
				mvt = mvt1;
			}
			calc (NiterOld, Niter);
			NiterOld = Niter;
		}

		lcalc.setText ("Dessin en cours");
		g.setColor (Color.black);
		g.fillRect (0, 0, xmax, ymax);
		g.setColor (Color.white);
		trace (g);
		lcalc.setText ("");
	}

	public void mousePressed (MouseEvent e) {
		char or1 = tor.getText().toUpperCase().charAt(0);
		if ("NSOE".indexOf (or1) != -1) {
			or = or1;
		}
		tor.setText (Character.toString (or));

		int niter1 = Niter;
		try {
			niter1 = Integer.parseInt (tfn.getText());
			if (niter1 > 0)
				Niter = niter1;
		}
		catch (NumberFormatException nfe) {}
		tfn.setText (Integer.toString (Niter));

		double l1 = l;
		try {
			l1 = Double.parseDouble (tfl.getText());
			if (l1 > 0.0)
				l = l1;
		}
		catch (NumberFormatException nfe) {}
		tfl.setText (Double.toString (l));
		ls2 = 0.5 * l;
		X0 = e.getX();
		Y0 = e.getY();
		init();
		repaint();
	}

	public void mouseReleased (MouseEvent e) {}
	public void mouseClicked (MouseEvent e) {}
	public void mouseEntered (MouseEvent e) {}
	public void mouseExited (MouseEvent e) {}

////////////////////////////////////////////////////////////////////////////////

	public static void main (String [] args) {
		int w = 800;
		int h = 800;

		sp2 s = new sp2();

		JFrame f = new JFrame ("La symétrie perturbée v2");
		f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		f.add (s);
		f.setSize (w, h);
		f.setVisible (true);
	}

}

