var ITER_MAX = 100;				// recursion limit
var NUM_STROKES = 10;
var TAN_OFFSET = 25;				// px, starting point of subbranch offset from parent cp
var BRANCH_MAX_RADIUS = 100;		// px, radius of maximum branch "length"
var BRANCH_MIN_RADIUS = 10;
var BRANCH_ANGLE_VAR = 45;		// "tightness"
var SLOPE_MAX = 5;
var EDGE_BUFFER = 50;
var branches = 0;				// global branch counter
var ctx;						// working canvas object

var WIDTH = 900, HEIGHT = 700;
var seedX = WIDTH / 2;
var seedY = HEIGHT;

function Point(x, y) {
	// pseudo-class
  this.x = x;
  this.y = y;
}

function Vector(x, y, angle, z){
	this.point = new Point(x, y);
	this.angle = angle;
	this.z = z;
}

function degToRad(deg){
	return deg * Math.PI / 180;
}

function radToDeg(rad){
	return rad * 180 / Math.PI
}

function randomRange(min, max){
	if(min > max) return 0;
	var delta = max - min;
	return (Math.random() * delta) + min;
}

function tanOffsetOld(x1, y1, m, h){
	// returns a new point offset from the original point at the same slope
	// h = offset
	
	var nx, ny;
	//nx = x1 + (h * radToDeg(Math.tan(degToRad(m))));
	nx = x1 + (h * Math.tan(m));
	ny = m * nx;
	
	return new Point(nx, ny);
	
}

function tanOffset(x1, y1, theta, h){
	//console.log("tanOffsetNew: "+theta);
	// returns a new point offset from the original point at a certain angle
	// h = offset
	
	theta = theta % 360;
	//console.log("tanOffsetNew: theta: "+theta);
	
	var nx, ny, dy, dx;
	dy = h * Math.sin(degToRad(theta));
	dx = pythag(h, dy);
	if((theta > 90 && theta <= 180) || (theta > 270 && theta <= 360)) dx = -dx;
	ny = dy + y1;
	nx = dx + x1;
	//console.log("theta: "+theta+" / dy: "+dy+" / dx: "+dx);
	
	return new Point(nx, ny);
	
}

function pythag(h, y){
	
	return Math.sqrt((h*h)-(y*y));
	
}

/*
function ffLog(aMessage) {
  var consoleService = Components.classes["@mozilla.org/consoleservice;1"]
                                 .getService(Components.interfaces.nsIConsoleService);
  consoleService.logStringMessage("My component: " + aMessage);
}
*/

function drawBranch(startX, startY, theta, num_children, max){
	// each call of this function draws 1 curve and spawns between MAX_CHILDREN and MIN_CHILDREN new calls of itself
	//console.log("drawBranch #"+branches+" / startX: "+startX+" / startY: "+startY);
	//ffLog("drawBranch");
	if(branches >= max) return;	// RECURSION LIMIT
	branches++;
	var ncp1x, ncp1y, ncp2x, ncp2y, nx, ny;
	
	// CHECK STARTING BOUNDARIES
	/*
	if(startX > WIDTH) startX = startX - WIDTH;
	else if(startX < 0) startX = WIDTH + startX;
	if(startY > HEIGHT) startY = startY - HEIGHT;
	else if(startY < 0) startY = HEIGHT + startY;
	*/
	
	var angleMin = theta - (BRANCH_ANGLE_VAR / 2);
	var angleMax = theta + (BRANCH_ANGLE_VAR / 2);
	
	// CTRL POINT 1
	var cp1offset = tanOffset(startX, startY, theta, TAN_OFFSET);		// returns a Point object
	ncp1x = cp1offset.x;
	ncp1y = cp1offset.y;
	
	// END POINT
	var ranDist = randomRange(BRANCH_MIN_RADIUS, BRANCH_MAX_RADIUS);
	var ranBranchTheta = randomRange(angleMin, angleMax);
	
	if(startX > WIDTH + EDGE_BUFFER) ranBranchTheta = -ranBranchTheta;
	else if(startX < EDGE_BUFFER) ranBranchTheta = -ranBranchTheta;
	else if(startY > HEIGHT + EDGE_BUFFER) ranBranchTheta = -ranBranchTheta;
	else if(startY < EDGE_BUFFER) ranBranchTheta = -ranBranchTheta;
	
	newPoint = tanOffset(startX, startY, ranBranchTheta, ranDist);
	nx = newPoint.x;
	ny = newPoint.y;
	
	// CTRL POINT 2
	var ranCtlDist = randomRange(BRANCH_MIN_RADIUS, BRANCH_MAX_RADIUS);
	var ranCtlTheta = randomRange(angleMin, angleMax);
	
	newCtlPoint = tanOffset(nx, ny, ranCtlTheta, ranCtlDist);
	ncp2x = newCtlPoint.x;
	ncp2y = newCtlPoint.y;
	
	// BOUNDARIES
	/*
	if(nx > WIDTH) nx = nx-WIDTH;
	if(ny > HEIGHT) ny = ny-HEIGHT;
	if(nx < 0) nx = WIDTH+nx;
	if(ny < 0) ny = HEIGHT+ny;
	*/
	
	// DRAW PATH
	ctx.beginPath();
	ctx.moveTo(startX, startY);
	// bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
	ctx.bezierCurveTo(ncp1x, ncp1y, ncp2x, ncp2y, nx, ny);
	ctx.stroke();
	
	var ranNumChildren;
	
	for(var i=0; i<num_children; i++){
		ranNumChildren = randomRange(1, 3);
		drawBranch(ncp1x, ncp1y, ranCtlTheta, ranNumChildren, ITER_MAX);
	}
	
}

function initCanvas(){
	//console.log("initCanvas");
	// get the canvas element using the DOM
  var canvas = document.getElementById('tutorial');
	canvas.width = WIDTH;
	canvas.height = HEIGHT;
  // Make sure we don't execute when canvas isn't supported
  if (canvas.getContext){

  	// use getContext to use the canvas for drawing
  	ctx = canvas.getContext('2d');

		return;

  } else {
    alert('You need Safari or Firefox 1.5+ to see this demo.');
  }
	
}

function test(){
	initCanvas();
	// draw some tests
	var sx, sy, newPoint;
	sx = WIDTH / 2;
	sy = HEIGHT / 2;

	/*
	for(var j=0; j<360; j++){
		console.log("j: "+j+" / "+Math.sin(degToRad(j)));
	}
	*/

	for(var i=0; i<360; i+=1){
		ctx.moveTo(sx, sy);
		newPoint = tanOffset(sx, sy, i, 100);
		ctx.lineTo(newPoint.x, newPoint.y);
		ctx.stroke();
	}
	
	//console.log("testing... "+newPoint.x+" / "+newPoint.y);
	//ctx.stroke();
	
}

function drawTree(){

	initCanvas();
	var cx = 0;//WIDTH / 5;
	var cy = 0;//HEIGHT / 5;
	
	//var ranSlope = (Math.random() * 2) - 1;		// should yield btw -1 and 1
	for(var i=0; i<NUM_STROKES; i++){
		branches = 0;
		var ranNumChildren = randomRange(1, 3);
		drawBranch(randomRange(cx-25, cx+25), randomRange(cy-25, cy+25), randomRange(0, 90), ranNumChildren, ITER_MAX);		// initial call
	}

}

function drawBezierScribble(p0x, p0y, cp0x, cp0y, cp1x, cp1y, p1x, p1y)
{
  // B(t) = ((1-t)^3 * P0) + (3t(1-t)^2 * P1) + (3(t^2)(1-t) * P3) + (t^3 * P4)
	var NUM_SEGMENTS = randomRange(25, 50);
  var delta = 1 / NUM_SEGMENTS;		//0.01;
  
  // the starting point
  var lx = p0x, ly = p0y;
  var cx, cy;

	var last = {lx:lx, ly:ly, langle:0, lcp:new Point(lx+10, ly+10)};
	var scribbleArray = [];
	
	// put all plot points in an array

  for(var t = 0; t <= 1.0; t += delta)		// delta*2; t += delta)//
  {
    // calc the next point
    cx = (Math.pow(1-t,3)*p0x) + (3*t*Math.pow(1-t,2)*cp0x) + (3*Math.pow(t,2)*(1-t)*cp1x) + (Math.pow(t,3)*p1x); 
    cy = (Math.pow(1-t,3)*p0y) + (3*t*Math.pow(1-t,2)*cp0y) + (3*Math.pow(t,2)*(1-t)*cp1y) + (Math.pow(t,3)*p1y);
    
    // draw a segment
    //line(lx, ly, cx, cy);
		//last = drawScribbleSegment(lx, ly, last.langle, last.lcp);
		scribbleArray.push(new Point(cx, cy));
    
    // store the last points
    lx = cx;
    ly = cy;
  }
	
	// draw scribbles between each pair of plot points
	last_angle = 0;
	for(var i=0; i < scribbleArray.length - 1; i++){
		console.log("i: "+i+" / "+scribbleArray[i].x+", "+scribbleArray[i].y+" / "+scribbleArray[i+1].x+", "+scribbleArray[i+1].y);
		last_angle = drawScribbleSegment2(scribbleArray[i], scribbleArray[i+1], last_angle);
	}
}

function drawScribbleSegment2(p0, p1, langle){
	console.log("langle: "+langle);
	rdist = randomRange(DIST_MIN, DIST_MAX);
	cp0 = tanOffset(p0.x, p0.y, langle, rdist);
	
	// find 2nd ctrl point based off of reverse of main angle
	rev_angle = (langle + 180) % 360;
	//rev_angle -= 180;
	// the first control point of the next segment must have the same slope as this angle
	nangle = randomRange(rev_angle - (ANGLE_VAR / 2), rev_angle + (ANGLE_VAR / 2));
	console.log("nangle: "+nangle);
	cp1 = tanOffset(p1.x, p1.y, nangle, randomRange(CTRL_DIST_MIN, CTRL_DIST_MAX));
	
	ctx.beginPath();
	ctx.moveTo(p0.x, p0.y);
	// bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
	ctx.bezierCurveTo(cp0.x, cp0.y, cp1.x, cp1.y, p1.x, p1.y);
	ctx.stroke();
	
	nangle = (nangle + 180) % 360;
	
	return nangle;
}

function drawScribbleSegment(lx, ly, langle, lcp){
	
	//console.log("i: "+i);
	rangle = randomRange(langle - (ANGLE_VAR / 2), langle + (ANGLE_VAR / 2));		// main angle
	
	// BOUNDARIES: Reverse angle if it's near edge and pointing offscreen
	if(lx > WIDTH - EDGE_BUFFER && (rangle > 270 || rangle < 90))
		//rangle = (rangle + 30) % 360;
		rangle = 180;
	if(lx < EDGE_BUFFER && (rangle > 90 || rangle < 270))
		//rangle = (rangle + 30) % 360;
		rangle = 0;
	if(ly > HEIGHT - EDGE_BUFFER && (rangle > 0 || rangle < 180))
		//rangle = (rangle + 30) % 360;
		rangle = 270;
	if(ly < EDGE_BUFFER && (rangle > 180 || rangle < 360))
		//rangle = (rangle + 30) % 360;
		rangle = 90;
	
	rdist = randomRange(DIST_MIN, DIST_MAX);
	np = tanOffset(lx, ly, rangle, rdist);		// find a new point based on the previous point
	
	// find 2nd ctrl point based off of reverse of main angle
	rev_angle = (rangle + 180) % 360;
	//rev_angle -= 180;
	// the first control point of the next segment must have the same slope as this angle
	nangle = randomRange(rev_angle - (ANGLE_VAR / 2), rev_angle + (ANGLE_VAR / 2));
	cp2 = tanOffset(np.x, np.y, nangle, randomRange(CTRL_DIST_MIN, CTRL_DIST_MAX));
	
	// DRAW PATH
	ctx.beginPath();
	ctx.moveTo(lx, ly);
	// bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
	ctx.bezierCurveTo(lcp.x, lcp.y, cp2.x, cp2.y, np.x, np.y);
	ctx.stroke();
	
	lx = np.x;
	ly = np.y;
	next_angle = (nangle + 180) % 360;
	lcp = tanOffset(np.x, np.y, next_angle, randomRange(CTRL_DIST_MIN, CTRL_DIST_MAX));
	return {lx:lx, ly:ly, langle:langle, lcp:lcp};
}

function drawLine(){
	
	var num_segments = randomRange(100, 200);
	var sx = randomRange(0, WIDTH);
	var sy = randomRange(0, HEIGHT);
	
	var lx = sx;
	var ly = sy;
	
	var rangle, langle, np, rev_angle;
	langle = randomRange(0, 360);	// first angle
	var lcp = tanOffset(sx, sy, langle, randomRange(CTRL_DIST_MIN, CTRL_DIST_MAX));
	//lcpx = icp.x;
	//lcpy = icp.y;
	
	for(var i=0; i<num_segments; i++){
		console.log("i: "+i);
		rangle = randomRange(langle - (ANGLE_VAR / 2), langle + (ANGLE_VAR / 2));		// main angle
		
		// BOUNDARIES: Reverse angle if it's near edge and pointing offscreen
		if(lx > WIDTH - EDGE_BUFFER && (rangle > 270 || rangle < 90))
			//rangle = (rangle + 30) % 360;
			rangle = 180;
		if(lx < EDGE_BUFFER && (rangle > 90 || rangle < 270))
			//rangle = (rangle + 30) % 360;
			rangle = 0;
		if(ly > HEIGHT - EDGE_BUFFER && (rangle > 0 || rangle < 180))
			//rangle = (rangle + 30) % 360;
			rangle = 270;
		if(ly < EDGE_BUFFER && (rangle > 180 || rangle < 360))
			//rangle = (rangle + 30) % 360;
			rangle = 90;
		
		rdist = randomRange(DIST_MIN, DIST_MAX);
		np = tanOffset(lx, ly, rangle, rdist);		// find a new point based on the previous point
		
		// find 2nd ctrl point based off of reverse of main angle
		rev_angle = (rangle + 180) % 360;
		//rev_angle -= 180;
		// the first control point of the next segment must have the same slope as this angle
		nangle = randomRange(rev_angle - (ANGLE_VAR / 2), rev_angle + (ANGLE_VAR / 2));
		cp2 = tanOffset(np.x, np.y, nangle, randomRange(CTRL_DIST_MIN, CTRL_DIST_MAX));
		
		// DRAW PATH
		ctx.beginPath();
		ctx.moveTo(lx, ly);
		// bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
		ctx.bezierCurveTo(lcp.x, lcp.y, cp2.x, cp2.y, np.x, np.y);
		ctx.stroke();
		
		lx = np.x;
		ly = np.y;
		next_angle = (nangle + 180) % 360;
		lcp = tanOffset(np.x, np.y, next_angle, randomRange(CTRL_DIST_MIN, CTRL_DIST_MAX));
	}
	
}

var ANGLE_VAR = 80;				// variation of angle between anchors
var DIST_MIN = 20;				// distance between anchors of segments
var DIST_MAX = 50;
var CTRL_DIST_MIN = 10;		// distance of control point from anchor
var CTRL_DIST_MAX = 20;

function scribble(){
	
	initCanvas();
	
	for(var i=0; i<randomRange(5, 10); i++){
		drawLine();
	}
}

//drawBezierScribble(float p0x, float p0y, float cp0x, float cp0y, float cp1x, float cp1y, float p1x, float p1y)

function scribble2(){
	
	initCanvas();
	
	//for(var i=0; i<randomRange(5, 10); i++){
	for(var i=0; i<1; i++){
		
		p0 = new Point(25, 25);
		p1 = new Point(WIDTH-25, HEIGHT-25);
		cp0 = tanOffset(p0.x, p0.y, 0, 500);
		cp1 = tanOffset(p1.x, p1.y, 180, 500);
		
		drawBezierScribble(p0.x, p0.y, cp0.x, cp0.y, cp1.x, cp1.y, p1.x, p1.y);
	}
}