/**
 * 
 */
package p5;
import anar.*;


// import geometry.Point3D;



import processing.core.PApplet;
import rad.*;


/**
 * @author gll
 * 
 */
public class Test01aDeployRadF extends PApplet implements RadObserver {

	/*
	 * Example for Anar library by Guillaume LaBelle + Julien Nembrini
	 * http://anar.ch
	 */



	Obj   myObject;

	Param angle    = new Param(0.3f);
	Param invAngle = new Param( -angle.get());


	// /////////////////////////////////
	// /////////////////////////////////
	// /////////////////////////////////

	public void setup(){

		// size(screen.width,screen.height,OPENGL);
		size(1000,500,OPENGL);
		Anar.init(this);
		Anar.drawAxis(true);

		simThread = new RadEngine(this,10f);


		generatorTranslationLimitedSet();
		generatorRotationLimitedSet();
		generatorInit();

		generatorAddOneFace();

		Face.globalRender = new RenderFaceDoubleSide(new AColor(255,180,180),new AColor(220));
	}

	// /////////////////////////////////
	// /////////////////////////////////
	// /////////////////////////////////


	public void draw(){
		if(frameCount%2==0)
			background(255);
		else
			background(254);

		myObject.draw();
		if(isAddingFace)
			generatorAddOneFace();

		// angle.set(angle.get()+0.01f);
		// invAngle.set( -angle.get());
	}


	boolean isAddingFace = true;


	public void keyPressed(){

		switch(key){
		case 'q':
			simThread.simulate(myObject);
			simThread.runNow();
			isSimRunning = true;
			if(isSimRunning)
				isSimRunning = false;
			break;
		case ' ':
			generatorAddOneFace();
			break;
		case 'w':
			//        MetaRad.switchRender(myObject);
			break;
		case 'e':
			//        MetaRad.switchToAverageRender(myObject);
			break;
		case 'p':
			Scene.autoSeek = false;
			break;
		case 'o':
			isAddingFace = (isAddingFace) ? false:true;
			break;
		case 'r':
			angle.set(0);
			invAngle.set( -angle.get());
			break;
		case 't':
			angle.set(0.3f);
			invAngle.set( -angle.get());
			break;
		case 'a':
			TextIO.write( ((Object)this).getClass().getName()+".lsp",myObject.toAutocad());
			break;

		}

	}


	// /////////////////////////////////
	// /////////////////////////////////
	// /////////////////////////////////
	Transform[] sides;

	void generatorTranslationLimitedSet(){
		// We limit the set of transforms to three different
		// It will produce a limited set of different patterns

		// The elementary operation
		Translate modulor = new Translate(Anar.PtNull(0,0,1));


		// Create 3 subsequent Transform from this one
		// I use TransformLinear to combine them as a group
		Transform side0 = new Transform();
		side0.add(modulor);

		Transform side1 = new Transform();
		side1.add(modulor);
		side1.add(modulor);

		Transform side2 = new Transform();
		side2.add(modulor);
		side2.add(modulor);
		side2.add(modulor);

		// Then I have three different transforms from the first one
		// They have different lengths
		// Remark, I ends up with only one parameter


		// Combine them in a table (it will be usefull when randomized)
		// Here I need to remember that 0 is short, 1 normal and 2 is long
		sides = new Transform[3];
		sides[0] = side0;
		sides[1] = side1;
		sides[2] = side2;
	}


	// /////////////////////////////////
	// /////////////////////////////////
	// /////////////////////////////////

	Transform[] rotations;

	void generatorRotationLimitedSet(){
		// It's good for sides
		// Now let's create a rotation (let's keep it simple with only one rotation)
		// On the chantier, it correspond to uniforms clips between panels

		// I need to create a RotateZ as it will be used inside Allingn
		// Allign will allign an axis to Z coordinate then apply a transform and
		// Come back to initial state
		RotateZ myRotation = new RotateZ(angle);
		RotateZ myInvRotation = new RotateZ(invAngle);

		Transform rotation_2 = new Transform();
		rotation_2.add(myInvRotation);
		rotation_2.add(myInvRotation);

		Transform rotation_1 = new Transform();
		rotation_1.add(myInvRotation);

		Transform rotation0 = new Transform();

		Transform rotation1 = new Transform();
		rotation1.add(myRotation);

		Transform rotation2 = new Transform();
		rotation2.add(myRotation);
		rotation2.add(myRotation);

		rotations = new Transform[5];
		rotations[0] = rotation_2;
		rotations[1] = rotation_1;
		rotations[2] = rotation0;
		rotations[3] = rotation1;
		rotations[4] = rotation2;
	}


	// /////////////////////////////////
	// /////////////////////////////////
	// /////////////////////////////////


	Pts ptsA = new Pts();
	Pts ptsB = new Pts();

	void generatorInit(){
		myObject = new Obj();

		ptsA = new Pts();
		ptsB = new Pts();

		ptsA.color(new AColor(0,0,255));
		ptsB.color(new AColor(255,150,150));

		// I need two initial points
		// This is where I set the side length of the whole thing
		Pt originA = Anar.Pt(0,0,0,"originA");
		Pt originB = Anar.Pt(0,10,0,"originB");

		// Add them to the list
		ptsA.add(originA);
		ptsB.add(originB);

		// (Update) We need those POints to orient the translation
		PtDER originAA = Anar.Pt(originA);
		PtDER originBB = Anar.Pt(originB);

		originAA.apply(sides[0]);
		originBB.apply(sides[0]);

		// Add them to the list
		ptsA.add(originAA);
		ptsB.add(originBB);

		// As the form is an inerplay between aNewPoint and a previousPt
		// I<ll create two fields to track them
		// Note, it's not derrived, it is the point itself (Pt previousA =
		// Anar.Pt(originA))
		previousA = originAA;
		previousB = originBB;

		previousAA = originA;
		previousBB = originB;

	}


	// /////////////////////////////////
	// /////////////////////////////////
	// /////////////////////////////////

	Pt  previousA;
	Pt  previousB;
	Pt  previousAA;
	Pt  previousBB;

	int delta     = 0;
	int faceCount = 0;

	void generatorAddOneFace(){

		PtDER newPtA = Anar.Pt(previousA,"A"+faceCount);
		PtDER newPtB = Anar.Pt(previousB,"B"+faceCount++);

		// Choose one translations from our set.
		// (we won't accept delta to be too long as we want to keep the set of faces
		// to a small set of cases
		int lengthA, lengthB; // Correspond to sides[0,1,2]

		do{
			lengthA = (int)random(sides.length);
			lengthB = (int)random(sides.length);
		} while (Math.abs(lengthA-lengthB+delta)>3); // Here I limit the maximum
		// sitance between paths (both
		// ways)

		// Update new delta state
		delta += lengthA-lengthB;


		// Create Rotation from an axis (eachPoint is different
		// allign need an axis of rotation (defined here by the two old resulting
		// points (previuos)
		// axis = previousA,previousB
		Transform rotmp = rotations[(int) ((float)Math.random()*rotations.length)];

		Transform axisRotateA = new Transform(previousA,previousB,rotmp);
		Transform axisRotateB = new Transform(previousA,previousB,rotmp);


		// Create a Translation alligned with the previous
		// Remember that we don't know how is oriented the last face
		Transform orientedTranslationA = new Transform(previousAA,previousA,sides[lengthA]);
		Transform orientedTranslationB = new Transform(previousBB,previousB,sides[lengthB]);


		// Apply to rotation to the translation
		Transform comboA = new Transform();
		comboA.add(orientedTranslationA);
		comboA.add(axisRotateA); // From the beginning


		Transform comboB = new Transform();
		comboB.add(orientedTranslationB);
		comboB.add(axisRotateB); // From the beginning


		// Here's where evrything is set together pt with transform
		newPtA.apply(comboA);
		newPtB.apply(comboB);

		// Put all that in that containers
		ptsA.add(newPtA);
		ptsB.add(newPtB);

		// Swap Previuos
		previousAA = previousA;
		previousBB = previousB;

		previousA = newPtA;
		previousB = newPtB;

		// myObject = null;
		myObject = new SweepTwoPaths(ptsA,ptsB);

		// http://www.javaworld.com/javaworld/javaqa/1999-08/04-qa-leaks.html
		// http://www.ibm.com/developerworks/java/library/j-leaks/
		// Java HeapSpace May Come from this...
		// Runtime r = Runtime.getRuntime();
		// long freeMem = r.freeMemory();
		// Anar.println("FreeHeapMem: " + freeMem);
		// Anar.println("-------------------");
	}


	// /////////////////////////////////
	// /////////////////////////////////
	// /////////////////////////////////
	// So Rad
	boolean   isSimRunning = true;

	RadEngine simThread;


	@Override
	public void radSimDone() {
		isSimRunning = false;
		// SwitchRender
	}

	// /////////////////////////////////
	// /////////////////////////////////
	// /////////////////////////////////


	/**
	 * container class for each element of the deployment
	 */
	class Element {

		int   A, B;
		float energy;

		Element(int _A, int _B, float _e){
			A = _A;
			B = _B;
			energy = _e;
		}
	}


	public static void main(String[] args){
		PApplet.main(new String[]{Test01aDeployRadF.class.getName()});
	}


}

