/**
 * Create a cloud view of the twitters
 * @author gabe@fijiwebdesign.com
 * @copyright www.fijiwebdesign.com
 */

/**
 * Data store for keywords and densities
 */
twit.cloud = {
	/**
	 * Keywords list [0: kewyord, ... n: keyword]
	 */
	keywords: [],
	/**
	 * Densities of kewyords {keyword:density, ....}
	 */
	densities: {},
	/**
	 * Total number of keywords to keep in list 
	 */
	max_keywords: 5000,
	/**
	 * Total number of keywords to keep in keyword pool (candidates)
	 */
	max_keyword_pool: 500,
	/**
	 * Excluded keywords, case insensitive
	 */
	stopwords: 'the and for was now are this how has have that had\
	too but can get from what you out got not all with one lot some\
	just here don didn http'.split(/\s+/),
	/**
	 * Keyword inclusive match
	 */
	msg_regex: /[a-z0-9][a-z0-9-\.]+[a-z0-9]/ig
};
/**
 * parses a message for keywords
 * @param {String} msg
 */
twit.cloud.parseMessage = function(msg) {
	var msgs[];
	// match this.msg_regex on msg
	msgs = msg.match(this.msg_regex) || [];
	// convert all messages to lower case
	msgs.forEach(function(msg, i) {
	  msgs[i] = msg.toLowerCase();
	})
	// remove excluded keywords
	msgs.forEach(function(msg, i) {
		if (this.stopwords.contains(msg.toLowerCase()) || umsgs.contains(msg.toLowerCase())) {
			msgs.splice(i, 1);
		}
	}.bind(this));
	return msgs;
};
/**
 * Adds an Array of keywords to keywords list, or increments their density if they exist
 * @param {Array} words
 */
twit.cloud.addKeywords = function(words) {
	if (!words) return;
	words.foreach(function(word) {
		if (!this.hasKeyword(word))
			this.addKeyword(word);
		else
			this.addDensity(word);
	}.bind(this));
};
/**
 * Adds a single keyword to the keywords list
 * @param {String} word
 */
twit.cloud.addKeyword = function(word) {
	this.keywords.push(word);
	this.densities[word] = 0;
};
/**
 * Checks the keywords list for the existence of a keyword
 * @param {String} word
 */
twit.cloud.hasKeyword = function(word) {
	return this.keywords.contains(word);
};
/**
 * Returns the density of a keyword
 * @param {String} word
 */
twit.cloud.getDensity = function(word) {
	return this.densities[word];
};
/**
 * Increments the density of a keyword
 * @param {String} word
 */
twit.cloud.addDensity = function(word) {
	return this.densities[word]++;
};
/**
 * Retrieves all the keywords and their densities ordered by density descending
 * @param {Int} Limit of keywords to return
 * @return {Array} [0:keyword, ..., n:keyword]
 */
twit.cloud.sortKeywordsByDensityDesc = function(limit) {
	this.keywords.sort(function(a, b) {
		return this.getDensity(b) - this.getDensity(a);
	}.bind(this));
	if (limit) {
		return this.keywords.slice(0, limit);
	}
	return this.keywords;
};
/**
 * Removes a keyword from the keyword list
 * @param {String} word
 */
twit.cloud.removeKeyword = function(word) {
	for(var i = 0; i < this.keywords.length; i++) {
		if (word == this.keywords[i]) {
			this.keywords.splice(i, 1);
		}
	}
};
/**
 * Trim keyword list. 
 * We keep a keyword list, and a pool of new keywords to add to the list
 * When the pool is full, we empty it (hypothetically)
 */
twit.cloud.trimKeywords = function() {
	this.sortKeywordsByDensityDesc();
	if (this.keywords.length > this.max_keywords + this.max_keyword_pool) {
		this.keywords = this.keywords.slice(0, this.max_keywords);
	}
};

/**
 * Cloud UI
 */
twit.cloud.ui = {
	/**
	 * Keyword added to UI
	 */
	keywords: {},
	/**
	 * Max keywords to display in UI
	 */
	max_keywords: 50,
	/**
	 * Animation speed of keywords appearing on UI
	 */
	add_speed: 2000,
	/**
	 * Animation speed of keywords removed from UI
	 */
	remove_speed: 2000,
	/**
	 * Animation speed of keywords density change in UI
	 */
	density_speed: 2000
};
/**
 * Add a Keyword to the Cloud UI
 * @param {String} word
 */
twit.cloud.ui.addKeyword = function(q) {
	var id = 'keyword_'+twit.queries.attrFormat(q);
	var newq = new Element('a', {
		href: '#',
		id: id,
		'class': 'word',
		q: q,
		styles: {
			opacity: 0
		},
		events: {
			'click': function(evt) {
				twit.setQ(q);
				alert(twit.cloud.getDensity(q), 2);
				var evt = new Event(evt);
				evt.stop();
			}
		}
	}).setText(q);
	newq.setStyle(this.density_style, this.density_min+this.density_unit);
	$('cloud').appendChild(newq);
	this.keywords[q] = $(id);
	new Fx.Tween(this.keywords[q], 'opacity', {
		transition: Fx.Transitions.Quad.easeInOut, 
		duration:this.add_speed,
		fps: 20
	}).start(0, 1);
	
	
};
/**
 * Remove a Keyword from the cloud UI
 * @param {String} word
 */
twit.cloud.ui.removeKeyword = function(word) {
	if (this.keywords[word] && this.keywords[word].parentNode) {
		new Fx.Tween(this.keywords[word], 'opacity', {
			transition: Fx.Transitions.Quad.easeInOut, 
			duration:this.remove_speed,
			onComplete: function() {
				this.keywords[word].remove();
			}.bind(this),
			fps: 20
		}).start(1, 0);
	}
		
};
/**
 * Update the Density of a keyword on the UI
 * @param {Sting} word
 * @param {Int} dmax Highest Density in keyowrd list
 */
twit.cloud.ui.density_min = 0.5;
twit.cloud.ui.density_max = 1.3;
twit.cloud.ui.density_unit = 'em';
twit.cloud.ui.density_style = 'font-size';
twit.cloud.ui.density_animate = false;
twit.cloud.ui.setDensity = function(word, dmax) {
	var step = (this.density_max-this.density_min)/dmax;
	var style_curr = parseFloat(this.keywords[word].getStyle(this.density_style)) || this.density_min;
	var style_to = twit.cloud.getDensity(word)*step+this.density_min;
	if (this.density_animate) {
		new Fx.Tween(this.keywords[word], this.density_style, {
			transition: Fx.Transitions.Quad.easeInOut, 
			duration:this.density_speed,
			unit: this.density_unit,
			fps: 20
		}).start(style_curr, style_to);
	} else {
		this.keywords[word].setStyle(this.density_style, style_to+this.density_unit);
	}
	
};
/**
 * Redraw the keywords in the clouds UI. Dense keywords are added and less dense removed.
 * Limit of keywords is controlled by twit.cloud.ui.max_keywords value.
 */
twit.cloud.ui.densitize = function() {
	//try {
		
	// get keword list of length this.max_keywords in order of density
	var keywords = twit.cloud.sortKeywordsByDensityDesc(this.max_keywords);
	
	var dmin = twit.cloud.getDensity(keywords.getLast());
	var dmax = twit.cloud.getDensity(keywords[0]);
	var step = 100/dmax;
	
	// add keywords in density list
	keywords.forEach(function(word, i) {
		if (!this.keywords[word]) {
			this.addKeyword(word);
			//alert(i+' ui.addKeyword:'+word, 1);
		}
		this.setDensity(word, dmax);
		//alert(i+' ui.setDensity:'+word, 1);
	}.bind(this));
	
	// remove keywords in UI that are NOT in density list
	var i = 0;
	for(var x in this.keywords) {
		if (this.keywords.hasOwnProperty(x)) {
			if (!keywords.contains(x)) {
				this.removeKeyword(x);
				//alert(i+' ui.removeKeyword:'+x, 1);
			}
		}
		i++;
	}
	
	//alert('Cloud Childs len:'+$('cloud').childNodes.length, 2);
	//alert('Keyword List len:'+twit.cloud.keywords.length, 2);

	//} catch(e) { alert(e, 1)}
};

/**
 * work
 */
twit.cloud.init = function() {
	
	// add on observer to be triggered when all msgs received and written
	twit.events.afterWriteMsgs.addObserver(function(results) {
		// add densities and keywords
		results.forEach(function(msg) {
			var words = twit.cloud.parseMessage(msg.text);
			words.forEach(function(word) {
				if (!twit.cloud.hasKeyword(word)) {
					twit.cloud.addKeyword(word);
					//twit.cloud.ui.addKeyword(word);
					//alert('add word:'+word, 1);
				}
				twit.cloud.addDensity(word);
			});
		});
		
		// trim keywords
		twit.cloud.trimKeywords();
		
		// display densities
		twit.cloud.ui.densitize();
	});
		
};
window.addEvent('load', twit.cloud.init);

