var $ = function(id) {
	return document.getElementById(id);
};
var Twit = function(){};
Twit.MyTwit = function(){};
Twit.Log = [];
Twit.pins = [];
Twit.api = {
	 timeline:'http://twitter.com/statuses/friends_timeline.json'
	,friend  :'http://twitter.com/statuses/friends.json'
	,user    :'http://twitter.com/statuses/user_timeline.json'
};
Twit.filters = [];
Twit.apply_filters = function(page) {
	Twit.filters.forEach(function(i) {
		if (typeof(i) == 'function') {
			i(page);
		}
	});
};
Twit.addFilter = function(f) {
	Twit.filters.push(f);
};
Twit.init = function(){
	window.scrollTo(0,0);
	var myTwit = new Twit.MyTwit();
	Twit.MyTwit.prototype.callback = function(myid,myname){
		Twit.Config.MyId = myid;
		Twit.Config.MyName = myname;
		Twit.Config.MyNameExp = new RegExp('\\W?'+myname+'\\W?','i');
		Twit.twitter = new Twit();
		Twit.twitter.initialize(myid);
		var count = $('count');
		Twit.twitter.timerTime= Twit.Config.time;
		(function(){
			Twit.TimelineTimer = arguments.callee;
			Twit.TimelineTimer.id = setTimeout(function(){
				count.innerHTML = Twit.twitter.timerTime-- || 'loading..';
				if (Twit.twitter.timerTime <= 0)
					Twit.twitter.load();
				Twit.TimelineTimer();
			},1000);
		})();
		Twit.TimerReset = function(){
			clearTimeout(Twit.TimelineTimer.id);
			Twit.twitter.timerTime = Twit.Config.time;
			Twit.TimelineTimer();
		};
		Twit.UpdateTimeline = function(){
			clearTimeout(Twit.TimelineTimer.id);
			Twit.twitter.load();
			Twit.TimelineTimer();
		}
		if (myname != session_name) {
			Twit.Cake.cookie('myname',myname);
			Twit.Cake.cookie('myid',myid);
			Twit.Session.cookie('session_name',myname);
		}
	}
	var session_name = Twit.Session.cookie('session_name');
	if (!session_name) {
		myTwit.load();
	} else {
		myTwit.reload(session_name);
	}
	$('viewlimit').innerHTML = Twit.Config.limit;
	Twit.utils.writeTimespan();
	Twit.utils.LDRizeCommand();
};
Twit.Config = {
	 time:90
	,MyId:''
	,MyName:''
	,MyNameExp:null
	,limit:100
	,urlExp:/(https?|ftp)(:\/\/[-_.!~*\'a-zA-Z0-9;\/?:\@&=+\$,%#]+)/g
	,userExp:/@(\w+)/g
};
Twit.data = {
	 entryLog:{}
	,userLog:{}
	,entryCount:0
	,userCount:0
};
Twit.utils = {
	 htmlBuild:function(text){
		return text.replace(Twit.Config.urlExp,function($0, $1){
			var url = $0;
			url = encodeURI(url);
			return '<a href="' + url + '" target="_blank">' + url +'</a>'
		}).replace(Twit.Config.userExp,function($0,$1){
			return '<a href="http://twitter.com/' + $1 + '" target="_blank">' + $0 +'</a>'
		});
	}
	,dateBuild:function(date){
		return [date.getFullYear(),'/', date.getMonth()+1, '/', date.getDate(), '&nbsp;', date.getHours(), ':', date.getMinutes(), '.', date.getSeconds()].join('');
	}
	,writeTimespan:function(){
		$('timespan').innerHTML = Twit.Config.time;
	}
	,LDRizeCommand:function(){
		//var Minibuffer,LDRize;
		//console.log(window.Minibuffer,window.LDRize);
		if (window.Minibuffer) {
			(function(retry){
				if (window.Minibuffer && window.LDRize) {
					Twit.utils.CommandRegister(window.Minibuffer,window.LDRize);
				} else if (!retry) {
					var f = arguments.callee;
					setTimeout(function(){
						f();
					},0,1);
				}
			})();
		} else {
			if (!window.Minibuffer && !window.LDRize) return;
			Twit.utils.CommandRegister(Minibuffer,LDRize);
		}
	}
};
Twit.utils.CommandRegister = function(Minibuffer,LDRize) {
	//console.log(Minibuffer,LDRize);
	Minibuffer.addShortcutkey({
		key: 'a',
		description: 'Open Links in updates',
		command: function(){
			Minibuffer.execute('pinned-or-current-node | TR2::links-in-updates | open | clear-pin');
		}
	});
	Minibuffer.addCommand({
		name: 'TR2::links-in-updates'
		,command: function(stdin) {
			var links = [];
			stdin.forEach(function(para,i) {
				links = links.concat($x('descendant::li[@class="entry-title entry-content"]/a',para));
			});
			return links.map(function(a){return a.href;});
		}
	});
	Minibuffer.addCommand({
		name: 'TR2::timespan'
		,command: function() {
			var time = prompt('更新間隔を指定してください(60以上)');
			if ( !(time = parseInt(time,10)) ) time = 90;
			time = Math.max(time,60);
			Twit.Config.time = time;
			Twit.Cake.cookie('loadTimespan',time);
			Twit.utils.writeTimespan();
		}
	});
	Minibuffer.addCommand({
		name: 'TR2::limit'
		,command: function() {
			var limit = prompt('一度に表示する発言数を指定してください(100)');
			if ( !(limit = parseInt(limit,10)) ) limit = 100;
			limit = Math.max(limit,20);
			Twit.Config.limit = limit;
			Twit.Cake.cookie('viewLimit',limit);
			$('viewlimit').innerHTML = Twit.Config.limit;
		}
	});
	Minibuffer.addCommand({
		 name: 'TR2::font-size'
		,command: function() {
			var font = prompt('フォントサイズを指定してください(60以上)');
			if ( !(font = parseInt(font,10)) ) font = 100;
			font = Math.max(font,60);
			Twit.Cake.cookie('fontSize',font);
			Twit.fontSet(font);
		}
	});
	Minibuffer.addCommand({
		name: 'TR2::width'
		,command: function() {
			var width = prompt('タイムラインの幅を指定してください(%指定)');
			if ( !(width = parseInt(width,10)) ) width = 70;
			width = Math.max(width,30);
			Twit.Cake.cookie('timelineWidth',width);
			Twit.setTimelineWidth(width);
		}
	});
	Minibuffer.addCommand({
		name: 'TR2::hatena-star-load'
		,command: function() {
			if (window.Hatena && window.Hatena.Star) Hatena.Star.EntryLoader.loadNewEntries($('timeline'));
		}
	});
	Minibuffer.addCommand({
		name: 'TR2::reset-timeline-timer'
		,command: function() {
			if (Twit.TimerReset) Twit.TimerReset();
		}
	});
	Minibuffer.addCommand({
		name: 'TR2::update-timeline'
		,command: function() {
			if (Twit.UpdateTimeline) Twit.UpdateTimeline();
		}
	});
	Twit.pageReset = function(){
		if (Twit.pins.length) Minibuffer.execute('unset-pin',Twit.pins);
		Minibuffer.execute('scrollto-top | LDRize::paragraph-re-collect');
		if (Twit.pins.length) Minibuffer.execute('set-pin', Twit.pins);
		Twit.pins = [];
	};
	var pager = function(go,d) {
		var n = parseInt(d,10);
		var tw = Twit.twitter;
		if (n) {
			tw.jump(n - 1,go);
		} else {
			if(go > 0) {
				tw.next();
			} else {
				tw.back();
			}
		}
	};
	Minibuffer.addCommand({
		 name: 'TR2::next'
		,command: function() {
			pager( 1,this.args.shift());
		}
	});
	Minibuffer.addCommand({
		name: 'TR2::previous'
		,command: function(){
			pager(-1,this.args.shift());
		}
	});
	Minibuffer.addShortcutkey({
		key: 'n',
		description: 'TR2::next page',
		command: function(){
			Minibuffer.execute('TR2::next');
		}
	});
	Minibuffer.addShortcutkey({
		key: 'N',
		description: 'TR2::previous page',
		command: function(){
			Minibuffer.execute('TR2::previous');
		}
	});
	Minibuffer.addShortcutkey({
		key: 'R',
		description: 'TR2::update timeline',
		command: function(){
			Minibuffer.execute('TR2::update-timeline');
		}
	});
};
Twit.Lag = function(options){this.init(options);};
Twit.Lag.prototype ={
	init:function(ms){
		this.ms = ms;
		this.exe = null;
	}
	,set:function(func,obj){
		if (this.exe && !this.exe.complete) {
			this.exe.cancel();
		}
		return this.later(func,obj);
	}
	,later:function(func,thisObject) {
		var self = this;
		return function(){
			var args = arguments;
			var res = {
				complete: false,
				cancel: function(){clearTimeout(PID);},
				notify: function(){clearTimeout(PID);later_func()}
			};
			var later_func = function(){
				func.apply(thisObject,args);
				res.complete = true;
			};
			var PID = setTimeout(later_func,self.ms);
			self.exe = res;
			return res;
		}
	}
};
Twit.Cookie = function(options){this.init(options);};
Twit.Cookie.prototype ={
	init: function(options) {
		options = options || {};
		var expires = '';
		if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
			var date;
			if (typeof options.expires == 'number') {
				date = new Date();
				date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
			} else {
				date = options.expires;
			}
			expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
		}
		this.expires = expires;
		this.path = options.path ? '; path=' + options.path : '';
		this.domain = options.domain ? '; domain=' + options.domain : '';
		this.secure = options.secure ? '; secure' : '';
	}
	,cookie: function(name, value) {
		//if (!value) {
		if (typeof(value) == 'undefined') {
			var cookieValue = null;
			if (document.cookie && document.cookie != '') {
				var cookies = document.cookie.split(';');
				for (var i = 0; i < cookies.length; i++) {
					var cookie = (cookies[i]||'').replace(/^\s+|\s+$/g, "");
					if (cookie.substring(0, name.length + 1) == (name + '=')) {
						cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
						//break;
					}
				}
			}
			return cookieValue;
		} else {
			document.cookie = [name, '=', encodeURIComponent(value), this.expires, this.path, this.domain, this.secure].join('');
		}
	}
	,clear: function(name) {
		document.cookie = [name, '=Deleted;expires=Thu, 01-Jan-1970 00:00:01 GMT;', this.path, this.domain, this.secure].join('');
	}
};
Twit.addStyle = function(selector, property, sheetindex, ruleindex) {
	sheetindex = sheetindex || 0;
	var sheet = document.styleSheets[sheetindex];
	ruleindex = ruleindex || sheet.cssRules.length;
	return sheet.insertRule( selector + "{" + property + "}", ruleindex );
};
Twit.fontSet = function(fontSize){
	Twit.addStyle('body','font-size:'+Math.max(fontSize,60)+'%;');
};
Twit.setTimelineWidth = function(width){
	Twit.addStyle('#main .leftcontent' ,'width:' + width + '%;');
	var rightWidth = (100-width);
	Twit.addStyle('#main .rightcontent',['width:',rightWidth,'%;left:',width,'%;'].join(''));
};
Twit.clear = function(node){
	var ul = $('timeline');
	//var just = (LDRize.getParagraphes().current.paragraph||{}).node;
	var list = $x('id("timeline")/li');
	list.forEach(function(entry){
		if (/gm_ldrize_pinned/.test(entry.className)) {
			Twit.pins.push(entry);
		} else {
			ul.removeChild(entry);
		}
	});
};

Twit.debug = false;
(function(){
	var Cake = new Twit.Cookie({expires:365*10});
	var Session = new Twit.Cookie();
	Twit.Cake = Cake;
	Twit.Session = Session;
	var font = Twit.Cake.cookie('fontSize');
	if (parseInt(font,10)) Twit.fontSet(parseInt(font,10));
	var width = Twit.Cake.cookie('timelineWidth');
	if (parseInt(width,10)) Twit.setTimelineWidth(parseInt(width,10));
	var time = Twit.Cake.cookie('loadTimespan');
	if (parseInt(time,10)) Twit.Config.time = parseInt(time,10);
	var limit = Twit.Cake.cookie('viewLimit');
	if (parseInt(limit,10)) Twit.Config.limit = parseInt(limit,10);
})();
Twit.prototype = {
	initialize:function(myid){
		this.api = Twit.api.timeline + '?id=' + myid;
		this.timeline = $('timeline');
		this.userLine = $('users');
		this.userLine.innerHTML = '';
		this.loader = (function(self){
			return self.jsonpLoader;
		})(this);
		//this.counter();
		this.load();
		this.page = {
			Log:[[]]
			,length:1
			,index:1
		}
		//this.currents = [];
		this.user = {};
		var pageIndex = $('pageIndex');
		this.pageIndex = pageIndex;
		this.pageLength = $('pageLength');
		//this.viewlimit = $('viewlimit');
		var self = this;
		pageIndex.addEventListener('mouseover',function(e){
			pageIndex.style.background = '#ffff33';
		},false);
		pageIndex.addEventListener('mouseout',function(e){
			pageIndex.style.background = 'transparent';
		},false);
		pageIndex.addEventListener('DOMMouseScroll',function(e){
			e.preventDefault();
			if (e.detail > 0) {
				self.next();
			} else {
				self.back();
			}
		},false);
	}
	,load:function(){
		var opt = {
			 namespace:'Twit'
			,callback:'Timeline'
		};
		this.timerTime = NaN;
		this.loader(opt);
	}
	/*,counter:function(){
		this.timerTime = Twit.Config.time;
		var self = this;
		setInterval(function(){
			$('count').innerHTML = self.timerTime-- || 'loading..';
		},1000);
	}*/
	,check:function(data){
		if (data.error) return this.message([data.error]);
		var update = [];
		var users = [];
		var self = this;
		data.forEach(function(entry){
			if (Twit.debug || !Twit.data.entryLog[entry.id]) {
				update.push(entry.id);
				Twit.data.entryLog[entry.id] = entry;
				if (!Twit.data.userLog[entry.user.screen_name]) {
					users.push(entry.user);
					Twit.data.userLog[entry.user.screen_name] = entry.user;
					self.user[entry.user.screen_name] = [entry.id];
				} else {
					self.user[entry.user.screen_name].push(entry.id);
				}
			}
		});
		Twit.data.entryCount += update.length;
		Twit.data.userCount  += users.length;
		this.message([Twit.utils.dateBuild(new Date()), update.length + ' Updated!', 'Total: ' + Twit.data.entryCount + ' Updates'], !update.length);
		update = update.reverse();
		var Log = this.page.Log[this.page.length] || [];
		if (Log.length + update.length <= Twit.Config.limit) {
			this.page.Log[this.page.length] = Log.concat(update);
			update = (this.page.length == this.page.index) ? update : [];
		} else {
			var t = Twit.Config.limit - Log.length;
			this.page.Log[this.page.length] = Log.concat(update.slice(0,t));
			this.page.length++;
			this.page.Log[this.page.length] = update.slice(t);
			update = (this.page.length == this.page.index + 1) ? update.slice(0,t) : [];
		}
		this.pageUp();
		this.userBuild(users);
		return update;
	}
	,pageUp:function(){
		this.pageIndex.innerHTML = this.page.index;
		this.pageLength.innerHTML = this.page.length;
	}
	,jump:function(index,delta){
		index += delta || 0;
		if (index <= this.page.length && index >= 1) {
			this.page.index = index;
			if (!Twit.timelag) Twit.timelag = new Twit.Lag(500);
			var self = this;
			Twit.timelag.set(function(){
				var Log= self.page.Log[index];
				Twit.clear();
				Twit.pageReset();
				self.build(Log);
			})();
			this.pageUp();
		}
	}
	,userjump:function(Log){
		if (!Twit.timelag) Twit.timelag = new Twit.Lag(500);
		var self = this;
		Twit.timelag.set(function(){
			Twit.clear();
			Twit.pageReset();
			self.build(Log);
			//self.filter(Log);
		})();
		this.pageUp();
	}
	,next:function(){
		return this.jump(this.page.index,1);
	}
	,back:function(){
		return this.jump(this.page.index,-1);
	}
	,message:function(messages, update){
		var lineMessage;
		if (update && this.lastline && this.lastline == this.timeline.lastChild) {
			lineMessage = this.lastline;
		} else {
			lineMessage = document.createElement('li');
			lineMessage.className = 'lineMessage';
			this.lastline = lineMessage;
		}
		lineMessage.innerHTML = ['<ul><li>', messages.join('</li><li>'), '</li></ul>'].join('');
		this.timeline.appendChild(lineMessage);
	}
	,setStyleTag:function(){
		return ' style="background:#ffcc66;"';
	}
	,userBuild:function(users){
		var self = this;
		users.forEach(function(user,i){
			var li = document.createElement('li');
			li.className = 'userIcon';
			li.innerHTML = [
				//'<a href="http://twitter.com/',user.screen_name,'" target="_blank">'
					'<img src="',user.profile_image_url.replace(/_normal\./,'_mini.'),'" alt="',user.screen_name,'" width="24" height="24" />'
				//,'</a>'
			].join('');
			li.addEventListener('click',function(e){
				var Log = self.user[user.screen_name];
				self.userjump(Log);
			},false);
			self.userLine.appendChild(li);
		});
	}
	,build:function(update){
		var self = this;
		//console.time('build');
		var ul = this.timeline;
		var entrys = [];
		var range = document.createRange();
		range.selectNodeContents(ul);
		update.forEach(function(id,i){
			var up = Twit.data.entryLog[id];
			var entry = document.createElement('li');
			entry.className = 'xfolkentry';
			entry.id = 'U' + up.id;
			//entry.style.display = 'none';
			var ur = up.user;
			var text = up.text;
			var isReply = Twit.Config.MyNameExp.test(text);
			var reply = up.in_reply_to_status_id ? {user:text.replace(/.*?@(\w+).*/,'$1'),id:up.in_reply_to_status_id} : null;
			var df = range.createContextualFragment([
				'<ul class="content">'
					,'<li class="icon">'
					,'<a href="http://twitter.com/',ur.screen_name,'/statuses/',up.id,'" target="_blank" class="taggedlink">'
					,'<img src="',ur.profile_image_url,'" width="48" height="48" alt="',ur.name,'">'
					,'</a>'
					,'</li>'
					,'<li class="user description"><a href="http://twitter.com/',ur.screen_name,'" target="_blank">',ur.name,'/',ur.screen_name,'</a>'
						,!!ur.protected ? '<span><img src="http://static.twitter.com/images/icon_red_lock.gif" class="protected" title="This user’s updates are protected." /></span>' : ''
					,'</li>'
					,'<li class="entry-title entry-content"', (isReply ? self.setStyleTag() : ''), '>', Twit.utils.htmlBuild(text),'</li>'
					,'<li class="meta entry-meta">',
						'<span class="date"><a href="http://twitter.com/',ur.screen_name,'/statuses/',up.id,'" target="_blank">'
						,Twit.utils.dateBuild(new Date(up.created_at)),'</a></span>&nbsp;'
					,'<span class="source">from&nbsp;',up.source,'</span>'
					, reply ? ('<a href="http://twitter.com/' + reply.user + '/status/' + reply.id + '">in reply to ' + reply.user + '</a>') :''
					, up.favorited ? '<img src="http://static.twitter.com/images/icon_star_full.gif" />' :''
					,'</li>'
				,'</ul>'
			].join(''));
			entry.appendChild(df);
			ul.appendChild(entry);
			entrys.push(entry);
		});
		//console.timeEnd('build');
		//console.time('LDRize');
		if (window.PagerizeFilter && entrys.length > 0) {
			window.PagerizeFilter(entrys);
			Twit.apply_filters(entrys);
		}
		else if (entrys.length > 0 && window.Minibuffer) Minibuffer.execute('LDRize::paragraph-re-collect');
		//console.timeEnd('LDRize');
		//console.timeEnd('update');
		//console.timeEnd('load');
	}
	,jsonCallback:function(obj){
		//console.timeEnd('jsonp');
		//console.time('update');
		this.timerTime = Twit.Config.time;
		var up = this.check(obj);
		if(up) this.build(up);
	}
	,httpCallback:function(obj){
		//console.timeEnd('jsonp');
		//console.time('update');
		this.timerTime = Twit.Config.time;
		var up = this.check(obj);
		if(up) this.build(up);
	}
	,httpLoader:function(opt){
		var self = this;
		var url = this.api + '&' + (new Date().getTime());
		var xhr = new XMLHttpRequest();
		xhr.open('POST',url,true);
		xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
		xhr.onreadystatechange = function() {
			if ( x.readyState == 4 && (x.status >= 200 && x.status < 600) ) {
				clearTimeout(Timeout);
				self.httpCallback.call(self,obj);
			}
		};
		xhr.send(null);
		var Timeout = setTimeout(function(){
			xhr.abort();
			self.httpCallback.call(self,{error:'タイムラインの取得に失敗しました'});
		},Twit.Config.time-10);
	}
	,jsonpLoader:function(opt){
		//console.time('jsonp');
		var self = this;
		var namespace = opt.namespace;
		var callback = opt.callback;
		var callname = opt.callname || 'callback';
		window[namespace] = window[namespace] || {};
		window[namespace][callback] = function(obj){
			self.jsonCallback.call(self,obj);
			document.body.removeChild(script);
		};
		var url = this.api + '&' + callname + '=' + [namespace, callback].join('.') + '&' + (new Date().getTime());
		var script  = document.createElement('script');
		script.addEventListener('error',function(e){
			self.jsonCallback.call(self,{error:'タイムラインの取得に失敗しました'});
			//document.body.removeChild(script);
		},false);
		script.type = 'text/javascript';
		script.src  = url;
		document.body.appendChild(script);
	}
}
Twit.MyTwit.prototype = {
	load:function(){
		this.api = Twit.api.user + '?count=1';
		var opt = {
			 namespace:'Twit'
			,callback:'MyName'
			//,callname:'callback'
		};
		Twit.prototype.jsonpLoader.call(this,opt);
	}
	,reload:function(myname){
		var myid = Twit.Cake.cookie('myid');
		if (String(myid).match(/\D/) || String(myname).match(/\W/)) {
			this.load();
		} else {
			this.callback(myid, myname);
		}
	}
	,jsonCallback:function(data){
		var up = data[0] || {};
		var user = up.user || {};
		if (!!user.id) {
			this.callback(user.id,user.screen_name);
		} else {
			Twit.prototype.message.call(Twit.Twitter,[data.error],true);
		}
	}
}
//window.addEventListener('load', Twit.init,false);
Twit.init();

function log() {
	if (Twit.debug) console.log(arguments);
}
function $x (exp, context, root) {
	context = context || document;
	root = root || context.ownerDocument || document;
	var results = root.evaluate(exp,context,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
	var ret = [];
	for (var i = 0, len = results.snapshotLength; i < len; i++) {
		ret.push(results.snapshotItem(i));
	}
	return ret;
}

