/*jslint white: true vars: true browser: true todo: true */
/*jshint camelcase:true, plusplus:true, forin:true, noarg:true, noempty:true, eqeqeq:true, bitwise:true, strict:true, undef:true, unused:true, curly:true, browser:true, devel:true, maxerr:100, white:false, onevar:false */
/*global jQuery:true $:true */

(function ($, undefined) {
	"use strict";

$(document).ready(function() {
	
	module("key-sequence", {
		setup: function() {
			tests.testSetup();
			$(document).on("simulate-keySequence", '#qunit-fixture', tests.assertExpectedEvent);
		},
		
		teardown: function() {
			$(document).off("simulate-keySequence", '#qunit-fixture');
			tests.testTearDown();
		}
	});
	
	//####### Test Functions #######
	test("simple sequence", function() {
		var testElement = $('#textInput');
		
		var testSequence = "fO0BaR1";
		
		tests.expectedEvents = [
			/* f */ {type: "keydown", keyCode: 70}, {type: "keypress", which: "f".charCodeAt(0)}, {type: "keyup", keyCode: 70},
			/* O */ {type: "keydown", keyCode: 79}, {type: "keypress", which: "O".charCodeAt(0)}, {type: "keyup", keyCode: 79},
			/* 0 */ {type: "keydown", keyCode: 48}, {type: "keypress", which: "0".charCodeAt(0)}, {type: "keyup", keyCode: 48},
			/* B */ {type: "keydown", keyCode: 66}, {type: "keypress", which: "B".charCodeAt(0)}, {type: "keyup", keyCode: 66},
			/* a */ {type: "keydown", keyCode: 65}, {type: "keypress", which: "a".charCodeAt(0)}, {type: "keyup", keyCode: 65},
			/* R */ {type: "keydown", keyCode: 82}, {type: "keypress", which: "R".charCodeAt(0)}, {type: "keyup", keyCode: 82},
			/* 1 */ {type: "keydown", keyCode: 49}, {type: "keypress", which: "1".charCodeAt(0)}, {type: "keyup", keyCode: 49},
			{type: "simulate-keySequence", sequence: testSequence}
		];
		
		testElement.simulate("key-sequence", {sequence: testSequence});
		
		strictEqual(testElement.val(), testSequence, "Verify result of sequence");
	});
	
	test("sequence callback", function() {
		var testElement = $('#textInput');
		
		var testSequence = "test";
		
		tests.expectedEvents = [
			/* t */ {type: "keydown", keyCode: 84}, {type: "keypress", which: "t".charCodeAt(0)}, {type: "keyup", keyCode: 84},
			/* e */ {type: "keydown", keyCode: 69}, {type: "keypress", which: "e".charCodeAt(0)}, {type: "keyup", keyCode: 69},
			/* s */ {type: "keydown", keyCode: 83}, {type: "keypress", which: "s".charCodeAt(0)}, {type: "keyup", keyCode: 83},
			/* t */ {type: "keydown", keyCode: 84}, {type: "keypress", which: "t".charCodeAt(0)}, {type: "keyup", keyCode: 84},
			{type: "simulate-keySequence", sequence: testSequence},
			{type: "callback"}
		];
		
		testElement.simulate("key-sequence", {sequence: testSequence, callback: function() {
			tests.assertExpectedEvent({type: "callback"});
		}});
		
	});


	test("no events", function() {
		var testElement = $('#textInput');
		
		var testSequence = "fO0BaR1";
		
		tests.expectedEvents = [
			{type: "simulate-keySequence", sequence: testSequence}
		];
		
		testElement.simulate("key-sequence", {sequence: testSequence, triggerKeyEvents: false});
		
		strictEqual(testElement.val(), testSequence, "Verify result of sequence");
	});

	test("special characters", function() {
		var testElement = $('#textInput');
		
		var testSequence = "_-.,;:+!?";
		
		tests.expectedEvents = [
			/* _ */ {type: "keydown", keyCode: 189}, {type: "keypress", which: "_".charCodeAt(0)}, {type: "keyup", keyCode: 189},
			/* - */ {type: "keydown", keyCode: 189}, {type: "keypress", which: "-".charCodeAt(0)}, {type: "keyup", keyCode: 189},
			/* . */ {type: "keydown", keyCode: 190}, {type: "keypress", which: ".".charCodeAt(0)}, {type: "keyup", keyCode: 190},
			/* , */ {type: "keydown", keyCode: 188}, {type: "keypress", which: ",".charCodeAt(0)}, {type: "keyup", keyCode: 188},
			/* ; */ {type: "keydown", keyCode: 186}, {type: "keypress", which: ";".charCodeAt(0)}, {type: "keyup", keyCode: 186},
			/* : */ {type: "keydown", keyCode: 186}, {type: "keypress", which: ":".charCodeAt(0)}, {type: "keyup", keyCode: 186},
			/* + */ {type: "keydown", keyCode: 187}, {type: "keypress", which: "+".charCodeAt(0)}, {type: "keyup", keyCode: 187},
			/* ! */ {type: "keydown", keyCode:  49}, {type: "keypress", which: "!".charCodeAt(0)}, {type: "keyup", keyCode: 49},
			/* ? */ {type: "keydown", keyCode: 191}, {type: "keypress", which: "?".charCodeAt(0)}, {type: "keyup", keyCode: 191},
			{type: "simulate-keySequence", sequence: testSequence}
		];
		
		testElement.simulate("key-sequence", {sequence: testSequence});
	});
	
	test("special sequences", function() {
		var testElement = $('#textInput');
		
		var testSequence = "as{selectall}{del}f{leftarrow}{{}b{rightarrow}{backspace}";
		
		tests.expectedEvents = [
			/* a */ {type: "keydown", keyCode: 65}, {type: "keypress", which: "a".charCodeAt(0)}, {type: "keyup", keyCode: 65},
			/* s */ {type: "keydown", keyCode: 83}, {type: "keypress", which: "s".charCodeAt(0)}, {type: "keyup", keyCode: 83},
			/* {del} */ {type: "keydown", keyCode: 46}, {type: "keyup", keyCode: 46},
			/* f */ {type: "keydown", keyCode: 70}, {type: "keypress", which: "f".charCodeAt(0)}, {type: "keyup", keyCode: 70},
			/* {leftarrow} */ {type: "keydown", keyCode: 37}, {type: "keyup", keyCode: 37},
			/* { */ {type: "keydown", keyCode: 219}, {type: "keypress", which: "{".charCodeAt(0)}, {type: "keyup", keyCode: 219},
			/* b */ {type: "keydown", keyCode: 66}, {type: "keypress", which: "b".charCodeAt(0)}, {type: "keyup", keyCode: 66},
			/* {rightarrow} */ {type: "keydown", keyCode: 39}, {type: "keyup", keyCode: 39},
			/* {backspace} */ {type: "keydown", keyCode: 8}, {type: "keyup", keyCode: 8},
			{type: "simulate-keySequence", sequence: testSequence}
		];
		
		testElement.simulate("key-sequence", {sequence: testSequence});
		
		strictEqual(testElement.val(), "{b", "Verify result of sequence (this is known to fail in IE)");
	});
	
	test("line break", function() {
		var testElement = $('#textInput');
		
		var testSequence = "foo{enter}bar";
		
		tests.expectedEvents = [
			/* f */ {type: "keydown", keyCode: 70}, {type: "keypress", which: "f".charCodeAt(0)}, {type: "keyup", keyCode: 70},
			/* o */ {type: "keydown", keyCode: 79}, {type: "keypress", which: "o".charCodeAt(0)}, {type: "keyup", keyCode: 79},
			/* o */ {type: "keydown", keyCode: 79}, {type: "keypress", which: "o".charCodeAt(0)}, {type: "keyup", keyCode: 79},
			/* {enter} */ {type: "keydown", keyCode: 13}, {type: "keypress", which: 13}, {type: "keyup", keyCode: 13},
			/* b */ {type: "keydown", keyCode: 66}, {type: "keypress", which: "b".charCodeAt(0)}, {type: "keyup", keyCode: 66},
			/* a */ {type: "keydown", keyCode: 65}, {type: "keypress", which: "a".charCodeAt(0)}, {type: "keyup", keyCode: 65},
			/* r */ {type: "keydown", keyCode: 82}, {type: "keypress", which: "r".charCodeAt(0)}, {type: "keyup", keyCode: 82},
			{type: "simulate-keySequence", sequence: testSequence}
		];
		
		testElement.simulate("key-sequence", {sequence: testSequence});
		
		strictEqual(testElement.val(), "foo\nbar", "Verify result of sequence (this is known to fail in Opera on Windows)");
	});
	
	test("delay", function() {
		var testElement = $('#textInput');
		
		var keyDelay = 100,
			testSequence = "test",
			lastEventOccurrence;
		
		tests.expectedEvents = [
			/* t */ {type: "keydown", keyCode: 84}, {type: "keypress", which: "t".charCodeAt(0)}, {type: "keyup", keyCode: 84},
			/* e */ {type: "keydown", keyCode: 69}, {type: "keypress", which: "e".charCodeAt(0)}, {type: "keyup", keyCode: 69},
			/* s */ {type: "keydown", keyCode: 83}, {type: "keypress", which: "s".charCodeAt(0)}, {type: "keyup", keyCode: 83},
			/* t */ {type: "keydown", keyCode: 84}, {type: "keypress", which: "t".charCodeAt(0)}, {type: "keyup", keyCode: 84},
			{type: "simulate-keySequence", sequence: testSequence }
		];
		
	
		// Unbind "normal" assertExpectedEvent function and replace with a fuzzy variant of it
		function assertExpectedEventDelay(event) {
			var delay = Date.now() - lastEventOccurrence;
			ok(delay >= keyDelay-10, "Verify events occur delayed (delay: "+delay+")"); // keyDelay-10 means tolerance of 10ms
			for (var prop in tests.expectedEvents[0]) {
				if (tests.expectedEvents[0].hasOwnProperty(prop)) {
					strictEqual(event[prop], tests.expectedEvents[0][prop], "Comparing "+prop+" (expected: "+tests.expectedEvents[0][prop]+")");
				}
			}
			if (event.type === tests.expectedEvents[0].type) {
				tests.expectedEvents.shift();
			}
			lastEventOccurrence = Date.now();
		}
		$(document).off("keydown", "#qunit-fixture", tests.assertExpectedEvent).on("keydown", "#qunit-fixture", assertExpectedEventDelay);
		
		stop();
		lastEventOccurrence = Date.now()-keyDelay; // The first key simulated without delay
		
		testElement.simulate("key-sequence", {sequence: testSequence, delay: keyDelay});
		
		setTimeout(function() {
			start();
		},(testSequence.length+2)*keyDelay);

	});
	
	// See issue #6 (https://github.com/j-ulrich/jquery-simulate-ext/issues/6)
	test("delay, spaces in sequence, non-input element", function() {
		var testElement = $('#emptyDiv');
		
		var keyDelay = 5,
			testSequence = "a b c";
		
		tests.expectedEvents = [
			/* a */ {type: "keydown", keyCode: 65}, {type: "keypress", which: "a".charCodeAt(0)}, {type: "keyup", keyCode: 65},
			/*   */ {type: "keydown", keyCode: 32}, {type: "keypress", which: " ".charCodeAt(0)}, {type: "keyup", keyCode: 32},
			/* b */ {type: "keydown", keyCode: 66}, {type: "keypress", which: "b".charCodeAt(0)}, {type: "keyup", keyCode: 66},
			/*   */ {type: "keydown", keyCode: 32}, {type: "keypress", which: " ".charCodeAt(0)}, {type: "keyup", keyCode: 32},
			/* c */ {type: "keydown", keyCode: 67}, {type: "keypress", which: "c".charCodeAt(0)}, {type: "keyup", keyCode: 67},
			{type: "simulate-keySequence", sequence: testSequence }
		];

		stop();
		testElement.simulate("key-sequence", {sequence: testSequence, delay: keyDelay,
			callback: function() {
			strictEqual(testElement.text(), testSequence, "Verify result of sequence");
			start();
		}
		});
		


	});

	module("quirk-detection", {
		
	});
	
	asyncTest("quirk detection", 1, function() {
		setTimeout(function() {
			var iFrameDoc = window.frames[0].document;
	
			var jquery = iFrameDoc.createElement('script');
			jquery.src = "../libs/jquery-1.10.2.js";
			iFrameDoc.body.appendChild(jquery);
	
			var bililiteRange = iFrameDoc.createElement('script');
			bililiteRange.src = "../libs/bililiteRange.js";
			iFrameDoc.body.appendChild(bililiteRange);
			
			var jquerySimulate = iFrameDoc.createElement('script');
			jquerySimulate.src = "../libs/jquery.simulate.js";
			iFrameDoc.body.appendChild(jquerySimulate);
			
			var simulateExt = iFrameDoc.createElement('script');
			simulateExt.src = "../src/jquery.simulate.ext.js";
			iFrameDoc.body.appendChild(simulateExt);
	
			var simulateExtKeySequence = iFrameDoc.createElement('script');
			simulateExtKeySequence.src = "../src/jquery.simulate.key-sequence.js";
			iFrameDoc.body.appendChild(simulateExtKeySequence);
			
			setTimeout(function() {
				notStrictEqual(window.frames[0].jQuery.simulate.prototype.quirks.delayedSpacesInNonInputGlitchToEnd, undefined, "Quirk detection was executed");
				start();
			}, 2000);
		}, 1000);
	});
	
	// See issue #9 (https://github.com/j-ulrich/jquery-simulate-ext/issues/9)
	asyncTest("disable quirk detection", 1, function() {
		setTimeout(function() {
			var iFrameDoc = window.frames[0].document;
	
			var jquery = iFrameDoc.createElement('script');
			jquery.src = "../libs/jquery-1.10.2.js";
			iFrameDoc.body.appendChild(jquery);
	
			var bililiteRange = iFrameDoc.createElement('script');
			bililiteRange.src = "../libs/bililiteRange.js";
			iFrameDoc.body.appendChild(bililiteRange);
			
			var jquerySimulate = iFrameDoc.createElement('script');
			jquerySimulate.src = "../libs/jquery.simulate.js";
			iFrameDoc.body.appendChild(jquerySimulate);
			
			setTimeout(function() {
				window.frames[0].jQuery.simulate.ext_disableQuirkDetection = true;
	
				var simulateExt = iFrameDoc.createElement('script');
				simulateExt.src = "../src/jquery.simulate.ext.js";
				iFrameDoc.body.appendChild(simulateExt);
	
				var simulateExtKeySequence = iFrameDoc.createElement('script');
				simulateExtKeySequence.src = "../src/jquery.simulate.key-sequence.js";
				iFrameDoc.body.appendChild(simulateExtKeySequence);
				
				setTimeout(function() {
					strictEqual(window.frames[0].jQuery.simulate.prototype.quirks.delayedSpacesInNonInputGlitchToEnd, undefined, "Quirk detection was disabled");
					start();
				}, 2000);
			}, 2000);
		}, 1000);
	});

});
	
}(jQuery));