/*global window: false, document: false, clearInterval: false */

(function() {
	
  
  /**
  * General helper utility function
  */
	var Util = function() {
	
		//an array of events to be stored so that we can remove them on unload
		var events = [];
	
		//add the binding
		Function.prototype.bind = function(obj) {
			var method = this;
			return function(){
				return method.apply(obj, arguments);
			};
		};
		
	
		/**
		* finds the index of the event in the event list
		* @return index int index of event in event list
		*/
		var findEventInList = function(obj, eventName, callback) {
		
			var tE, i, len = events.length;
		
			for (i=0;i<len;i++) {
				tE = events[i];
				if (tE.obj === obj && tE.eventName === eventName && tE.callback === callback) {
					return i;
				}
			}
		
			return -1;
		};
	  
    /**
    * adds an event to the list of events
    */
		var addEventToList = function(obj, eventName, callback) {
			events[events.length] = {
				"obj":obj,
				"eventName":eventName,
				"callback":callback
			};
		};
	
    /**
    * removes an event from the list of events
    */
		var removeEventFromList = function(targ, eventName, callback) {
			var eventIndex = findEventInList(targ, eventName, callback);
			if (eventIndex >= 0) {
				
        delete events[eventIndex].obj;
        delete events[eventIndex].eventName;
        delete events[eventIndex].callback;
        
        events.splice(eventIndex, 1);
				
				return true;
			} else {
				return false;
			}
		};
	
    /**
    * checks to see if an event exists in the event queue
    */
		var eventExists = function(obj, eventName, callback) {
				return (findEventInList(obj, eventName, callback) >= 0);
		};
	
		return {
			/**
			* converts an rgb hex string to a series of integers
			*/
			convertRGBHexToInt:function(ostr) {
						
				if (ostr.indexOf('#')+1) {
				    ostr = ostr.replace("#",'');
			    }
			    
				var r = parseInt( ostr.substring(0,2), 16);
				var g = parseInt( ostr.substring(2,4), 16);
				var b = parseInt( ostr.substring(4), 16);
			
				return [r,g,b];
			
			},
		
			/**
			* converts an rgb integer array to a hex string
			*/
			convertRGBIntToHex:function(r,g,b) {
				var x = (Math.round(r).toString(16)) , 
				    y = (Math.round(g).toString(16)), 
				    z = (Math.round(b).toString(16));
				    
				return ("#" + ((x.length === 1) ? "0"+x : x) + ((y.length === 1) ? "0"+y : y)  + ((z.length === 1) ? "0"+z : z)); 
			},
		
			/**
			* attach a callback to an element when an event is fired
			* @param obj HTMLElement element to bind event to
			* @param eventName string name of event to bind to
			* @param callback Function function to call when the event triggers
			* @param bindingElement Object object to bind the callback to so that this refers to the intended item
			*/		
			addEvent:function(obj, eventName, callback, bindingElement) {
			
				if (typeof(bindingElement) !== "undefined") {
					callback = callback.bind(bindingElement);
				}
			
				//you can only add the event once
				if (!eventExists(obj,eventName,callback)) {
				
					//add it to the array of events so that we can remove it on unload
					addEventToList(obj, eventName, callback);
				
					//attach the event
					if (obj.addEventListener) {
						obj.addEventListener( eventName, callback, false);
					} else {
						if (obj.attachEvent) {
							obj.attachEvent('on'+eventName, callback);
						} else {
							obj['on'+eventName] = callback;
						}
					}
				
				}
			},
		
			/**
			* remove the event from the object to prevent memory leaks
			* @param obj HTMLElement element to bind event to
			* @param eventName string name of event to bind to
			* @param callback Function function to call when the event triggers
			* @param bindingElement Object object to bind the callback to so that this refers to the intended item
			*/
			removeEvent:function(obj, eventName, callback, bindingElement) {
			  
        if (typeof(bindingElement) !== "undefined") {
					callback = callback.bind(bindingElement);
				}
			
				if (eventExists(obj,eventName,callback)) {
          
					//remove it from the list
					removeEventFromList(obj,eventName,callback);
				
					//remove the event
					if (typeof(obj.removeEventListener) != "undefined") {
						obj.removeEventListener( eventName, callback, false);
					} else {
						if (obj.detachEvent) {
							obj.detachEvent('on'+eventName, callback);
						} else {
							obj['on'+eventName] = null;
						}
					}
          
        }
        
			},
      
      /**
      * remove all of the events from the queue
      */
      removeAllEvents:function() {
        
        while (events.length) {
          this.removeEvent(events[0].obj, events[0].eventName, events[0].callback);
        }
        
      },
			
      /**
      * I think I wrote this by myself, is not very efficient but it works!
      */
	    getElementsByClassName:function(cName, nName, parent) {
				
        parent = parent || document.body;
        nName = nName || "*";
				
        var candidates = parent.getElementsByTagName(nName), 
            candidateClasses = [], 
            elms=[];

        for (var i=0,j=candidates.length;i<j;i++) {
          candidateClasses = candidates[i].className.split(" ");
          for (var m=candidateClasses.length;m--;) {
            if (candidateClasses[m] === cName) {
              elms[elms.length] = candidates[i];
            }
          }
        }

        return elms;				

      }

    };
	
  }();
  
  
  
  /**
  * manages all of my public feeds, currently Twitter and Flickr
  */
  var PublicFeeds = function() {
    
    var flickrJSON;
    var twitterJSON;
    
    /**
    * spit out my last 8 photos from Flickr
    */
    var doFlickrStuff = function() {
      
      var docFrag = document.createDocumentFragment();
      var photos = flickrJSON.items;
      var p, li, a;
      
      for (var i=0;i<8;i++) {
        
        p = photos[i];
        li = document.createElement('li'); 
        a = document.createElement('a');

        a.href = p.link;
        a.title = p.title;
        a.style.background = 'transparent url('+p.media.m.replace('_m','_s')+') 50% 50% no-repeat';
        
        li.appendChild(a);
        docFrag.appendChild(li);
          
      }
      
      document.getElementById('flickr-photos').appendChild(docFrag);
      
    };
    
    /**
    * parse the twitter feed and post my past 4 entries
    */
    var doTwitterStuff = function() {
      
      var docFrag = document.createDocumentFragment();
      var t, li, a, span;
      
      for (var i=0;i<4;i++) {
        
        t = twitterJSON[i];
        li = document.createElement('li'); 
        a = document.createElement('a');
        span = document.createElement('span');

        a.appendChild(document.createTextNode(t.text));
        a.href="http://www.twitter.com/erudianart/";
        
        span.appendChild(document.createTextNode(t.created_at));
        
        li.appendChild(a);
        li.appendChild(span);
        docFrag.appendChild(li);
          
      }
      
      document.getElementById('twitter-feed').appendChild(docFrag);
      
    };
    
    
    return {
      init:function() {

        var script;
        
        //only add the script / global variable if the element is on the page, this way
        //I have zero global variables for most pages!
        if ( !!document.getElementById('flickr-photos') ) {
          
          //global variable, but what can you do :(          
          window.jsonFlickrFeed = function(json) {
            flickrJSON = json;
            doFlickrStuff(); //thanks schill;
          };

          //add the script tag to the page dynamically, this way we don't always need a global var
          script = document.createElement('script');
          script.type = "text/javascript";
          script.src = "http://api.flickr.com/services/feeds/photos_public.gne?id=15975796@N00&lang=en-us&format=json";
          document.getElementsByTagName('head')[0].appendChild(script);
          
        }
     
        //only add the script / global variable if the element is on the page, this way
        //I have zero global variables for most pages!  
        if ( !!document.getElementById('twitter-feed') ) {
          
          //global variable, but what can you do :(          
          window.jsonTwitterFeed = function(json) {
            twitterJSON = json;
            doTwitterStuff(); //thanks schill;
          };

          //add the script tag to the page dynamically, this way we don't always need a global var
          script = document.createElement('script');
          script.type = "text/javascript";
          script.src = "http://twitter.com/statuses/user_timeline/erudianart.json?callback=jsonTwitterFeed";
          document.getElementsByTagName('head')[0].appendChild(script);
          
        }
       
      }
      
    };
    
  }();
  
  Util.addEvent(window,"load",PublicFeeds.init);

  
  
  /**
  * Interval based animation utility.  I think I should adopt the use of public variables and use the 
  * Constructor syntax as there should only be one instance of an animator, and private variables/blessed
  * methods are slower than public methods.
  */
	var Animator = function() {
	
		var queue = [], //queue of objects to animate
		    intervalID = null, //interval identifier
		    interval = 30; //wait 30ms to animate
	
		// a list of functions for the animators
		var animationFunctions = {
			"x":function(obj, tween, tweenIndex) {
				obj.style.left = tween[0][tweenIndex];
			},
			"y":function(obj, tween, tweenIndex) {
				obj.style.top = tween[0][tweenIndex];
			},
			"xy":function(obj, tween, tweenIndex) {
				obj.style.left = tween[0][tweenIndex];
				obj.style.top = tween[1][tweenIndex];
			},
			"w":function(obj, tween, tweenIndex) {
				obj.style.width = tween[0][tweenIndex];
			},
			"h":function(obj, tween, tweenIndex) {
				obj.style.height = tween[0][tweenIndex];
			},
			"wh":function(obj, tween, tweenIndex) {
				obj.style.width = tween[0][tweenIndex];
				obj.style.height = tween[1][tweenIndex];
			},
			"bg":function(obj, tween, tweenIndex) {
				//convert rgb values back to hex, and yes, colour, not color
				obj.style.backgroundColor = Util.convertRGBIntToHex(tween[0][tweenIndex], tween[1][tweenIndex], tween[2][tweenIndex]);
			}
		};
	
		/**
		* loops through all of the items in the queue and calls the animation function
		* for that item, passing to it the tweens and the current tween index
		*/
		var animate = function() {
		
			for (var i=0,j=queue.length;i<j;i++) {
			
				queue[i].animator( queue[i].o, queue[i].tween, queue[i].tweenIndex );
				queue[i].tweenIndex++;
			
				if (queue[i].tweenIndex == queue[i].tweenLength) {
					
					if (typeof(queue[i].onComplete) === "function") {
						queue[i].onComplete();
					}
					
					queue.splice(i, 1);
					i--;
					j--;
					
				}
			
			}
		
			if (!queue.length) {
				this.stop();
			}
		
		};
	
	
		/**
		* Use schiller's method of to determine at each frame, the percent of the difference
		* from start to end made by that frame.
		*/
		var createPercentTween = function(duration) {
		
			var percentTween = [];
		
			var numberOfFrames = Math.ceil(duration/interval);
			var pivot = Math.ceil(numberOfFrames/2);
			var cumsum = pivot*(pivot+1);
		
			if (pivot*2 != numberOfFrames) {
			    cumsum -= pivot;
		    }
		    
			for (var i=0;i<pivot;i++) {
				percentTween[i] = (i+1)/cumsum;
			}
		
			for (var j=pivot;j<numberOfFrames;j++) {
				i--;
				percentTween[j] = percentTween[i];
			}
		
			return percentTween;
		
		};
		
		/**
		* creates the tweens for the objects for the animation
		*/
		var createTween = function(obj) {
		
			//percent tween is the same regardless of differences
			var percentTween = createPercentTween(obj.duration);
		
			obj.tween = [];
			obj.tweenIndex = 0;
			obj.tweenLength = percentTween.length;
		
			var diff, last;
		
			//check to see if it is an array or not
			if (obj.start.length) {
			
				//multiple things to update for animation
				for (var i=0;i<obj.start.length;i++) {
				
					obj.tween[i] = [];
					diff = obj.end[i] - obj.start[i];
					last = obj.start[i];
				
					for (var m=0,n=percentTween.length;m<n;m++) {
						obj.tween[i][m] = last + percentTween[m]*diff;
						last = obj.tween[i][m];
            obj.tween[i][m] += obj.units[i];
					}
				
				}
			
			} else {
			
				//only one attribute to update for the object
				obj.tween[0] = [];
			
				diff = obj.end - obj.start;
				last = obj.start;
				for (var x=0,y=percentTween.length;x<y;x++) {
					obj.tween[0][x] = last + percentTween[x]*diff;
					last = obj.tween[0][x];
					obj.tween[0][x] += obj.units;
				}

			}
		
		};		
	
		return {
		
			/**
			* add the object to the queue of items to be animated
			*/
			queueItem:function(obj) {
			
				createTween(obj);
				obj.animator = animationFunctions[ obj.animationType ];
				queue[ queue.length ] = obj;
			
			},
		
			/**
			* start animation
			*/
			start:function() {
				if (queue.length > 0) {
					intervalID = window.setInterval(animate.bind(this), interval);
				}	
			},
		
			/**
			* stop the animation
			*/
			stop:function() {
				clearInterval(intervalID);
				intervalID = null;
			}
		
		};
	
	}();
	
  
  /**
  * Im reaching in and reaching out and reaching for the random or whatever
  * will bewilder me. Spiral Out. Keep going.
  *
  * Construct the golden rectangles / golden spiral. 
  */
	Util.addEvent(window,"load",function() {
	
		Animator.queueItem({
			o:document.getElementById('work'), duration:300,
			start:150, end:200, units:'px',
			animationType:"x",
			onComplete:function() {
				
				var research = document.getElementById('research');
				research.style.visibility = 'visible';
				
				Animator.queueItem({
					o:research, duration:300,
					start:-50, end:50, units:'px',
					animationType:'y',
					onComplete:function() {
					
						var thoughts = document.getElementById('thoughts');
						thoughts.style.visibility = 'visible';
						
						Animator.queueItem({
							o:thoughts, duration:300,
							start:150, end:0, units:'px',
							animationType:'x'							
						});
					
					}
					
				});
	
			}
				
		});
    
    //fade the background in while the animation is occuring
    if (document.getElementsByTagName('body')[0].className !== "") {
      var colourHash = {"thoughts":"#ffef09","research":"#ff9e00","work":"#00dcff","about":"#81e412"};
      Animator.queueItem({
        o:Util.getElementsByClassName('header','div')[0],
        duration:900,
        start:Util.convertRGBHexToInt('#313131'),
        end:Util.convertRGBHexToInt(colourHash[document.body.className]),
        units:['','',''],
        animationType:'bg'
      });								
    }
		
		Animator.start();
		
	});
    
  //clean up
  Util.addEvent(window,"unload",Util.removeAllEvents, Util);

	
})();
