var $3n = {
	delicious_tags : params()['delicious_tags'] || 'humor-awesome',
	user_names     : {} // todo
}

get_user_names()

var Cell = new Class({
	Implements: Options,
	options: {
		main_class    : 'single-wide',
		custom_class	: '',
		title 				: '',
		created_on		: new Date(1985,5,31),
		source				: '#'
	},
	initialize: function(html, options){
		this.setOptions(options)
		this.html = html
		
		this.update_element()
		
		this.setup_tip()
		this.add_events()
		
		return this
	},
	
	create_element: function(){
		this.element = this.element || new Element('div')
		this.element.addClass('cell')
								.addClass(this.options.main_class)
								.addClass(this.options.custom_class)
								.addClass((coin_toss(0.1) ? 'inverted' : ''))
		
		if 			($type(this.html) === 'element') this.element.adopt(this.html)
		else if ($type(this.html) === 'string')  this.element.set('html', this.html)
		
		if (!this.element.getElement('.new-icon'))
			this.element.adopt(new Element('div', {
				'class' : 'new-icon', 
				'title' : "I'm new, to you.",
				'html'  : "new"
			}))

		this.element.store('source',  this.options.source)
		this.element.store('title',   this.options.title)
		this.element.store('created', this.options.created_on.timeDiffInWords())

		return this.element
	},
	
	update_element: function(){
		this.element = this.create_element()
		return this
	},
	
	setup_tip: function(){
	  Cell.tip = Cell.tip || new JustTheTip(null, {
    	showDelay  : 400,
    	x_location : 'left',
    	y_location : 'bottom',
    	fade_in_duration  : 100,
    	fade_out_duration : 200
    }).addEvent('tipShown', function(tip,elem){
        tip.set('html', "<a class='title' href='" + elem.retrieve('source') + "'>" + elem.retrieve('title') + "</a><span class='date'>" + elem.retrieve('created') + "</span>")
      })
	},
	
	add_events: function(){
		if (this.options.source.length > 4) this.element.act_like_link(this.options.source)
		Cell.tip.add_element(this.element)
	},
	
	to_html: function(){
		return this.element
	}
})
Cell.tip = null

var ImageCell = new Class({
	Extends: Cell,
	initialize: function(src, options){
		this.setOptions(options)
		
		var elem = new Element('img', {
			'src' 	: src,
			'styles': { 'display':'none'	}
		})
		
		return this.parent(elem, options)
	},
	
	to_html: function(){
		this.html.on_has_width(function(){ 
			this.setStyle('display','block').thumbnail.delay(1, this, [140,140])
		}.bind(this.html))
		
		return this.parent()
	}
})


var Model = new Class({
	Implements: Events,
	initialize: function(){
		this.db = []
		this.data_ready = false
		return this
	},
	
	get_data: function(){
		if (this.data_ready)
			this.fireEvent('dataReady', this)
		else
			new JsonP(
				this.json_url, 
				$merge(	{abortAfter : 1000, retries : 1, onComplete : this.process_data.bind(this) }, this.json_opts) 
			).request()
			
		return this
	},
	
	process_data: function(custom_name){
		this.db = this.db.map(function(row){
			row.model = this
			return row
		}, this)
		
		this.data_ready = true
		this.fireEvent('dataReady', this)
		$3n.grid_latest.set( custom_name || this.site_name, this.db[0].created_on.toString())
	},
	
	sort_by: function(field){
		return this._sort_by.cache(this)(field)
	},
	_sort_by: function(field){
		return this.db.sort(function(a,b){
			a[field] - b[field]
		})
	},
	new_items: function(){
		return this.db.filter(function(x){
			return x.is_new
		})
	},
	
	to_cells: function(limit){
		var limit = limit || 100
		this.cells = [this.title_elem].combine(this.db.map(function(row){ 
			if (limit > 1) {
				var cell = this._to_cell.apply(row)
				cell.element.hasClass('double-wide') ? limit -= 2 : --limit
				return cell.to_html()
			}
		}.bind(this))).flatten()
		
		return this.cells
		// return [this.title_elem].combine(this.db.map(function(row){ return this._to_cell.apply(row).to_html() }.bind(this))).first(limit||100)
	},
	
	current_user: function(){
		return current_user(this.site_name)
	}
})

var Flickr = new Class({
	Extends: Model,
	
	site_name  : "flickr",
	nombre     : "SEEING", 
	json_url   : "http://api.flickr.com/services/feeds/photos_public.gne",
	web_source : "http://www.flickr.com/photos/" + current_user('flickr'),
	json_opts  : { globalFunction : 'jsonFlickrFeed',
								 data: { id     : $3n.user_names.flickr_id,
												 lang   : "en-us",
									       format : 'json' } },
 	initial_limit : 15,									
	
	initialize: function(){
		if (this.json_opts.data.id === '') this.json_url = null
		return this.parent()
	},
	
	process_data: function(json){
		this.db = json.items.map(function(json_item){
			return {
				title       : json_item.title,
				created_on  : Date.parse(json_item.date_taken),
				img_url     : json_item.media.m,
				source      : json_item.link,
				description : json_item.description,
				tags        : json_item.tags,
				is_new      : Date.parse(json_item.date_taken) > Date.parse($3n.grid_latest.get(this.site_name))
			}
	  }.bind(this))

		this.parent()
		return this.db
	},
	
	_to_cell: function(){
		return new ImageCell(this.img_url, { 
			'title' 		   : this.title, 
			'created_on'   : this.created_on,
			'source' 		   : this.source,
			'custom_class' : (this.is_new ? 'new' : '')
		})
	}
})

var Twitter = new Class({
	Extends: Model,
	
	site_name  : "twitter",
	nombre     : "SAYING",
	json_url   : "http://search.twitter.com/search.json",
	web_source : "http://www.twitter.com/" + current_user('twitter'),
	json_opts  : { data: { q : "from:" + current_user('twitter') } },
 	initial_limit : 15,	
  
  initialize: function(){
		return this.parent()
  },

	process_data: function(json){
		this.db = json.results.map(function(json_item){
			return {
				title       : json_item.text,
				created_on  : Date.parse(json_item.created_at),
				source      : "http://www.twitter.com/" + json_item.from_user + "/status/" + json_item.id,
				html        : json_item.text.make_urls_links().link_replies().link_hashcodes(),
				is_new      : Date.parse(json_item.created_at) > Date.parse($3n.grid_latest.get(this.site_name))
			}
	  }.bind(this))
	
		this.parent()
		return this.db
	},					
	
	_to_cell: function(){
		return new Cell(this.html, { 
			'main_class'	 : (this.title.length > 90) ? 'double-wide' : 'single-wide',
			'custom_class' : 'text tweet ' + (this.is_new ? 'new' : ''),
			'created_on'	 : this.created_on,
			'source'			 : this.source
		})
	}	
})

var LastFM = new Class({
	Extends: Model,
	
	site_name  : "lastfm",
	nombre     : "HEARING",
	web_source : "http://www.last.fm/user/" + current_user('lastfm'),
	json_url   : "http://lastfm-api-ext.appspot.com/2.0/",
	json_opts  : { data : { method  : 'user.getRecentTracks',
	 												user    : current_user('lastfm'),
													api_key : 'b25b959554ed76058ac220b7b2e0a026',
													limit   : 100,
													outtype : 'js' } },
	// json_url   : "http://www.dapper.net/transform.php",
	// json_opts  : { data : { dappName    : '3NsRecentlyPlayedTracks',
	//  												transformer : 'JSON',
	// 												applyToUrl  : 'http://ws.audioscrobbler.com/2.0/user/3n/recenttracks.rss?limit=100',
	// 												extraArg_callbackFunctionWrapper : 'shitPoop' },
	// 							 globalFunction : 'shitPoop' },
	
 	initial_limit : 10,													
													
  initialize: function(){
		return this.parent()
  },

	process_data: function(json){
		// this.db = json.groups.RSS_Item.map(function(json_item){
		// 	return {
		// 		track       : json_item.Title[0].value.match(/\– (.+)/)[1],
		// 		track_url   : json_item.Title[0].href,
		// 		artist      : json_item.Title[0].value.match(/(.+)\s–/)[1],
		// 		html        : "<span class='artist'>" + json_item.Title[0].value.match(/(.+)\s–/)[1] + "</span> <a class='track' href='" + json_item.Title[0].href + "'>" + json_item.Title[0].value.match(/\– (.+)/)[1] + "</a>",
		// 		created_on  : Date.parse(json_item.Publication_Date[0].value).decrement('hour',8),
		// 		is_new      : Date.parse(json_item.Publication_Date[0].value).decrement('hour',8) > Date.parse($3n.grid_latest.get(this.site_name))
		// 	}
		// 	  }.bind(this))
		this.db = json.recenttracks.map(function(json_item){
			return {
				track       : json_item.name,
				track_url   : json_item.url,
				artist      : json_item.artist.name,
				html        : "<span class='artist'>" + json_item.artist.name + "</span> <a class='track' href='" + json_item.url + "'>" + json_item.name + "</a>",
				created_on  : Date.parse(json_item.date.text).decrement('hour',8),
				is_new      : Date.parse(json_item.date.text).decrement('hour',8) > Date.parse($3n.grid_latest.get(this.site_name))
			}
			  }.bind(this))

		this.parent()
		return this.db
	},					

	_to_cell: function(){
		return new Cell(this.html, { 
			'custom_class' : 'text lastfm-song ' + (this.is_new ? 'new' : ''),
			'created_on'	 : this.created_on
		})
	},
	
	to_cells: function(limit){	
		var tmp = []
		var limit = limit || 100
		
		for (var i=0; i < this.db.length; i++){
			if (i > 0 && this.db[i-1].artist === this.db[i].artist ){
				if (prev_cell) {
					prev_cell.html += ", <a class='track' href='" + this.db[i].track_url + "'>" + this.db[i].track + "</a>"
					prev_cell.options.main_class = 'double-wide'
					prev_cell.update_element()
				}
			} else {
				var prev_cell = this._to_cell.apply(this.db[i])
				tmp.push(prev_cell)
			}
		}
		
		// return [this.title_elem].combine(tmp.map(function(t){ return t.to_html() })).first(limit||100)
		this.cells = [this.title_elem].combine(tmp.map(function(cell){ 
			cell.element.hasClass('double-wide') ? limit -= 2 : --limit
			if (limit > 0) return cell.to_html()
		}.bind(this)).flatten())
		
		return this.cells
	}

})

var Delicious = new Class({
	Extends: Model,
	
	site_name  : "delicious",
	jsonp_opts : {data: { count: 20 }},
	initial_limit : 10,														
  
  initialize: function(tag){
		this.tag = tag
		this.nombre = tag.toUpperCase()
		this.json_url = "http://feeds.delicious.com/v2/json/" + this.current_user() + "/" + this.tag
		this.web_source = "http://www.delicious.com/" + this.current_user() + "/" + this.tag
		return this.parent()
  },

	process_data: function(json){
		if (json.length == 0) return
		this.db = json.map(function(json_item){
			return {
				href        : json_item.u,
				created_on  : Date.parse(json_item.dt),
				text        : json_item.d,
				html        : "<a href='" + json_item.u + "'>" + json_item.d + "</a>",
				is_new      : Date.parse(json_item.dt) > Date.parse($3n.grid_latest.get(this.site_name + '-' + this.tag))
			}
	  }.bind(this))
	
		this.parent(this.site_name + '-' + this.tag)
		
		return this.db
	},					
	
	_to_cell: function(){
		if (this.href.test(/png|gif|jpg|jpeg|bmp|svg/i)) { // todo put in brawndo
		  return new ImageCell(this.href, {
		    'title'        : this.text,
				'created_on'	 : this.created_on,
				'source'			 : this.href,
				'custom_class' : (this.is_new ? 'new' : '')
			})
		} else {
		 	return new Cell(this.html, {
				'main_class'	 : (this.text.length > 90) ? 'double-wide' : 'single-wide',
				'created_on'	 : this.created_on,
				'source'			 : this.href,
				'custom_class' : (this.is_new ? 'new' : '')				
			}) 
		}
	}	
})

var GitHub = new Class({
	Extends: Model,
	
	site_name  : "github",
	nombre     : "CODING",
	web_source : "http://www.github.com/" + current_user('github'),
	json_url   : "http://github.com/" + current_user('github') + ".json",
	initial_limit : 10,

	initialize: function(){
		return this.parent()
	},
	
	process_data: function(json){
		this.db = json.map(function(json_item){
			return {
				html        : this._gen_html(json_item),
				source      : this._gen_source(json_item),
				created_on  : Date.parse(json_item.created_at),
				is_new      : Date.parse(json_item.created_at) > Date.parse($3n.grid_latest.get(this.site_name))
			}
		}.bind(this))
		
		this.db = this.db.filter(function(x){ return x.html })

		this.parent()
		return this.db
	},
	
	_gen_html: function(json_item){
		if (!json_item.repository) return
		switch(json_item.type) {
			case 'CommitEvent' :
				return json_item.actor + " commited to " + "<a href='" + json_item.repository.url + "'>" + json_item.repository.name + "</a>"; break;
			default : return; break;			
		}
	},
	
	_gen_source: function(json_item){
		if (!json_item.repository) return
		switch(json_item.type) {
			case 'CommitEvent' :
				return "http://github.com/" + this.current_user() + "/" + json_item.repository.name + "/commit/" + json_item.payload.commit; break;
			default : return; break;			
		}
	},
	
	_to_cell: function(){
		return new Cell(this.html, { 
			'main_class'	 : 'single-wide',
			'custom_class' : 'text ' + (this.is_new ? 'new' : ''),
			'created_on'	 : this.created_on,
			'source'			 : this.source
		})
	}
})

var YouTube = new Class({
  Extends: Model,
  
  site_name  : "youtube",
  nombre     : "WATCHING",
  json_url   : "http://gdata.youtube.com/feeds/api/users/" + current_user('youtube') + "/favorites",
  web_source : "http://www.youtube.com/user/" + current_user('youtube'),
  json_opts  : { data : { 'v'           : 2,
                          'alt'         : 'json-in-script',
                          'max-results' : 15 } },
  initial_limit : 10,
  
  initialize: function(){
    return this.parent()
  },
  
  process_data: function(json){
    this.db = json.feed.entry.map(function(json_item){
      return {
        href        : this._gen_href(json_item),
        created_on  : Date.parse(json_item.published.$t),
        text        : json_item.title.$t,
        html        : "<a href='" + this._gen_href(json_item) + "'>" + json_item.title.$t + "</a>",
        is_new      : Date.parse(json_item.published.$t) > Date.parse($3n.grid_latest.get(this.site_name))
      }
    }.bind(this))
    
    this.db = this.db.filter(function(x){ return x.html })
 
    this.parent()
    return this.db
  },
  
  _gen_href: function(json_item){
    return json_item.link.filter(function(link){
      return link.rel == 'alternate'
    }).first().href;
  },
  
  _to_cell: function(){
    return new Cell(this.html, {
      'main_class'   : (this.text.length > 90) ? 'double-wide' : 'single-wide',
      'created_on'   : this.created_on,
      'source'       : this.href,
      'custom_class' : 'text ' + (this.is_new ? 'new' : '')
    })
  }
})

// both methods in here should be renamed to represent their sort order
var Grid = new Class({
	Implements : Events,
	initialize: function(elem, buckets){
		this.element = $(elem)
		this.buckets = buckets
		
		this.nav = new FixedNav(new Element('ul', {'id':'grid-nav'}).inject(this.element, 'before'), this.element)
		return this
	},
	
	to_html: function(format){
		format = format || 'grouped'
		this.element.set('html','')
		
		this.buckets.each(function(b,i){
			b.each(function(m){
				m.injected = false
				m.bucket = i
				m.removeEvents('dataReady').addEvent('dataReady', this["_"+format].bind(this))
				m.get_data()
			}, this)
		}, this)
	},
	
	_grouped: function(model){	
		if (model.db.length === 0) return
		var finished_models = this.buckets.flatten().filter(function(m){ return m.injected })
		
		this.nav.element.simple_show()
						
		model.nav = model.nav || new Element('li', {
			'class' : model.site_name + (model.new_items().length > 0 ? ' opaque' : ''),
			'html'  : model.nombre + ' ' + (model.new_items().length || '')
		}).addEvent('click', function(){ this.removeClass('opaque') })
		
		model.title_elem = model.title_elem || new Element('div', {
			'class' : 'cell single-wide grid-title ' + model.site_name, 
			'html'  : model.nombre
		}).act_like_link(model.web_source)
			.adopt( new Element('span', {'class':'show-all','html':'SHOW ' + model.db.length}).addEvent('click', this.model_toggle_all.bind(model)) )
			
		model.db.first(10).each(function(row,i){
			var text = $pick(row.title, row.text, row.html, '')
			var the_match = text.match(/\b([A-Z]\w+)(\s[A-Z]\w+){0,5}/)
			if (the_match && !['the','youtube'].contains(the_match[0].toLowerCase())) 
				$('fun-zone').adopt( 
					new Element('span', {'class':'word','html':the_match[0]})
						.addEvent('click', function(){ model.cells[i+1].scroll_to(10,true) })
				)
		})

		if (finished_models.length > 0){
			finished_models.each(function(fm){
				if (model.bucket < fm.bucket || (fm.sort_by('created_on').first().created_on < model.sort_by('created_on').first().created_on && fm.bucket >= model.bucket)){
					if (!model.injected){
						model.to_cells(model.initial_limit).each(function(cell){ cell.inject(fm.title_elem,'before') }, this)
						this.nav.add_pair( [model.nav.inject(fm.nav, 'before'), model.title_elem] )
					}						
					model.injected = true
				}
			}, this)
		}
		
		if (!model.injected) {
			this.element.adopt( model.to_cells(model.initial_limit) )
			this.nav.add_pair( [model.nav.inject(this.nav.element, 'bottom'), model.title_elem] )
			model.injected = true
		}
		
		if (finished_models.length == this.buckets.flatten().length - 1){
			this.fireEvent('shitsDoneScro')
			this.nav.handle_hash_scroll()
		}			
	},
	_sorted: function(model){
		if (model.db.length === 0) return
		var finished_models = this.buckets.flatten().filter(function(m){ return m.data_ready })
		this.sorted_cells = this.sorted_cells || []
	
		this.nav.element.simple_hide()
	
		this.sorted_cells = this.sorted_cells.include(model.db.first(20).map(function(x){ return x.model._to_cell.apply(x) })).flatten().sort(function(a,b){
			if      (a.options.created_on >  b.options.created_on) return 1
			else if (a.options.created_on <= b.options.created_on) return -1
			else return 0
		})
		
		if (finished_models.length === this.buckets.flatten().length)
			this.element.adopt(this.sorted_cells.reverse().map(function(x){ return x.to_html() }))
	},
	
	model_toggle_all: function(e){
		e.stop()
		this.cells.filter(function(c){ return !c.hasClass('grid-title') }.bind(this)).each(function(cell){ cell.destroy() })
		this.title_elem.getFirst('span').set('html', this.cells.length > this.initial_limit ? 'SHOW ' + this.db.length : 'SHOW ' + this.initial_limit)
		this.to_cells(this.cells.length > this.initial_limit ? this.initial_limit : null).reverse().each(function(cell){ cell.inject(this.title_elem,'after') }, this)
	}
})

var FixedNav = new Class({
	initialize: function(elem, bff, pairs){
		this.element = elem
		this.bff     = bff
		this.pairs   = pairs || []
		
		this.set_styles()
		this.pairs.each(this.add_pair.bind(this))
		
		this.element.addEvent('click', function(e){
			e.stopPropagation()
			document.location.hash = '#'
		}.bind(this))
	
		window.addEvent('resize', this.set_styles.bind(this))
		this.set_styles.delay(1000, this)
	}, 
	
	set_styles: function(){
		this.element.setStyles({
			'position' : 'fixed',
			'top'      : this.bff.getTop(),
			'left'     : this.bff.getLeft() + this.bff.getWidth()
		})
	},
	
	add_pair: function(pair){
		var nav_elem = pair[0]
		var bff_elem = pair[1]
		
		this.pairs.include(pair)
		
		nav_elem.addEvent('click', function(e){
			e.stopPropagation()
			hash_clear_off()
			bff_elem.scroll_to(79, null, hash_clear_on.create({delay:10}))
			document.location.hash = nav_elem.get('html').match(/^(\w)+/)[0].clean().toLowerCase()
		})
	},
	
	handle_hash_scroll: function(){
		this.pairs.each(function(pair){
			if (pair[0].get('html').match(/^(\w)+/)[0].clean().toLowerCase() === document.location.hash.slice(1))
				pair[1].scroll_to(79, null, hash_clear_on.create({delay:10}))
		})
	}
})

function current_user(site){
	return $3n.user_names[site + '_user'] || $3n.user_names.global_user
}

function get_user_names(){
	[['global_user','bjeanes'],['twitter_user','bjeanes'],['flickr_id',null],['flickr_user','bjeanes'],['delicious_user','bjeanes'],['lastfm_user','bjeanes'],['github_user','bjeanes']].each(function(u){
		var passed_in = params()[u[0]]
		$3n.user_names[u[0]] = (($3n.user_names.global_user && !passed_in) || passed_in === '') ? '' : (passed_in || u[1])
	})
}

function goog(){    
  var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
  new Element('script', {'src':gaJsHost + 'google-analytics.com/ga.js', 'type':'text/javascript'}).inject(document.body, 'bottom')
  try {
    var pageTracker = _gat._getTracker("UA-3747936-4");
    pageTracker._trackPageview();
  } catch(err) {}
}

function hash_clear_on(){
	$3n.hash_clear = $3n.hash_clear || function(){ if (document.location.hash !== "#/") document.location.hash = "/" }
	window.addEvent('scroll', $3n.hash_clear)
}
function hash_clear_off(){
	$3n.hash_clear = $3n.hash_clear || function(){ if (document.location.hash !== "#/") document.location.hash = "/" }
	window.removeEvent('scroll', $3n.hash_clear)
}

function they_spinnin(){
	$('main').addEvent('click', function(e){ 
		if (e.target.id !== 'main') return;
		this.rotate(this.get_transform_int() + 180,{
			duration   : 3000,
			transition : 'ease-in-out'
		}); 
	}) 
	$('title').addEvent('click', function(){ 
		this.rotate(1440,{
			duration   : 2000,
			onComplete : this.rotate.bind(this, [0.01,{duration:0.01}]),
			transition : 'cubic-bezier(0.3,0.1,0.1,1)'
		}) 
	})
	window.addEvent('keyup', function(e){
		if (e.key == 'g'){
			var sex = new Element('div', {'styles':{'float':'left','border':'10px solid red'}}).adopt(
				new Element('iframe', {'src':'http://www.google.com', 'width':'500px', 'height':'500px'})
			).inject(document.body,'top')	
			sex.addEvent('click', sex.rotate.bind(sex, [360,{
				duration   : 2000, 
				onComplete : sex.rotate.bind(sex,[0.01,{duration:0.01}])
			}]))
		}else
		if (e.key == 'b'){
			$$('body').rotate(1440,{
				duration   : 2000, 
				onComplete : $$('body').rotate.bind($$('body'), [0.01,{duration:0.01}]),
				transition : 'cubic-bezier(0.3,0.1,0,1)'
			})
		}
	})
}

window.addEvent('domready', function(){

  $(document.body)
		.set('html', '')
		.addClass('loading')
		
	if ($3n.user_names.global_user) {
		new Element('p', {'class':'shits_custom', html:"This site has been customized for " + $3n.user_names.global_user + "."}).inject($('footer'),'before')
		$('footer').addClass('custom')
	} 
		
	$('fun-zone').set('html', '<span class="title">in</span><span class="title">summary</span>')	

	if (navigator.userAgent.match('iPhone')) document.body.addClass('iphone');
	
	if (!$defined(Cookie.read('grid_latest_'+$3n.user_names.global_user))) $(document.body).addClass('all-new')
	$3n.grid_latest = new Hash.Cookie('grid_latest_'+$3n.user_names.global_user, {duration:100, path: '/'})
	
	$3n.the_grid = new Grid('main', [
		[ new Flickr, 
		  new Twitter ],
		[ new LastFM  ],
		$3n.delicious_tags.split('-').map(function(tag){ return new Delicious(tag) }),
		[ new GitHub ]
	]).addEvent('shitsDoneScro', function(){ $(document.body).removeClass('loading') })
		
	$3n.the_grid.to_html()
	
	new BrawndoButton($("sort-group"), {text_states:['SORT','GROUP']})
		.addEvent('onState0', $3n.the_grid.to_html.bind($3n.the_grid))
		.addEvent('onState1', $3n.the_grid.to_html.bind($3n.the_grid, 'sorted'))
	
	if ( Browser.Engine.webkit ) they_spinnin()
  if ( !document.location.href.match(/~ian/) ) goog()
	
})