/* Timer -- Singleton which manages setInterval() reuse
 *          for multiple concurrent animations
 *
 * e.g. var t = Timer.register(fn, obj, ...args);
 *      // fn is called on obj with args every Timer.INTERVAL milliseconds
 *
 *      Timer.unregister(t);
 *      // fn will no longer be called
 */

Timer = {
	register: function() {
		var r = this.REGISTER,
		    c = Function.prototype.call;

		r.push(arguments);

		if (!this.__ID__) {
			this.__ID__ = setInterval(propagate, this.INTERVAL);
		}

		return arguments;

		function propagate() {
			var i = r.length;

			while (i--) {
				c.apply(c, r[i]);
			}
		}
	},
	unregister: function(t) {
		var r = this.REGISTER,
		    i = r.length;

		while (i--) {
			if (r[i] === t) {
				r.splice(i, 1);
				break;
			}
		}

		if (r.length === 0) {
			clearInterval(this.__ID__);
			delete this.__ID__;
		}
	},
	REGISTER: [],
	INTERVAL: 25
};


/* Animator -- Constructor to plot points along the curve y with
 *             supplied origin and destination
 *
 *             y = (destination - origin) * sin(x) + origin
 *
 * e.g. var anim = new Animator(100, fn);
 *      // Optionally modify anim.duration = (number of milliseconds)
 *
 *      anim.seek(0);
 *      // fn(point, anim) is called with points in range (100, 0]
 */

function Animator(origin, update) {
	this.checkpoints = [];
	this.value       = origin;
	this.update      = update;
}

Animator.prototype.duration = 1000;

Animator.prototype.seek = function(n) {
	this.scalar   = Math.PI / (2 * this.duration);
	this.__DATA__ = Timer.register(this.step, this, new Date(), this.value, n);
};

Animator.prototype.step = function(start, origin, destination) {
	var t = new Date() - start,
	    x = this.scalar * t,
	    y = Math.round((destination - origin) * Math.sin(x) + origin);

	if (y === this.value) {
		return;
	}

	if (t >= this.duration || y === destination) {
		this.value = destination;
		t          = this.duration;
		this.stop();
	} else {
		this.value = y;
	}

	if ((t/this.duration) >= this.checkpoints[0]) {
		this.checkpoints.shift().call(this, this.value);
	}

	this.update.call(this, this.value);
};

Animator.prototype.stop = function() {
	Timer.unregister(this.__DATA__);
};

Animator.prototype.addCheckpoint = function(value, responder) {
	this.checkpoints.push(new Animator.Checkpoint(value, responder));
	this.checkpoints.sort();
};

Animator.Checkpoint = function(value, responder) {
	this.value     = value;
	this.responder = responder;
};

Animator.Checkpoint.prototype.valueOf = function() {
	return this.value;
};

Animator.Checkpoint.prototype.call = function() {
	Function.prototype.call.apply(this.responder, arguments);
};


// DOM -- Miscellaneous utilities for DOM manipulation

DOM = {
	hasClass: function(element, className) {
		var pattern = new RegExp('(?:^|\\s)' + className + '(?:\\s|$)');

		return pattern.test(element.className);
	},
	addClass: function(element, className) {
		return element.className += (element.className && ' ') + className;
	},
	removeClass: function(element, className) {
		var classes = element.className.split(/\s+/g),
		    i       = classes.length;

		while (i) {
			if (classes[--i] == className) {
				classes.splice(i, 1);
			}
		}

		return element.className = classes.join(' ');
	}
};


/* Maestro -- Object whose keys are element IDs and values are functions
 *            to be invoked when a matching element exists
 *
 *            (Requires the function 'blqOnDomReady' defined in blq_core.js
 *            to initialise.)
 */

Maestro = {
	'caption-video': function(caps) {
		// Behaviour for video thumbnails in gallery
		var thumbs    = caps.getElementsByTagName('a'),
		    i         = thumbs.length,
		    next      = document.getElementById('gallery-next'),
		    previous  = document.getElementById('gallery-previous'),
		    viewer    = document.getElementById('video-player'),
		    trigger   = document.getElementById('gallery-mode-videos'),
		    slide     = new Animator(0, setPosition),
		    x         = 0,
		    offset    = 402, // horizontal px difference between thumb 1 and 4
		    threshold = offset * Math.ceil(i / 3),
		    current   = thumbs[0];

		while (i) {
			thumbs[--i].onclick = showVideo;
		}

		slide.duration  = 600;

		trigger.onclick = function() {
			if (!DOM.hasClass(viewer, 'active')) {
				var non = document.getElementById('gallery-viewer');

				DOM.removeClass(non, 'active');
				DOM.removeClass(document.getElementById('caption-image'), 'active');

				DOM.addClass(caps, 'active');
				DOM.addClass(viewer, 'active');
				
				focusVideos();
			}
			return false;
		};

		if (DOM.hasClass(viewer, 'active')) {
			focusVideos();
		}

		function focusVideos() {
			next.onclick = function() {
				if (thumbs.length > 3) {
					x = (x + offset) % threshold;
					slide.stop();
					slide.seek(0 - x);
				}
				return false;
			};

			previous.onclick = function() {
				if (thumbs.length > 3) {
					x = (threshold + x - offset) % threshold;
					slide.stop();
					slide.seek(0 - x);
				}
				return false;
			};

			showVideo();
		}

		function showVideo() {
			var e,
			    emp  = new embeddedMedia.Player(),
			    path = (this.parentNode ? current = this : current).parentNode.title,
			    s    = viewer.parentNode.getElementsByTagName('embed'),
			    i    = s.length;

			while (i) {
				e = s[--i];
				e.parentNode.removeChild(e);
			}

			emp.setWidth(445);
			emp.setHeight(286);
			emp.setDomId('video-player');
			emp.setPlaylist('http://www.bbc.co.uk/musictv/maestro/emp/' + path);
			emp.set('config_settings_skin', 'black');
			emp.set('autoplay', 'true');
			emp.set('autoPlay', 'true');
			emp.set('auto_play', 'true');
			emp.write();

			document.getElementById('caption-text').firstChild.data
				= current.getElementsByTagName('img')[0].alt;

			return false;
		}

		function setPosition(x) {
			for (var i = 0, a; a = thumbs[i++];) {
				a.style.left = x + 'px';
			}
		}
	},
	'caption-image': function(caps) {
		// Behaviour for image thumbnails in gallery
		var thumbs    = caps.getElementsByTagName('a'),
		    i         = thumbs.length,
		    next      = document.getElementById('gallery-next'),
		    previous  = document.getElementById('gallery-previous'),
		    viewer    = document.getElementById('gallery-viewer'),
		    trigger   = document.getElementById('gallery-mode-images'),
		    slide     = new Animator(0, setPosition),
		    x         = 0,
		    offset    = 402, // horizontal px difference between thumb 1 and 4
		    threshold = offset * Math.ceil(i / 3),
		    fade      = new Animator(100, setOpacity),
		    trident   = /*@cc_on!@*/false,
		    caption   = thumbs[0].getElementsByTagName('img')[0].alt;

		while (i) {
			thumbs[--i].onclick = viewImage;
		}

		slide.duration = fade.duration = 600;

		trigger.onclick = function() {
			if (!DOM.hasClass(viewer, 'active')) {
				var e,
				    s = viewer.parentNode.getElementsByTagName('embed'),
				    i = s.length;

				while (i) {
					e = s[--i];
					e.parentNode.removeChild(e);
				}

				DOM.removeClass(document.getElementById('video-player'), 'active');
				DOM.removeClass(document.getElementById('caption-video'), 'active');

				DOM.addClass(caps, 'active');
				DOM.addClass(viewer, 'active');
				
				focusImages();
			}
			return false;
		};

		if (DOM.hasClass(viewer, 'active')) {
			focusImages();
		}

		function focusImages() {
			next.onclick = null;
			previous.onclick = null;

			next.onclick = function() {
				if (thumbs.length > 3) {
					x = (x + offset) % threshold;
					slide.stop();
					slide.seek(0 - x);
				}
				return false;
			};

			previous.onclick = function() {
				if (thumbs.length > 3) {
					x = (threshold + x - offset) % threshold;
					slide.stop();
					slide.seek(0 - x);
				}
				return false;
			};

			document.getElementById('caption-text').firstChild.data = caption;
		}

		function viewImage() {
			if (viewer.src == this.href) return false;

			document.getElementById('caption-text').firstChild.data
				= caption = this.getElementsByTagName('img')[0].alt;

			var a   = false,
			    b   = false,
			    img = new Image();

			fade.stop();
			fade.checkpoints.length = 0;

			if (fade.value == 0) reveal();
			else {
				fade.seek(0);
				fade.addCheckpoint(1, function() {
					if (b) reveal(); else a = true;
				});
				img.onload = function() {
					if (a) reveal(); else b = true;
				};
				img.src = this.href;
			}

			function reveal() {
				viewer.onload = function() {
					fade.stop();
					fade.seek(100);
				};
				viewer.src = img.src;
			}

			return false;
		}

		function setPosition(x) {
			for (var i = 0, a; a = thumbs[i++];) {
				a.style.left = x + 'px';
			}
		}

		function setOpacity(percent) {
			if (trident) {
				viewer.style.zoom   = '1';
				viewer.style.filter = 'alpha(opacity=' + percent + ')';
			} else {
				viewer.style.opacity = percent / 100;
			}
		}
	},
	'student-intro-carousel-nav': function(nav) {
		// Init fade animations for homepage carousel
		var s,
		    target   = document.getElementById('carousel-presenter'),
		    students = nav.getElementsByTagName('a'),
		    i        = students.length,
		    position = selectStudent(students),
		    selected = students[position],
		    a        = new Animator(100, setOpacity),
		    b        = new Animator(100, setOpacity),
		    next     = document.getElementById('carousel-next'),
		    previous = document.getElementById('carousel-previous'),
		    trident  = /*@cc_on!@*/false,
		    ids      = {
			               'David Soul'     : 'carousel-david-soul',
			               'Katie Derham'   : 'carousel-katie-derham',
			               'Goldie'         : 'carousel-goldie',
			               'Alex James'     : 'carousel-alex-james',
			               'Sue Perkins'    : 'carousel-sue-perkins',
			               'Bradley Walsh'  : 'carousel-bradley-walsh',
			               'Jane Asher'     : 'carousel-jane-asher',
			               'Peter Snow'     : 'carousel-peter-snow',
			               'Clive Anderson' : 'carousel-presenter',
			               'The Mentors'    : 'carousel-mentors',
			               'The Orchestra'  : 'carousel-orchestra',
			               'The Judges'     : 'carousel-judges'
		               };

		lazyLoadImage(position);

		DOM.removeClass(students[8].parentNode, 'selected');
		DOM.removeClass(target, 'selected');
		DOM.removeClass(target, 'default');

		target = getTarget(selected);

		DOM.addClass(selected.parentNode, 'selected');
		DOM.addClass(target, 'selected');

		while (i--) {
			s         = students[i];
			s.onclick = changeStudent;

			if (i != 8 && i != position) {
				lazyLoadImage(i);
			}
		}

		function selectStudent(all) {
			var t = [],
			    i = all.length - 4;

			while (i) {
				if (!/eliminated/.test(all[--i].className)) {
					t.push(i);
				}
			}

			return t[Math.floor(Math.random() * t.length)] || 8;
		}

		function lazyLoadImage(i) {
			var img = new Image(),
			    key = students[i].firstChild.data,
			    tar = document.getElementById(ids[key]);

			img.src       = getImagePath(i);
			img.alt       = key;
			img.className = 'homepage-image';

			return tar.appendChild(img);
		}

		function getImagePath(i) {
			var a = '/musictv/maestro/assets/style/images/',
			    z = DOM.hasClass(students[i], 'eliminated') ? '-eliminated.jpg'
			                                                : '.jpg';
			switch (i) {
				case  0: return a + 'carousel-david-soul'    + z;
				case  1: return a + 'carousel-katie-derham'  + z;
				case  2: return a + 'carousel-goldie'        + z;
				case  3: return a + 'carousel-alex-james'    + z;
				case  4: return a + 'carousel-sue-perkins'   + z;
				case  5: return a + 'carousel-bradley-walsh' + z;
				case  6: return a + 'carousel-jane-asher'    + z;
				case  7: return a + 'carousel-peter-snow'    + z;
				case  8: return a + 'carousel-presenter'     + z;
				case  9: return a + 'carousel-mentors'       + z;
				case 10: return a + 'carousel-orchestra'     + z;
				case 11: return a + 'carousel-judges'        + z;
			}
		}

		next.onclick = function() {
			DOM.removeClass(selected.parentNode, 'selected');

			position += 1;
			position %= students.length;
			selected  = students[position];

			hideStudent();

			return false;
		};

		previous.onclick = function() {
			DOM.removeClass(selected.parentNode, 'selected');

			position += students.length - 1;
			position %= students.length;
			selected  = students[position];

			hideStudent();

			return false;
		};

		function changeStudent() {
			if (this !== selected) {
				DOM.removeClass(selected.parentNode, 'selected');

				position = getPosition(this);
				selected = this;

				hideStudent();
			}

			return false;
		}

		function hideStudent() {
			a.checkpoints.length = 0;
			a.duration = b.duration = 400; // Fade-out duration 400 ms

			DOM.addClass(selected.parentNode, 'selected');

			a.stop();
			b.stop();

			a.seek(0);
			b.seek(0);

			if (a.value === 0) {
				showStudent();
			} else {
				a.addCheckpoint(1, showStudent);
			}
		}

		function showStudent() {
			var previous = target;

			target = getTarget(selected);

			if (previous === target) {
				return;
			}

			DOM.removeClass(previous, 'selected');

			a.duration           = b.duration = 1000; // Fade-in duration 1000 ms
			a.checkpoints.length = 0;

			setOpacity.call(a, a.value = 0);
			setOpacity.call(b, b.value = 0);

			DOM.addClass(target, 'selected');

			a.stop();
			b.stop();

			a.seek(100);

			// Animation 'b' starts halfway (0.5) through animation 'a'
			a.addCheckpoint(0.5, function() {
				b.seek(100);
			});
		}

		function setOpacity(percent) {
			if (trident) {
				this.style.zoom   = '1';
				this.style.filter = 'alpha(opacity=' + percent + ')';
			} else {
				this.style.opacity = percent / 100;
			}
		}

		function getTarget(link) {
			var m = document.getElementById(ids[link.firstChild.data]),
			    n = m.getElementsByTagName('img');

			a.style = n[n.length - 1].style;
			b.style = m.getElementsByTagName('div')[0].style;

			return m;
		}

		function getPosition(item) {
			var i = students.length;

			while (i--) {
				if (students[i] === item) {
					return i;
				}
			}

			return 0;
		}
	},
	'search': function(input) {
		// Perform video search with supplied JSON database
		var DATA,
		    JSON_URI = document.getElementsByName('json')[0].value,
		    tBody    = document.getElementById('left-three-quarter-block')
		                       .getElementsByTagName('tbody')[0],
		    tags     = tBody.getElementsByTagName('a'),
		    i        = tags.length,
		    keywords = '';

		while (i--) {
			if (tags[i].parentNode.className == 'second-col') {
				tags[i].onclick = searchTag;
			}
		}

		input.form.onsubmit = function() { onkey(); return false };
		input.onkeypress    = onkey;

		function onkey() {
			if (keywords != input.value) {
				keywords = input.value;
				(DATA ? getResults : fetchData)(input.value);
			}
		}

		function getResults(data) {
			var w = formatQuery(data),
			    r = DATA.videos,
			    i = r.length,
			    m = [];

			while (i--) {
				v = r[i];

				if (w.test(v.tags)) {
					m.push(v);
				}
			}

			buildResults(m);
		}

		function buildResults(results) {
			var r,
			    a,
			    td,
			    tr,
			    i = results.length,
			    e = tBody.cloneNode(false);

			if (i == 0) {
				td = tBody.appendChild(document.createElement('tr'))
				          .appendChild(document.createElement('td'));
				td.colspan = 2;
				td.appendChild(document.createTextNode('No videos match "' + input.value + '".'));
			}

			while (i--) {
				r      = results[i];
				a      = document.createElement('a');
				tr     = document.createElement('tr');
				td     = document.createElement('td');
				a.href = r.href;

				a.appendChild(document.createTextNode(r.name));
				e.appendChild(tr).appendChild(td).appendChild(a);
				tr.appendChild(createTags(r.tags));
			}

			tBody.parentNode.replaceChild(e, tBody);
			tBody = e;
		}

		function createTags(tags) {
			var a,
			    td = document.createElement('td'),
			    i  = tags.length;

			td.className = 'second-col';

			while (i--) {
				a         = td.appendChild(document.createElement('a'));
				a.href    = '#';
				a.onclick = searchTag;

				a.appendChild(document.createTextNode(tags[i]));
				td.appendChild(document.createTextNode(', '));
			}

			td.removeChild(td.lastChild);

			return td;
		}

		function fetchData() {
			var client = window.XMLHttpRequest ? new XMLHttpRequest()
			                                   : new ActiveXObject('Microsoft.XMLHTTP');

			client.open('GET', JSON_URI, true);

			client.onreadystatechange = function() {
				if (client.readyState == 4 && client.status == 200) {
					DATA             = eval('(' + client.responseText + ')');
					input.onkeypress = onkey;
					client           = null;

					DOM.removeClass(input.form.parentNode, 'busy');
					getResults(input.value);
				}
			};

			client.send(null);

			DOM.addClass(input.form.parentNode, 'busy');
			input.onkeypress = null;
		}

		function searchTag() {
			input.value = this.firstChild.data;

			//input.form.onsubmit();
			onkey();

			return false;
		}

		function formatQuery(data) {
			var m = data.replace(/(?=[|?.+*^$(){}[\\\]])/g, '\\').match(/\S+/g),
			    n = m.length,
			    r = [],
			    i = 0,
			    j;

			while (i < n) {
				j = i + 1;

				while (j <= n) {
					r.push(m.slice(i, j).join(' '));

					++j;
				}

				++i;
			}

			return new RegExp(r.join('|'), 'i');
		}
	},
	'vtngResultsList': function(list) {
		// Add a cap image to each item in the voting list 
		var b,
		    s = list.getElementsByTagName('img'),
		    i = s.length;
		
		while (i--) {
			b = new Image();
			b.src = '/musictv/maestro/assets/style/images/vote' + s[i].id.replace(/[^0-9]/g, '') + 'end.jpg';
			b.style.height = '17px';
			s[i].parentNode.appendChild(b);
		}
	}
};

blqOnDomReady(function() {
	var id,
	    element;

	for (id in Maestro) {
		if (element = document.getElementById(id)) {
			Maestro[id](element);
		}
	}
});

try {
	DOM.addClass(document.documentElement, 'js');
	// Prevent background-image flicker in IE:
	document.execCommand("BackgroundImageCache", false, true);
} catch (err) {}