/**
 * @author davidharvey
 */


function Curve(fixedR, movingR, distance) { this.init(fixedR, movingR, distance) }
Curve.implement({
	init: function(fixedR, movingR, distance) {
		this.fixedR = fixedR;
		this.movingR = movingR;
		this.distance = distance;
	},
	validate: function() {
		return !isNaN(this.fixedR) && !isNaN(this.movingR) && !isNaN(this.distance)
	},
	setD: function(d) {
		this.distance = d;
	},
	getD: function() {
		return this.distance;
	},
	draw: function(ctx) {
		var diff = this.fixedR - this.movingR;
		var diffByMoving = diff/this.movingR;
		
		var l = lcm(this.fixedR,this.movingR);
		var limit = 2 * (l/this.fixedR) * Math.PI;
		
		var started = false;

		ctx.beginPath();

		for (var theta = 0; theta < limit; theta = theta + 0.01) {
			var x = diff * Math.cos(theta) + this.distance * Math.cos(diffByMoving*theta);
			var y = diff * Math.sin(theta) - this.distance * Math.sin(diffByMoving*theta);
			if (started) {
				ctx.lineTo(x,y);
			} else {
				ctx.moveTo(x,y);
			}
			started = true;
		}
		ctx.stroke();		
	}
})

function DrawingSequence(curve) {
	this.init(curve);
}
DrawingSequence.implement({
	init: function(curve) {
		this.curve 		= curve;
		this.deltaX 	= 0;
		this.deltaY 	= 0;
		this.deltaD 	= 0;
		this.deltaTheta = 0;
		this.applyDxDyToRotated = false;
		this.repeat 	= 1;
		
		this.unwindX = this.unwindY = this.unwindD = this.unwindT = false;
	},
	
	draw: function(ctx,pens) {
		if (pens.length == 0 ) {
			pens.push("black");
		}
		
		ctx.save();
		var count = 0;
		var rotation = 0;
		while (count < this.repeat) {
			ctx.strokeStyle = pens[count % pens.length]
			this.curve.draw(ctx);
			count++;
			if (count < this.repeat) {
				this.curve.setD(this.curve.getD() + this.deltaD);				
				if (this.applyDxDyToRotated) {
					ctx.translate(this.deltaX, this.deltaY);
					ctx.rotate((this.deltaTheta * Math.PI) / 180);
				} else {
					ctx.restore();
					ctx.translate(this.deltaX, this.deltaY);
					ctx.save();
					rotation += this.deltaTheta;
					ctx.rotate((rotation * Math.PI) / 180);
				}
			}
		}
		ctx.restore();
		
		if (this.unwinding()) {
			var dx = this.unwindX ?     - this.deltaX : this.deltaX;
			var dy = this.unwindY ?     - this.deltaY : this.deltaY;
			var dd = this.unwindD ?     - this.deltaD : this.deltaD;
			var dt = this.unwindTheta ? - this.deltaTheta : this.deltaTheta;
			ctx.save();
			count--;
			while (count > 0) {
				this.curve.setD(this.curve.getD() + dd);
				if (this.applyDxDyToRotated) {
					ctx.translate(dx, dy);
					ctx.rotate((dt * Math.PI)/180);
				} else {
					ctx.restore();
					ctx.translate(dx, dy);
					ctx.save();
					rotation += dt;
					ctx.rotate((rotation * Math.PI)/180);
				}
				ctx.strokeStyle = pens[count % pens.length]
				this.curve.draw(ctx);
				count--;
			}
			ctx.restore();
		}
	},
	
	setRepeat: function(value) {
		this.repeat = value || 1;
		return this;
	},
	setDeltaX: function(value) {
		this.deltaX = value || 0;
		return this;
	},
	setDeltaY: function(value) {
		this.deltaY = value || 0;
		return this;
	},
	setDeltaTheta: function(value) {
		this.deltaTheta = value || 0;
		return this;
	},
	setDeltaD: function(value,def) {
		this.deltaD= value || 0;
		return this;
	}, 
	setUnwindX: function(value) {
		this.unwindX = value;
		return this;
	},
	setUnwindY: function(value) {
		this.unwindY = value;
		return this;
	},
	setUnwindD: function(value) {
		this.unwindD = value;
		return this;
	},
	setUnwindTheta: function(value) {
		this.unwindTheta = value;
		return this;
	},
	setApplyDxDyToRotated: function(value) {
		this.applyDxDyToRotated = value;
		return this;		
	},
	unwinding: function() {
		return this.repeat > 1 && (this.unwindX || this.unwindY || this.unwindD || this.unwindTheta);
	}
})
 
function draw(){
	var fixedR = parseInt($('#fixed').val());
	var movingR = parseInt($('#inner').val());
	var d = parseInt($('#distance').val());

	var curve = new Curve(fixedR, movingR, d);
	
	var unwind = $('#unwind').attr('checked');
	
	var ds = new DrawingSequence(curve);
	ds.setRepeat(parseInt($('#repeat').val()))
	  .setDeltaX(parseInt($('#deltaX').val()))
	  .setDeltaY(parseInt($('#deltaY').val()))
	  .setDeltaD(parseInt($('#deltaD').val()))
	  .setDeltaTheta(parseInt($('#deltaTheta').val()) || 0);

	ds.setUnwindX($('#unwindX').attr('checked'))
	  .setUnwindY($('#unwindY').attr('checked'))
	  .setUnwindD($('#unwindD').attr('checked'))
	  .setUnwindTheta($('#unwindTheta').attr('checked'))
	  .setApplyDxDyToRotated($('#applyDxDyToRotated').attr('checked'))

	if (curve.validate()) {
		drawCurve(ds);
	} else {
		alert("Enter integer values for fixed and moving radii and distance!")
	}
}


function clearCanvas() {
	var canvas = document.getElementById('spiro');
	if (canvas.getContext) {
		var ctx = canvas.getContext('2d');
		ctx.clearRect(0,0,canvas.width, canvas.height);
	}
}

function drawCurve (ds) {
	var canvas = document.getElementById('spiro');
	if (canvas.getContext){
		var ctx = canvas.getContext('2d');
		ctx.save();
		
		var colors = $("#selectedColors li");
		var pens = new Array();
		colors.each(function(i) {
			pens.push(this.getAttribute("id"));			
		});
		
		ctx.globalAlpha = (100-$('#transparency').slider("value"))/100;
		
		ctx.translate(canvas.width/2, canvas.height/2);
		ds.draw(ctx,pens);
		ctx.restore();
	} else {
		alert('HTML <canvas> not supported in this browser!')
	}
}




