/**
* $BirdsByME this code is parte of site BirdsBy.me 
* Copyright(c) 2011 Isidro Vila Verde (jvverde@gmail.com)
* Dual licensed under the MIT and GPL licenses
* Version: 1.1
* Last Revision: 2012-01-08
*
* Requires jQuery 1.6.2
*
*
*ChangeLog
*version 0.9 (27-12-2011)
*version 1.0 (28-12-2011)
*/
var callback = function(){};


$(document).ready(function(){
	$DEFAULT_PERIOD = 7; //7 days
	var $contentor = $('#contentor');
	var $flickr_key = 'ac3a60e9450fae7d6befc613d6a7d998';
	var $window = $(window);
	(function(){	//only necessary for quirks mode
		var $cursor = 'auto';
		$('#search').hover(function(){
			$cursor = $(this).css('cursor');
			 $(this).css('cursor','pointer');
		},function(){
			 $(this).css('cursor',$cursor);
		});
	})();
	var console = {
		log: function(){},
		warn:function(){},
		error:function(){},
		debug:function(){},
		info:function(){}
	};
	if (window.console) console = window.console;
	var $mystore = {
		get: function($key){
			try{
				return store.get($key)
			}catch(e){
				console.warn('Error getting data from store');
			}
		},
		set: function($key,$val){
			try{
				return store.set($key,$val)
			}catch(e){
				console.warn('Error setting into store');
			}
		}
	};

	var asyncal= (function(){ //The asyncal function asynchronous call a function passed as parameter
		if(window.addEventListener && window.postMessage){
        var timeouts = [];
        var messageName = "zero-timeout-message";

        function handleMessage(event) {
            if (event.source == window && event.data == messageName) {
                event.stopPropagation();
                if (timeouts.length > 0) {
                    var fn = timeouts.shift();
                    fn();
                }
            }
        }
 
        window.addEventListener("message", handleMessage, true);
				
        return function(fn) {
            timeouts.push(fn);
            window.postMessage(messageName, "*");
        }
		}else{ //if not supported just use setTimeout with minimal timeout
			return function(fn){
				setTimeout(fn,50);
			}
		}
	})(); //construct it
	//two auxiliary functions
	function getCurrentDate(){
		var $d = new Date;
		return {
							year: $d.getFullYear(),
							month: $d.getMonth(),
							day: $d.getDay()
		};
	}
	function signalError(rsp){
		$.pnotify({
				pnotify_title: rsp.stat,
				pnotify_text:  rsp.code + ': '+ rsp.message,
				pnotify_opacity: 0.6,
				pnotify_type: 'error',
				pnotify_error_icon: 'ui-icon ui-icon-signal-diag',
				pnotify_hide: true
		});
	}
	//now some global arrays
	var $tags = []; //all tags will be stored here
	var $users = [];

	$.ajax({ //load tags
		dataType:'json',
		url:'names.json',
		success:function($d){
			$tags = $d;
			//some sanity check
			$.each($tags,function($i,$tag){
				if (typeof $tag.flickr === 'undefined'){
					$tag.flickr = {};
				}	
				if (typeof $tag.flickr.users === 'undefined'){
					$tag.flickr.users = [];
				}
				if (typeof $tag.flickr.global === 'undefined'){
					$tag.flickr.global = {};
				}			
			});
			var $tagnames = []; //normalized names;
			for (var $i in $tags){
				$tags[$i].nname = {
					la : $tags[$i].name.la.replace(/ /g,'').toLowerCase(),
					en : $tags[$i].name.en.replace(/ /g,'').toLowerCase()
				};
			}
			main();
		},
		error: function(){
			$.pnotify({
					pnotify_title: 'Error',
					pnotify_text: 'Getting names.json from server',
					pnotify_opacity: 0.6,
					pnotify_type: 'error',
					pnotify_error_icon: 'ui-icon ui-icon-signal-diag',
					pnotify_hide: false
			});
		}
	});
	var main = function(){
		$.ajaxSetup({
			dataType:'jsonp',
			type: 'GET',
			url:'http://api.flickr.com/services/rest/'
		});
		function loadFromFlickr($d,$f){ //always set callback before sent the request. This could be a problem if more than one request is throwed in simultaneous
			$.ajax({
				data:$d,
				beforeSend: function(){
					callback = $f;
				} 
			})
		}
		var $data = { //common data for flickr methods
			api_key: $flickr_key,
			format:'json',
			jsoncallback:'callback'
		};
		var $search = $.extend({},$data,{ //the data (ajax parameter) for search method
			method: 'flickr.photos.search',
			sort: 'date-posted-desc',
			per_page: 1
		});
		var $reference = $('<div class="photoframe">').css({
			position:'absolute',
			'z-index': -3000,
			left: 0,
			top: 0
		}).appendTo($contentor);
		var $wh;		//window height
		var $ww;		//window width
		var $eh = $reference.outerHeight(true);	//photoframe element hight
		var $ew = $reference.outerWidth(true);	//photoframe element width
		var $nc;  //number of columns
		var $rpw; //rows of photos per window
		function update_dimensions(){
			$wh = $window.height();
			$ww = $window.width();
			$nc = ($ww / $ew) | 0;
			if ($nc < 1) $nc = 1;
			$rpw = 1 + $wh / $eh | 0;
		}
		update_dimensions();	//init the above variables
		function get4tag($i,$f,$error){ //get tags[$i] for any user and then callback $f
			var $tag = $tags[$i];
			if (!$tag.flickr){ 
				$tag.flickr ={
					global:{},
					users:{}
				}
			}
			var $d = $.extend({},$search,{
				tags:$tag.name.la,
				sort: 'relevance'
			});
			loadFromFlickr($d,function(rsp){
				if (rsp.stat === "ok" && rsp.photos){ 
					$tag.flickr.global = {
						date: getCurrentDate(),
						cnt: rsp.photos.total
					}
					if (rsp.photos.photo[0]){
						$tag.flickr.global.photo = {
							id: rsp.photos.photo[0].id,
							owner: rsp.photos.photo[0].owner,
							secret: rsp.photos.photo[0].secret,
							farm: rsp.photos.photo[0].farm,
							server: rsp.photos.photo[0].server,
							title: rsp.photos.photo[0].title
						}
					}else{
						$tag.flickr.global.photo = {};
					}
				}else{
					signalError(rsp);
					if($error && $error instanceof Function){
						$error();
					}	
				}
				$f($tag); //finally callback
			})
		}
		function get4tag4user($i,$u,$f,$error){ 	//get tags[$i] for user $u and then callback $f
			var $tag = $tags[$i];

			var $user_id = $users[$u];

			var $d = $.extend({},$search,{
				tags:$tag.nname.la,
				user_id: $user_id
			});
			loadFromFlickr($d,function(rsp){
				if (rsp.stat === "ok" && rsp.photos){
					$tag.flickr.users[$u] = {
						date: getCurrentDate(),
						cnt: rsp.photos.total
					}
					if (rsp.photos.photo[0]){
						$tag.flickr.users[$u].photo = {
							id: rsp.photos.photo[0].id,
							owner: rsp.photos.photo[0].owner,
							secret: rsp.photos.photo[0].secret,
							farm: rsp.photos.photo[0].farm,
							server: rsp.photos.photo[0].server,
							title: rsp.photos.photo[0].title
						}
					}else{
						$tag.flickr.global.photo = {};
					}
					$f($tag);//finally callback
				}else{
					signalError(rsp);
					if($error && $error instanceof Function){
						$error();
					}						
				}
			})
		}
		var $photoframes = new Array();
		function show($i,$u,$n){
			var $tag = $tags[$i];
			var $global = $tags[$i].flickr.global;
			var $user = (function(){
				return $u ? $tag.flickr.users[$u] : $tag.flickr.global;
			})();
			var $reloadCnt = 0; //for img element;
			var $img	=	$('<img/>').attr({
				'data-src': 'http://farm' 
					+ $user.photo.farm 
					+ '.staticflickr.com/' 
					+ $user.photo.server 
					+ '/' 
					+ $user.photo.id 
					+ '_' 
					+ $user.photo.secret 
					+ '_m.jpg',
				'class': 'imghide'
			}).error(function(){
				console.warn('not loaded %d',$reloadCnt);
				$img.hide();
				if ($reloadCnt++ > 20) return;
				setTimeout(function(){
					var $p = $img.parent();
					$img.remove();
					$p.append($img);
					$img.show();
				},100 * $reloadCnt);
			});
			var $photoframe = $('<div class="photoframe"/>')
			.attr('index',$i)
			.append($('<div class="icon"/>').append(
				$('<a/>').attr({
					href : 'http://www.flickr.com/photos/' + $user.photo.owner + '/' + $user.photo.id,
					target: '_blank'
				}).append($img)
			))
			.append($('<div class="desc"/>')
				.append($('<div class="index"/>').text($n))
				.append($('<div class="la"/>').text($tag.name.la))
				.append($('<div class="en"/>').text($tag.name.en))
				.append($('<div class="counters"/>')
					.append($('<div class="user"/>').append(
						$('<a/>').attr({
							href:'http://www.flickr.com/photos/' + $user.photo.owner + '/tags/'+$tag.name.la,
							target: '_blank'
						}).text($user.cnt)
					))
					.append($('<div class="total"/>').append(
						$('<a/>').attr({
							href: 'http://www.flickr.com/photos/tags/'+$tag.name.la,
							target: '_blank'
						}).text($global.cnt)
					))
				)
			);
			$photoframes.push($photoframe);	
			if($photoframes.length <= 2*$rpw*$nc || $photoframes[$photoframes.length - 1 - 2*$rpw*$nc].is('.visible')){
				loadImageIfOnScreen($photoframes);
			}
		} //end show; 
		function getPhotos4user($username, $f,$e){
			if($users[$username]){
				$f();
			}else{ //get user_id associated with username $u
				getUserId($username, function(rsp){
					$users[$username] = rsp.user.nsid; //set it for next time
					$f();
				},$e)
			}
		}
		var $current_user = '';
		var $stop = false;
		$('#stop').click(function(){
			$stop = true;
		});
		var $getUserPhotos = function($username){
			$stop = false;
			$current_user = $username;
			$photoframes = [];
			if($current_user){
				var $run = run($current_user);
				getPhotos4user($current_user,function(){
					var $cnt = 1;  //number of found photos
					getUserTags($users[$username], function(rsp){
						var $utags = [];
						for (var $i in rsp.who.tags.tag){
							$utags[$i] = rsp.who.tags.tag[$i]._content;
						};
						var $foundtags = [];
						var $start = 0;
						(function $f($i){
							if ($i >= $utags.length){
								$foundtags.push(-1); //end mark
								return;
							}else{
								for(var $j = $start; $j < $tags.length; $j++){
									if ($utags[$i] ===  $tags[$j].nname.la){
										$foundtags.push($j);
										$start = $j + 1;	//next time start from next position
										break;
									}
								}
								asyncal(function(){
									$f($i + 1);
								});
							}
						})(0); //start it from 0
						var $getNextTag = function(){ //a recursive function
							if ($foundtags.length == 0){ //if queue is empty
								setTimeout($getNextTag,100); //try later
								return;
							}
							var $i = $foundtags.shift();
							if ($i < 0 || $stop){
								$run.finish($cnt-1);
								return;
							}
							$run.update($i);
							var $tag = $tags[$i];

							var $key4tag = $username + '_' + $i;
							$tag.flickr.users[$username] = $mystore.get($key4tag);
							
							var $setuser = function(){
								var $user =  $tags[$i].flickr.users[$username];
								if ($user){
									$mystore.set($key4tag,$user);
									if ($user.cnt && $user.cnt > 0){
										show($i,$username,$cnt++);
									}
								}
								$getNextTag();
							}
							if ($tags[$i].flickr.users[$username] === undefined 
								||$tags[$i].flickr.users[$username].cnt === undefined 
								||$tags[$i].flickr.users[$username].cnt == 0 //shoud never happens but...
							){ 
								//if not yet defined try to get it from flickr
								get4tag4user($i,$username,$setuser);
							}else{	
								asyncal($setuser);
							}
						}; //end $getNextTag
						$getNextTag(); //start it
					}) //end do getUserTags
				},function(){ //in case of error
					$run.abort();
				});
			}else{
					var $cnt = 1;  //number of found photos
					var $run = run();
					var $getTag = function($i){ //a recursive function
						if ($i >= $tags.length || $stop){
							$run.finish($cnt-1);
							return;
						}
						$run.update($i);
						var $tag = $tags[$i];
			
						if ($tag.flickr.global.cnt){
							asyncal(function(){
								show($i,'',$cnt++);
								$getTag($i+1);
							});
						}else{
							asyncal(function(){
								$getTag($i+1);
							});
						}
					}; //end $getTag
					$getTag(0);
			}
		}; //end getUserPhotos
		$('#search').click(function(){
			$getUserPhotos($('#username').val().toLowerCase());
		})
		////////////////////////////////////auxiliary functions/////////////////////
		function run(){
			var $username = $('#username').val();
			$('.init').removeClass('init');
			$contentor.children('.photoframe').remove(); //clean
			$contentor.children('div.stub').remove();
			$('#username').parent().hide('slow');
			var $init = new Date;
			var $pb = progressbar('slow');
			var $a = $('#author').empty();
			var $c = $('#current_search').text('Initializing...');
			if($username){
				$a.text('Bird species by ' + $username);
			}else{
				$a.text('All species in Flickr');
			}
			function showForm(){
				$('#search,#username').parent().slideDown('slow');
			}
			return {
				finish: function($n){
					if ($n){
						if($username){
							$a.text($n + ' bird species by ' + $username);
						}else{
							$a.text($n + ' bird species in flickr ');
						}
					}
					$c.empty();
					$sort();
					$pb.finish('slow');
					showForm();
				},
				update: function($i){
					$pb.update($i);
					$c.html('Looking for <i>' + $tags[$i].name.la +'</i>');
				},
				abort: function(){
					$c.text('');
					$pb.clean();
					showForm();
				}
			}
		}
		function progressbar($v){
			var $init = new Date;
			var $progressbar = $contentor.find('div#progress').show($v);
			var $value = $progressbar.find('div#value').width('0%').text('');
			var $timeleft = $progressbar.find('div#timeleft').text('Downloading user tags...');
			var $duration = $progressbar.find('div#duration').text('');
			return {
				update: function($i){
					if ($i == 0) $i = 1;
					var $d = new Date - $init;
					//var $tl = $d * ($tags.length / $i - 1) / 1000 * (1 + 0.3 * Math.log(($tags.length - 1) / $i));
					var $tl = $d * ($tags.length / $i - 1) / 1000;
					if ($tl >= 60){
						$tl = Math.round(0.5+$tl/60) + 'min ';
					}else{
						$tl = Math.round(0.5+$tl) + 's';
					}
					$timeleft.text($tl);
					var $percent = Math.round(1000 * $i / $tags.length) / 10; 
					var $p = $percent + '%';
					$value.width($p).text($p);
					if($percent > 50){
						$d /= 1000;
						if ($d > 60) $d = Math.round(0.5+$d/60) + 'min';
						else $d = Math.round(0.5+$d) + 'sec';
						$duration.text($d);
					}
				},
				finish: function($v){
					$timeleft.text(0);
					$value.width('100%').text('100%');
					$progressbar.slideUp($v);
				},
				clean:function($v){
					$progressbar.slideUp($v);
				}
			}
		}
		var $compare = {
			lang_la: function($i,$j){
				if ($tags[$i].name.la < $tags[$j].name.la) return -1;
				if ($tags[$i].name.la > $tags[$j].name.la) return 1;
				return 0;
			},
			lang_en: function($i,$j){
				if ($tags[$i].name.en < $tags[$j].name.en) return -1;
				if ($tags[$i].name.en > $tags[$j].name.en) return 1;	
				return 0;
			},
			lang_gn: function($i,$j){
				if ($tags[$i].name.gn < $tags[$j].name.gn) return -1;
				if ($tags[$i].name.gn > $tags[$j].name.gn) return 1;	
				return 0;
			},
			userPhotos: function($i,$j){
				if (!$current_user){
					if ($tags[$i].flickr.global.cnt < $tags[$j].flickr.global.cnt) return -1;
					if ($tags[$i].flickr.global.cnt > $tags[$j].flickr.global.cnt) return 1;	
					return 0;					
				}else{
					if ($tags[$i].flickr.users[$current_user].cnt < $tags[$j].flickr.users[$current_user].cnt) return -1;
					if ($tags[$i].flickr.users[$current_user].cnt > $tags[$j].flickr.users[$current_user].cnt) return 1;	
					return 0;
				}
			},
			totalPhotos: function($i,$j){
				if ($tags[$i].flickr.global.cnt < $tags[$j].flickr.global.cnt) return -1;
				if ($tags[$i].flickr.global.cnt > $tags[$j].flickr.global.cnt) return 1;	
				return 0;
			}
		};
		var $sort = function(){
			if ($photoframes.length < 2) return;
			var $f = $compare[$('input:radio[name="order"]:checked').val()];
			if (!$f || ! $f instanceof Function) return;				
			function insert($i){
				var $index = $photoframes[$i].attr('index');
				if ($index === undefined){
					console.warn('Error on photo ', $i, $photoframes[$i])
					return;
				}
				var $node = $root;
				do{
					var $cmp = $f($index,$node.index);
					if ($cmp == 0){
						if(!$node.left){
								$node.left ={
									parent: $node,
									index: $index,
									photo: $photoframes[$i]
								}
								break;
						}
						if(!$node.right){
								$node.right ={
									parent: $node,
									index: $index,
									photo: $photoframes[$i]
								}
								break;
						}
						var $d = Math.random();
						if ($d < 0.5){
							$node = $node.right;
						}else{
							$node = $node.left;
						}						
					}else if($cmp < 0){
						if(!$node.left){
								$node.left ={
									parent: $node,
									index: $index,
									photo: $photoframes[$i]
								}
								break;
						}else{
							$node = $node.left;
						}
					}else{
						if(!$node.right){
								$node.right = {
									parent: $node,
									index: $index,
									photo: $photoframes[$i]
								}
								break;
						}else{
							$node = $node.right;
						}
					}
				}while(1);
			}
			function getLesser($node){
				var $n = $node;
				while($n.left) $n = $n.left;
				return $n
			}
			function getNext($node){
				var $right = $node.right;
				if ($right){
					return getLesser($right);
				}else{
					var $p = $node.parent;
					while ($p && $p.right === $node){
						$node = $p;
						$p = $node.parent;
					}
					return $p;
				}
			}
			var $random =[];
			for (var $i = 0; $i < $photoframes.length; $i++){
				$random[$i] = $i;
			}
			for (var $i = 0; $i < $photoframes.length; $i++){
				var $j = Math.floor(Math.random() * $photoframes.length);
				var $tmp = $random[$j];
				$random[$j] = $random[$i];
				$random[$i] = $tmp;
			}
			var $root = {
					index: $photoframes[$random[0]].attr('index'),
					photo: $photoframes[$random[0]]
			}
			for (var $i = 1; $i < $photoframes.length; $i++){
				insert($random[$i]);
			}
			var $r = [];
			var $node = getLesser($root);
			while($node){
				$r.push($node.photo);
				$node = getNext($node);
			}
			$photoframes = $r;
			if ($('input:radio[name="dir"]:checked').val() === 'desc'){
				$photoframes.reverse();
			}
			loadImageIfOnScreen($photoframes);
		}
		$('input:radio[name="order"]').change(function(){
			asyncal($sort);
		});
		$('input:radio[name="dir"]').change(function(){
			asyncal(function(){
				$photoframes.reverse();
				loadImageIfOnScreen($photoframes);
			});
		});

		var loadImageIfOnScreen = function($imgs){
			var $wst = $window.scrollTop();
			var $r = 1 + ($wst / $eh) | 0;
			var $init = 0;
			if ($r > $rpw) $init = ($r - $rpw) * $nc;
			var $end = $init + (3*$rpw) * $nc - 1; 
			if ($end > $imgs.length) $end = $imgs.length;
			$contentor.children('div.photoframe').removeClass('visible');
			for(var $i = $init; $i < $end; $i++){
				var $photoframe = $imgs[$i].addClass('visible').appendTo($contentor);
				var $img = $photoframe.find('.imghide');
				$img.attr({
					src: $img.data('src')
				}).removeClass('imghide');
			}
			$contentor.children('div.photoframe').not('.visible').remove();
			$contentor.children('div.stub').remove();
			$('<div class="stub">').css({	//simulate space at page begin
				'background-image':"url('images/stork.png')",
				visibility:'visible',
				overflow:'hidden',
				height: ($init / $nc *  $eh) + 'px',
				width: $nc * $ew + 'px'	
			}).prependTo($contentor);
			$('<div class="stub">').css({	//simulate space at page end
				'background-image':"url('images/stork.png')",
				visibility:'visible',
				overflow:'hidden',
				height: ( ($imgs.length - $end) / $nc *  $eh) + 'px',
				width: $nc * $ew + 'px' 	
			}).appendTo($contentor);
		};

		$window.scroll(function(){
			asyncal(function(){
				loadImageIfOnScreen($photoframes);
			});
		});
		$window.resize(function(){
				update_dimensions();
			asyncal(function(){
				loadImageIfOnScreen($photoframes)
			});
		});
		function getUserTags($user_id, $f,$e){
			var $d = $.extend({},$data,{
				method: 'flickr.tags.getListUser',
				user_id: $user_id
			});
			loadFromFlickr($d,function(rsp){
				if (rsp.stat === "ok"){
					$f(rsp);
				}else{
					signalError(rsp);
					if($e && $e instanceof Function) $e(rsp)
				}
			});
		}
		function getUserId($username, $f,$e){
			var $d = $.extend({},$data,{
				method: 'flickr.people.findByUsername',
				username: $username
			});
			loadFromFlickr($d,function(rsp){
				if (rsp.stat === "ok"){
					$f(rsp);
				}else{
					signalError(rsp);
					if($e && $e instanceof Function) $e(rsp)
				}
			});
		}
		function getUserInfo($user_id, $f,$e){
			var $d = $.extend({},$data,{
				method: 'flickr.people.getInfo',
				user_id: $user_id
			});
			loadFromFlickr($d,function(rsp){
				if (rsp.stat === "ok"){
					$f(rsp);
				}else{
					signalError(rsp);
					if($e && $e instanceof Function) $e(rsp)
				}
			});
		}
					/*$.each($imgs,function(){
				var $this = $(this);
				var $y = $this.parent().offset().top;		 
				var $p = $y - $wst; //position on window
				if ($p < 2*$w && -$w < $p){
					$this.attr({
						src: $this.data('src')
					}).removeClass('imghide');
				}
			});*/
	} //end of main
});

