
var $ = require("jquery");
var CausalDiagram = require("../../app/causalDiagram.js");
var SVG = require("svg.js");

describe("causalDiagram",	function() {
	var div;
	var basicPattern;
	var draw;
	var causalDiagram;
	var arrowMarker;
	
	beforeEach(function() {
		causalDiagram = new CausalDiagram(null);
		div = document.createElement("div");
		draw = SVG(div);
		var defs = draw.defs();
		arrowMarker = causalDiagram._initArrow(defs);
		basicPattern = {
				"name" : "Basic",
				"jugglerCount" : 1,
				"beats" : [
					{
						"throws": [
							{
								"sourceJuggler":0,
								"sourceHand":"R",
								"targetJuggler":0,
								"throwHeight": 3
							}
							]
					}
					]
		};
	});

	describe("initialState", function() {
		it("should have correct initial constants", function() {
			expect(causalDiagram._CIRCLE_RADIUS).toEqual(40);
			expect(causalDiagram._X_MARGIN).toEqual(50);
			expect(causalDiagram._BEAT_WIDTH).toEqual(100);
			expect(causalDiagram._Y_MARGIN).toEqual(75);
			expect(causalDiagram._BEAT_HEIGHT).toEqual(125);
			expect(causalDiagram._CIRCLE_CORNER_ANGLE).toEqual(Math.PI / 4);
		});
	});

	describe("getBeatPosition", function() {
		it("should calculate beat position", function() {
			for (var beat = 0; beat < 20; beat++) {
				for (var juggler = 0; juggler < 20; juggler++) {
					var pos = causalDiagram.getBeatPosition(beat, juggler);
					expect(pos.x).toEqual(beat * 100 + 50);
					expect(pos.y).toEqual(juggler * 125 + 75);
				}
			}
		});
	});

	describe("circle corners", function() {

		var circle_corner_adj;
		var totalBeats;
		var totalJugglers;

		beforeEach(function() {
			totalBeats = 20;
			totalJugglers = 20;
			circle_corner_adj = (Math.round((Math.cos(causalDiagram._CIRCLE_CORNER_ANGLE)*causalDiagram._CIRCLE_RADIUS)*100) / 100);
		});

		it("should  calculate upper left", function() {
			for (var beat = 0; beat < totalBeats; beat++) {
				for (var juggler = 0; juggler < totalJugglers; juggler++) {
					var pos = causalDiagram.getBeatPosition(beat, juggler);
					var posX = causalDiagram.getUpperLeft(pos);
					
					expect(posX.x).toEqual(beat * 100 + 50 - circle_corner_adj);
					expect(posX.y).toEqual(juggler * 125 + 75 - circle_corner_adj);
				}
			}
		});

		it("should  calculate upper right", function() {
			for (var beat = 0; beat < totalBeats; beat++) {
				for (var juggler = 0; juggler < totalJugglers; juggler++) {
					var pos = causalDiagram.getBeatPosition(beat, juggler);
					var posX = causalDiagram.getUpperRight(pos);
					expect(posX.x).toEqual(beat * 100 + 50 + circle_corner_adj);
					expect(posX.y).toEqual(juggler * 125 + 75 - circle_corner_adj);
				}
			}
		});

		it("should  calculate lower left", function() {
			for (var beat = 0; beat < totalBeats; beat++) {
				for (var juggler = 0; juggler < totalJugglers; juggler++) {
					var pos = causalDiagram.getBeatPosition(beat, juggler);
					var posX = causalDiagram.getLowerLeft(pos);
					expect(posX.x).toEqual(beat * 100 + 50 - circle_corner_adj);
					expect(posX.y).toEqual(juggler * 125 + 75 + circle_corner_adj);
				}
			}
		});

		it("should  calculate lower right", function() {
			for (var beat = 0; beat < totalBeats; beat++) {
				for (var juggler = 0; juggler < totalJugglers; juggler++) {
					var pos = causalDiagram.getBeatPosition(beat, juggler);
					var posX = causalDiagram.getLowerRight(pos);
					expect(posX.x).toEqual(beat * 100 + 50 + circle_corner_adj);
					expect(posX.y).toEqual(juggler * 125 + 75 + circle_corner_adj);
				}
			}
		});

		it("should  calculate right", function() {
			for (var beat = 0; beat < totalBeats; beat++) {
				for (var juggler = 0; juggler < totalJugglers; juggler++) {
					var pos = causalDiagram.getBeatPosition(beat, juggler);
					var posX = causalDiagram.getRight(pos);
					expect(posX.x).toEqual(beat * 100 + 50 + causalDiagram._CIRCLE_RADIUS);
					expect(posX.y).toEqual(juggler * 125 + 75);
				}
			}
		});

		it("should  calculate left", function() {
			for (var beat = 0; beat < totalBeats; beat++) {
				for (var juggler = 0; juggler < totalJugglers; juggler++) {
					var pos = causalDiagram.getBeatPosition(beat, juggler);
					var posX = causalDiagram.getLeft(pos);
					expect(posX.x).toEqual(beat * 100 + 50 - causalDiagram._CIRCLE_RADIUS);
					expect(posX.y).toEqual(juggler * 125 + 75);
				}
			}
		});

		it("should  calculate top ", function() {
			for (var beat = 0; beat < totalBeats; beat++) {
				for (var juggler = 0; juggler < totalJugglers; juggler++) {
					var pos = causalDiagram.getBeatPosition(beat, juggler);
					var posX = causalDiagram.getTop(pos);
					expect(posX.x).toEqual(beat * 100 + 50);
					expect(posX.y).toEqual(juggler * 125 + 75 - causalDiagram._CIRCLE_RADIUS);
				}
			}
		});

		it("should  calculate bottom ", function() {
			for (var beat = 0; beat < totalBeats; beat++) {
				for (var juggler = 0; juggler < totalJugglers; juggler++) {
					var pos = causalDiagram.getBeatPosition(beat, juggler);
					var posX = causalDiagram.getBottom(pos);
					expect(posX.x).toEqual(beat * 100 + 50);
					expect(posX.y).toEqual(juggler * 125 + 75 + causalDiagram._CIRCLE_RADIUS);
				}
			}
		});
	});
	
	describe("beatDiff", function() {
		var throwObj;

		beforeEach(function() {
			throwObj = {
					"sourceJuggler":0,
					"sourceHand":"R",
					"targetJuggler":0,
					"throwHeight": 3
			};
		});

		it("should calculate beat diff", function() {
			for (var i = 0; i < 20; i++) {
				throwObj.throwHeight = i;
				var diff = causalDiagram.getBeatDiff(throwObj);
				expect(diff).toEqual(i - 2);
			}
		});
	});
	

	describe("drawCircles", function() {
		var throws;
		var totalBeats;
		var totalJugglers;
		beforeEach(function() {
			totalBeats = 3;
			totalJugglers = 3;
			throws = [];
			for (var i = 0; i < totalJugglers; i++) {
				var throwObj = {
						"sourceJuggler":i,
						"sourceHand": (i % 2 == 0) ? "R" : "L",
						"targetJuggler":i,
						"throwHeight": 3
				};
				throws.push(throwObj);
			}
			causalDiagram.passCircles = {};
		});
		
		it("shouldDrawCirlesCorrectly", function() {
			for (var beat = 0; beat < totalBeats; beat++) {
				causalDiagram.drawCircles(draw, beat, throws);
				for (var juggler = 0; juggler < totalJugglers; juggler++) {
					var circleId = "causal_beat_"+beat+"_juggler_"+juggler;
					var pos = causalDiagram.getBeatPosition(beat, juggler);
					var circle = $(div).find("#"+circleId);
					expect(circle.attr("cx")).toEqual(""+pos.x);
					expect(circle.attr("cy")).toEqual(""+pos.y);
					expect(circle.attr("r")).toEqual(""+causalDiagram._CIRCLE_RADIUS);
					expect(circle.hasClass("causal_beat_circle")).toBe(true);
					expect(causalDiagram.passCircles[circleId]).not.toBe(undefined);
				}
			}
			
		});
		
		it("shouldDrawTextCorrectly", function() {
			for (var beat = 0; beat < totalBeats; beat++) {
				causalDiagram.drawCircles(draw, beat, throws);
				for (var juggler = 0; juggler < totalJugglers; juggler++) {
					var textId = "causal_beat_label_"+beat+"_juggler_"+juggler;
					var handText = (juggler % 2 == 0) ? "R" : "L";
					var pos = causalDiagram.getBeatPosition(beat, juggler);
					var text = $(div).find("#"+textId);
					expect(text.attr("x")).toEqual(""+(pos.x-10));
					//weird svg.js text movement thing...
					expect(eval(text.attr("y"))).toBeGreaterThan(""+(pos.y-46));
					expect(eval(text.attr("y"))).toBeLessThan(""+(pos.y-36));
					expect(text.attr("font-size")).toEqual("35");
					expect(text.attr("font-family")).toEqual("sans-serif");
					var tSpan = $(text).find("tspan")[0];
					expect(tSpan.childNodes[0].nodeValue).toEqual(handText);
				}
			}
		});
	});
	
	describe("draw throws", function() {
		var throws;
		var totalBeats;
		var totalJugglers;
		var throwObj;

		beforeEach(function() {
			totalBeats = 3;
			totalJugglers = 3;
			throwObj = {
					"sourceJuggler":0,
					"sourceHand":"R",
					"targetJuggler":0,
					"throwHeight": 3
			};
		});
		
		describe("isLineInDiagram", function() {

			it("should calculate if a throw is a line or curve for passes", function() {
				throwObj.targetJuggler = 1;
				for (var height = 0; height < 20; height++) {
					throwObj.throwHeight = height;
					expect(causalDiagram.isLineInDiagram(throwObj)).toBe(true);
				}
			});
			it("should calculate if a throw is a line or curve for 0", function() {
				throwObj.targetJuggler = 0;
				throwObj.throwHeight = 0;
				expect(causalDiagram.isLineInDiagram(throwObj)).toBe(false);
			});

			it("should calculate if a throw is a line or curve for 1", function() {
				throwObj.targetJuggler = 0;
				throwObj.throwHeight = 1;
				expect(causalDiagram.isLineInDiagram(throwObj)).toBe(false);
			});

			it("should calculate if a throw is a line or curve for 2", function() {
				throwObj.targetJuggler = 0;
				throwObj.throwHeight = 2;
				expect(causalDiagram.isLineInDiagram(throwObj)).toBe(false);
			});

			it("should calculate if a throw is a line or curve for 3", function() {
				throwObj.targetJuggler = 0;
				throwObj.throwHeight = 3;
				expect(causalDiagram.isLineInDiagram(throwObj)).toBe(true);
			});

			it("should calculate if a throw is a line or curve for large selfs", function() {
				throwObj.targetJuggler = 0;
				for (var height = 4; height < 20; height++) {
					throwObj.throwHeight = height;
					expect(causalDiagram.isLineInDiagram(throwObj)).toBe(false);
				}
			});
		});
		
		describe("drawThrowLine", function() {
			beforeEach(function() {
				causalDiagram.throwLines = {};
			});
			it("should draw 3 self", function() {
				var id = "foobar";
				var pos1 = causalDiagram.getBeatPosition(0, 0);
				var pos2 = causalDiagram.getBeatPosition(1, 0);
				causalDiagram.drawThrowLine(draw, id, throwObj, 0, arrowMarker, null, null);
				var line = $(div).find("#"+id);
				var start = causalDiagram.getRight(pos1);
				var end = causalDiagram.getLeft(pos2);
				expect(line.attr("x1")).toEqual(""+start.x);
				expect(line.attr("y1")).toEqual(""+start.y);
				expect(line.attr("x2")).toEqual(""+end.x);
				expect(line.attr("y2")).toEqual(""+end.y);
				expect(line.hasClass("causal_pass_line")).toBe(true);
				expect(line.attr("marker-end")).toEqual("url(#arrowMarker)");
				expect(causalDiagram.throwLines[id]).not.toBe(undefined);
			});
			it("should draw pass down", function() {
				var id = "foobar";
				throwObj.targetJuggler = 1;
				var pos1 = causalDiagram.getBeatPosition(0, 0);
				var pos2 = causalDiagram.getBeatPosition(1, 1);
				causalDiagram.drawThrowLine(draw, id, throwObj, 0, arrowMarker, null, null);
				var line = $(div).find("#"+id);
				var start = causalDiagram.getLowerRight(pos1);
				var end = causalDiagram.getUpperLeft(pos2);
				expect(line.attr("x1")).toEqual(""+start.x);
				expect(line.attr("y1")).toEqual(""+start.y);
				expect(line.attr("x2")).toEqual(""+end.x);
				expect(line.attr("y2")).toEqual(""+end.y);
				expect(line.hasClass("causal_pass_line")).toBe(true);
				expect(line.attr("marker-end")).toEqual("url(#arrowMarker)");
			});
			it("should draw pass up", function() {
				var id = "foobar";
				throwObj.sourceJuggler = 1;
				var pos1 = causalDiagram.getBeatPosition(0, 1);
				var pos2 = causalDiagram.getBeatPosition(1, 0);
				causalDiagram.drawThrowLine(draw, id, throwObj, 0, arrowMarker, null, null);
				var line = $(div).find("#"+id);
				var start = causalDiagram.getUpperRight(pos1);
				var end = causalDiagram.getLowerLeft(pos2);
				expect(line.attr("x1")).toEqual(""+start.x);
				expect(line.attr("y1")).toEqual(""+start.y);
				expect(line.attr("x2")).toEqual(""+end.x);
				expect(line.attr("y2")).toEqual(""+end.y);
				expect(line.hasClass("causal_pass_line")).toBe(true);
				expect(line.attr("marker-end")).toEqual("url(#arrowMarker)");
			});
			describe("wrapping", function() {
				var borderWest;
				var borderEast;
				beforeEach(function() {
					//3 beats wide
					borderWest = draw.line(0, 0, 0, 315);
					borderEast = draw.line(300, 0, 300, 315);
					causalDiagram.pattern =
						{
							"beats":["A", "B", "C"]
						};
				});
				describe("wrap eastern border", function() {
					it("should split up straight throw", function() {
						var id = "foobar";
						throwObj.sourceJuggler = 0;
						var pos1 = causalDiagram.getBeatPosition(2, 0);

						causalDiagram.drawThrowLine(draw, id, throwObj, 2, arrowMarker, borderEast, borderWest);
						var line = $(div).find("#"+id);
						var start = causalDiagram.getRight(pos1);
						var end = {
								"x": 300,
								"y": pos1.y
						};
						//var end = causalDiagram.getLowerLeft(pos2);
						expect(line.attr("x1")).toEqual(""+start.x);
						expect(line.attr("y1")).toEqual(""+start.y);
						expect(line.attr("x2")).toEqual(""+end.x);
						expect(line.attr("y2")).toEqual(""+end.y);
						expect(line.hasClass("causal_pass_line")).toBe(true);
						expect(line.attr("marker-end")).not.toEqual("url(#arrowMarker)");
						
						var line2 = $(div).find("#"+id+"_A");
						var pos2 = causalDiagram.getBeatPosition(0, 0);
						end = causalDiagram.getLeft(pos2);
						start = {
								"x": 0,
								"y": pos1.y
						};
						expect(line2.attr("x1")).toEqual(""+start.x);
						expect(line2.attr("y1")).toEqual(""+start.y);
						expect(line2.attr("x2")).toEqual(""+end.x);
						expect(line2.attr("y2")).toEqual(""+end.y);
						expect(line2.hasClass("causal_pass_line")).toBe(true);
						expect(line2.attr("marker-end")).toEqual("url(#arrowMarker)");
					});
					it("should split up up pass", function() {
						var id = "foobar";
						throwObj.sourceJuggler = 1;
						throwObj.targetJuggler = 0;
						var pos1 = causalDiagram.getBeatPosition(2, 1);
						
						causalDiagram.drawThrowLine(draw, id, throwObj, 2, arrowMarker, borderEast, borderWest);
						var line = $(div).find("#"+id);
						var start = causalDiagram.getUpperRight(pos1);
						var end = {
								"x": 300,
								"y": 137.5
						};
						//var end = causalDiagram.getLowerLeft(pos2);
						expect(line.attr("x1")).toEqual(""+start.x);
						expect(line.attr("y1")).toEqual(""+start.y);
						expect(line.attr("x2")).toEqual(""+end.x);
						expect(line.attr("y2")).toEqual(""+end.y);
						expect(line.hasClass("causal_pass_line")).toBe(true);
						expect(line.attr("marker-end")).not.toEqual("url(#arrowMarker)");
						
						var line2 = $(div).find("#"+id+"_A");
						var pos2 = causalDiagram.getBeatPosition(0, 0);
						
						start = {
								"x": 0,
								"y": end.y
						};
						end = causalDiagram.getLowerLeft(pos2);
						expect(line2.attr("x1")).toEqual(""+start.x);
						expect(line2.attr("y1")).toEqual(""+start.y);
						expect(line2.attr("x2")).toEqual(""+end.x);
						expect(line2.attr("y2")).toEqual(""+end.y);
						expect(line2.hasClass("causal_pass_line")).toBe(true);
						expect(line2.attr("marker-end")).toEqual("url(#arrowMarker)");
						
					});

					it("should split up down pass", function() {
						var id = "foobar";
						throwObj.sourceJuggler = 0;
						throwObj.targetJuggler = 1;
						var pos1 = causalDiagram.getBeatPosition(2, 0);
						
						causalDiagram.drawThrowLine(draw, id, throwObj, 2, arrowMarker, borderEast, borderWest);
						var line = $(div).find("#"+id);
						var start = causalDiagram.getLowerRight(pos1);
						var end = {
								"x": 300,
								"y": 137.5
						};
	
						expect(line.attr("x1")).toEqual(""+start.x);
						expect(line.attr("y1")).toEqual(""+start.y);
						expect(line.attr("x2")).toEqual(""+end.x);
						expect(line.attr("y2")).toEqual(""+end.y);
						expect(line.hasClass("causal_pass_line")).toBe(true);
						expect(line.attr("marker-end")).not.toEqual("url(#arrowMarker)");
						
						var line2 = $(div).find("#"+id+"_A");
						var pos2 = causalDiagram.getBeatPosition(0, 1);
						
						start = {
								"x": 0,
								"y": end.y
						};
						end = causalDiagram.getUpperLeft(pos2);
						expect(line2.attr("x1")).toEqual(""+start.x);
						expect(line2.attr("y1")).toEqual(""+start.y);
						expect(line2.attr("x2")).toEqual(""+end.x);
						expect(line2.attr("y2")).toEqual(""+end.y);
						expect(line2.hasClass("causal_pass_line")).toBe(true);
						expect(line2.attr("marker-end")).toEqual("url(#arrowMarker)");
						
					});
				});
			});
		});
		
		describe("selfCurve", function() {
			beforeEach(function() {
				causalDiagram.throwCurves = {};
			});
			it("should draw self 2", function() {
				var id = "foobar";
				throwObj.throwHeight = 2;
				var pos1 = causalDiagram.getBeatPosition(0, 0);
				var pos2 = causalDiagram.getBeatPosition(0, 0);
				causalDiagram.drawSelfCurve(draw, id, throwObj, 0, arrowMarker, null, null);
				var path = $(div).find("#"+id);
				var start = causalDiagram.getUpperLeft(pos1);
				var end = causalDiagram.getUpperRight(pos2);
				var expectedPath = 
					"m "+start.x+" "+start.y+" A 20 30 0 0 1 "+end.x +" "+end.y;
				expect(path.attr("d")).toEqual(expectedPath);
				expect(path.hasClass("causal_pass_line")).toBe(true);
				expect(path.attr("marker-end")).toEqual("url(#arrowMarker)");
				expect(path.attr("fill")).toEqual("transparent");
				expect(causalDiagram.throwCurves[id]).not.toBe(undefined);
			});
			it("should draw self 0", function() {
				var id = "foobar";
				throwObj.throwHeight = 0;
				var pos1 = causalDiagram.getBeatPosition(2, 0);
				var pos2 = causalDiagram.getBeatPosition(0, 0);
				causalDiagram.drawSelfCurve(draw, id, throwObj, 2, arrowMarker, null, null);
				var path = $(div).find("#"+id);
				var start = causalDiagram.getLowerLeft(pos1);
				var end = causalDiagram.getLowerRight(pos2);
				var expectedPath = 
					"m "+start.x+" "+start.y+" A 100 100 0 0 1 "+end.x +" "+end.y;
				expect(path.attr("d")).toEqual(expectedPath);
				expect(path.hasClass("causal_pass_line")).toBe(true);
				expect(path.attr("marker-end")).toEqual("url(#arrowMarker)");
				expect(path.attr("fill")).toEqual("transparent");
			});
			it("should draw self 1", function() {
				var id = "foobar";
				throwObj.throwHeight = 1;
				var pos1 = causalDiagram.getBeatPosition(1, 0);
				var pos2 = causalDiagram.getBeatPosition(0, 0);
				causalDiagram.drawSelfCurve(draw, id, throwObj, 1, arrowMarker, null, null);
				var path = $(div).find("#"+id);
				var start = causalDiagram.getBottom(pos1);
				var end = causalDiagram.getBottom(pos2);
				var expectedPath = 
					"m "+start.x+" "+start.y+" A 100 100 0 0 1 "+end.x +" "+end.y;
				expect(path.attr("d")).toEqual(expectedPath);
				expect(path.hasClass("causal_pass_line")).toBe(true);
				expect(path.attr("marker-end")).toEqual("url(#arrowMarker)");
				expect(path.attr("fill")).toEqual("transparent");
			});
			it("should draw self 6", function() {
				var id = "foobar";
				throwObj.throwHeight = 6;
				var pos1 = causalDiagram.getBeatPosition(0, 0);
				var pos2 = causalDiagram.getBeatPosition(4, 0);
				causalDiagram.drawSelfCurve(draw, id, throwObj, 0, arrowMarker, null, null);
				var path = $(div).find("#"+id);
				var start = causalDiagram.getUpperRight(pos1);
				var end = causalDiagram.getUpperLeft(pos2);
				var expectedPath = 
					"m "+start.x+" "+start.y+" A 300 300 0 0 1 "+end.x +" "+end.y;
				expect(path.attr("d")).toEqual(expectedPath);
				expect(path.hasClass("causal_pass_line")).toBe(true);
				expect(path.attr("marker-end")).toEqual("url(#arrowMarker)");
				expect(path.attr("fill")).toEqual("transparent");
			});
			describe("wrapping", function() {
				var borderWest;
				var borderEast;
				beforeEach(function() {
					//3 beats wide
					borderWest = draw.line(0, 0, 0, 315);
					borderEast = draw.line(300, 0, 300, 315);
					causalDiagram.pattern =
					{
							"beats":["A", "B", "C"]
					};
				});
				describe("wrap eastern border", function() {
					it("should split up self throw", function() {
						var id = "foobar";
						throwObj.sourceJuggler = 0;
						throwObj.targetJuggler = 0;
						throwObj.throwHeight = 4;
						var pos1 = causalDiagram.getBeatPosition(1, 0);

						causalDiagram.drawSelfCurve(draw, id, throwObj, 1, arrowMarker, borderEast, borderWest);
						
						var path = $(div).find("#"+id);
						var start = causalDiagram.getUpperRight(pos1);
						var end = {
								"x": 300,
								"y": 29.910105658160088
						};
						var expectedPath = 
							"m "+start.x+" "+start.y+" A 100 100 0 0 1 "+end.x +" "+end.y;
						expect(path.attr("d")).toEqual(expectedPath);
						expect(path.hasClass("causal_pass_line")).toBe(true);
						expect(path.attr("marker-end")).not.toEqual("url(#arrowMarker)");
						expect(path.attr("fill")).toEqual("transparent");
						
						var path2 = $(div).find("#"+id+"_A");
						var pos2 = causalDiagram.getBeatPosition(0, 0);
						
						start = {
								"x": 0,
								"y": end.y
						};
						end = causalDiagram.getUpperLeft(pos2);
						expectedPath = 
							"m "+start.x+" "+start.y+" A 100 100 0 0 1 "+end.x +" "+end.y;
						expect(path2.attr("d")).toEqual(expectedPath);
						expect(path2.hasClass("causal_pass_line")).toBe(true);
						expect(path2.attr("marker-end")).toEqual("url(#arrowMarker)");
						expect(path2.attr("fill")).toEqual("transparent");

					});
				});

				describe("wrap western border", function() {
					it("should split up self throw", function() {
						var id = "foobar";
						throwObj.sourceJuggler = 0;
						throwObj.targetJuggler = 0;
						throwObj.throwHeight = 0;
						var pos1 = causalDiagram.getBeatPosition(1, 0);

						causalDiagram.drawSelfCurve(draw, id, throwObj, 1, arrowMarker, borderEast, borderWest);

						var path = $(div).find("#"+id);
						var start = causalDiagram.getLowerLeft(pos1);
						var end = {
								"x": 0,
								"y": 120.08989069607223
						};
						var expectedPath = 
							"m "+start.x+" "+start.y+" A 100 100 0 0 1 "+end.x +" "+end.y;
						expect(path.attr("d")).toEqual(expectedPath);
						expect(path.hasClass("causal_pass_line")).toBe(true);
						expect(path.attr("marker-end")).not.toEqual("url(#arrowMarker)");
						expect(path.attr("fill")).toEqual("transparent");

						var path2 = $(div).find("#"+id+"_A");
						var pos2 = causalDiagram.getBeatPosition(2, 0);

						start = {
								"x": 300,
								"y": end.y
						};
						end = causalDiagram.getLowerRight(pos2);
						expectedPath = 
							"m "+start.x+" "+start.y+" A 100 100 0 0 1 "+end.x +" "+end.y;
						expect(path2.attr("d")).toEqual(expectedPath);
						expect(path2.hasClass("causal_pass_line")).toBe(true);
						expect(path2.attr("marker-end")).toEqual("url(#arrowMarker)");
						expect(path2.attr("fill")).toEqual("transparent");

					});
				});
			});
		});
		
		describe("drawSVG", function() {
			var pattern
			var totalBeats;
			var totalJugglers;
			
			var circlesDrawn;
			var throwsDrawn;
			var defsWithArrow;
			
			beforeEach(function() {
				
				totalBeats = 3;
				totalJugglers = 3;
				circlesDrawn = [];
				throwsDrawn = [];
				defsWithArrow = [];
				pattern = {
						"name" : "Test",
						"jugglerCount" : totalJugglers,
						"beats" : []
				};
				for (var i = 0; i < totalBeats; i++) {
					var beat = {
						"throws" : []
					};
					pattern.beats.push(beat);
					for (var j = 0; j < totalJugglers; j++) {
						var throwObj = {
							"sourceJuggler":j,
							"sourceHand":"R",
							"targetJuggler":j,
							"throwHeight": 3
						};
						beat.throws.push(throwObj);
					}
				}

				//clear the page...
				causalDiagram = new CausalDiagram(pattern);
				div = document.createElement("div");
				draw = null;
				causalDiagram.drawCircles = function(draw, beatNb, throws) {
					circlesDrawn[beatNb]={
						"draw": draw,
						"beatNb": beatNb,
						"throws": throws
					};
				}
				causalDiagram.drawThrows = function(draw, beatNb, throws, arrowMarker) {
					throwsDrawn[beatNb] ={
						"draw": draw,
						"beatNb": beatNb,
						"throws": throws,
						"arrowMarker": arrowMarker
					};
				}
				causalDiagram._initArrow = function(defs) {
					defsWithArrow = defs;
					return {
						"id":"fooArrow"
					};
				}
			});
			
			it("should draw the diagram correctly", function() {
				draw = causalDiagram.drawSVG(div);
				
				var mySvg = $(div).find("svg")[0];
				var viewWidth = causalDiagram._BEAT_WIDTH * totalBeats;
				var viewHeight = causalDiagram._BEAT_HEIGHT * (totalJugglers + 1);
				expect(mySvg.attributes.viewBox.value).toEqual("0 0 "+viewWidth+" "+viewHeight);
				
				var defs = mySvg.childNodes[0];
				expect(defs.tagName).toEqual("defs");
				expect(defsWithArrow).toBe(defs.instance);
				
				expect(circlesDrawn.length).toEqual(totalBeats);
				for (var i = 0; i < totalBeats; i++) {
					expect(circlesDrawn[i].draw).toBe(draw);
					expect(circlesDrawn[i].beatNb).toEqual(i);
					expect(circlesDrawn[i].throws).toBe(pattern.beats[i].throws);
				}
					
				expect(throwsDrawn.length).toEqual(totalBeats);
				for (var i = 0; i < totalBeats; i++) {
					expect(throwsDrawn[i].draw).toBe(draw);
					expect(throwsDrawn[i].beatNb).toEqual(i);
					expect(throwsDrawn[i].throws).toBe(pattern.beats[i].throws);
					expect(throwsDrawn[i].arrowMarker.id).toEqual("fooArrow");
				}
			});
		});
		describe("drawThrows", function() {
			
			var linesDrawn;
			var throws;
			beforeEach(function() {

				throws = [];
				linesDrawn=[];
				
				causalDiagram.drawThrowLine = function(draw, id, throwObj, beatNb, arrowMarker, borderEast, borderWest) {
					linesDrawn[id] ={
						"draw": draw,
						"id": id,
						"throwObj": throwObj,
						"beatNb": beatNb,
						"isCurve": false,
						"arrowMarker": arrowMarker
					};
				}
				causalDiagram.drawSelfCurve = function(draw, id, throwObj, beatNb, arrowMarker, borderEast, borderWest) {
					linesDrawn[id] ={
							"draw": draw,
							"id": id,
							"throwObj": throwObj,
							"beatNb": beatNb,
							"isCurve": true,
							"arrowMarker": arrowMarker
					};
				}
				
			});
			
			it("should draw selfs", function() {
				var throwCount = 6;
				for (var i = 0; i < throwCount; i++) {
					throws.push(
							{
								"sourceJuggler":i,
								"sourceHand":"R",
								"targetJuggler":i,
								"throwHeight": i
							}
							);
				}
				causalDiagram.drawThrows(draw, 3, throws, arrowMarker);

				for (var i = 0; i < throwCount; i++) {
					var beat1Pos = causalDiagram.getBeatPosition(3, i);

					var beatDiff = causalDiagram.getBeatDiff(throws[i]);
					var beat2Nb = 3 + beatDiff;
					var beat2Pos = causalDiagram.getBeatPosition(beat2Nb, i);
				
					var id = "throw_3_"+i;
					var line = linesDrawn[id];
					expect(line.draw).toBe(draw);
					expect(line.id).toEqual(id);
					expect(line.throwObj).toBe(throws[i]);
					expect(line.beatNb).toEqual(3);
					expect(line.arrowMarker).toBe(arrowMarker);
					if (i == 3) {
						expect(line.isCurve).toBe(false);
					} else {
						expect(line.isCurve).toBe(true);
					}
				}
								
			});
			
			it("should draw passes", function() {
				var throwCount = 3;
				for (var i = 0; i < throwCount; i++) {
					throws.push(
							{
								"sourceJuggler":i,
								"sourceHand":"R",
								"targetJuggler":i+1,
								"throwHeight": i+3
							}
							);
				}
				causalDiagram.drawThrows(draw, 0, throws, arrowMarker);

				for (var i = 0; i < throwCount; i++) {
					var beat1Pos = causalDiagram.getBeatPosition(0, i);

					var beatDiff = causalDiagram.getBeatDiff(throws[i]);
					var beat2Nb = beatDiff;
					var beat2Pos = causalDiagram.getBeatPosition(beat2Nb, i+1);
				
					var id = "throw_0_"+i;
					var line = linesDrawn[id];
					expect(line.draw).toBe(draw);
					expect(line.id).toEqual(id);
					expect(line.throwObj).toBe(throws[i]);
					expect(line.beatNb).toEqual(0);
					expect(line.arrowMarker).toBe(arrowMarker);
					expect(line.isCurve).toBe(false);
				}
								
			});
		});
	});
	
	describe("_getPatternVersion", function() {
		it("should handle missing version", function() {
			basicPattern.version = null;
			causalDiagram = new CausalDiagram(basicPattern);
			expect(causalDiagram._getPatternVersion()).toEqual(0);
		});

		it("should handle version", function() {
			basicPattern.version = 1;
			causalDiagram = new CausalDiagram(basicPattern);
			expect(causalDiagram._getPatternVersion()).toEqual(1);
		});
	});

	
	describe('getThrowIDEndingOnBeat', function() {
		
		var pattern = null;
		beforeEach(function() {
			pattern = {
				"name" : "Three Count With 441",
				"jugglerCount" : 2,
				"beatCount" : 6,
				"beats" : [
					{
						"throws": [
							{
								"sourceJuggler":0,
								"sourceHand":"R",
								"targetJuggler":1,
								"throwHeight": 4
							},
							{
								"sourceJuggler":1,
								"sourceHand":"R",
								"targetJuggler":1,
								"throwHeight": 3
							}
						]
					},
					{
						"throws": [
							{
								"sourceJuggler":0,
								"sourceHand":"L",
								"targetJuggler":0,
								"throwHeight":1
							},
							{
								"sourceJuggler":1,
								"sourceHand":"L",
								"targetJuggler":0,
								"throwHeight":3
							}
						]
					},
					{
						"throws": [
							{
								"sourceJuggler":0,
								"sourceHand":"R",
								"targetJuggler":0,
								"throwHeight":4
							},
							{
								"sourceJuggler":1,
								"sourceHand":"R",
								"targetJuggler":1,
								"throwHeight":3
							}
						]
					},
					{
						"throws": [
							{
								"sourceJuggler":0,
								"sourceHand":"L",
								"targetJuggler":1,
								"throwHeight":4
							},
							{
								"sourceJuggler":1,
								"sourceHand":"L",
								"targetJuggler":1,
								"throwHeight":3
							}
						]
					},
					{
						"throws": [
							{
								"sourceJuggler":0,
								"sourceHand":"R",
								"targetJuggler":0,
								"throwHeight":1
							},
							{
								"sourceJuggler":1,
								"sourceHand":"R",
								"targetJuggler":0,
								"throwHeight":3
							}
						]
					},
					{
						"throws": [
							{
								"sourceJuggler":0,
								"sourceHand":"L",
								"targetJuggler":0,
								"throwHeight":4
							},
							{
								"sourceJuggler":1,
								"sourceHand":"L",
								"targetJuggler":1,
								"throwHeight":3
							}
						]
					}
				]
			};
			causalDiagram = new CausalDiagram(pattern);
		});
		it('should know which throw ends on which beat', function() {
			expect(causalDiagram.getThrowIDEndingOnBeat(0,0)).toEqual('throw_1_0');
			expect(causalDiagram.getThrowIDEndingOnBeat(0,1)).toEqual('throw_5_1');
			expect(causalDiagram.getThrowIDEndingOnBeat(1,0)).toEqual('throw_5_0');
			expect(causalDiagram.getThrowIDEndingOnBeat(1,1)).toEqual('throw_0_1');
			expect(causalDiagram.getThrowIDEndingOnBeat(2,0)).toEqual('throw_1_1');
			expect(causalDiagram.getThrowIDEndingOnBeat(2,1)).toEqual('throw_0_0');
			expect(causalDiagram.getThrowIDEndingOnBeat(3,0)).toEqual('throw_4_0');
			expect(causalDiagram.getThrowIDEndingOnBeat(3,1)).toEqual('throw_2_1');
			expect(causalDiagram.getThrowIDEndingOnBeat(4,0)).toEqual('throw_2_0');
			expect(causalDiagram.getThrowIDEndingOnBeat(4,1)).toEqual('throw_3_1');
			expect(causalDiagram.getThrowIDEndingOnBeat(5,0)).toEqual('throw_4_1');
			expect(causalDiagram.getThrowIDEndingOnBeat(5,1)).toEqual('throw_3_0');
		})
	});
});