// fix IE6 background flicker bug
try {
  document.execCommand("BackgroundImageCache", false, true);
} catch(err) {}

Date.prototype.clone = function() {
	var	newdate = new Date();
	newdate.setTime(this.getTime());
	return newdate;
}

FUKIDASI_SIZE = [120, 120];
DAY_WIDTH = FUKIDASI_SIZE[0] * 24

Region = {
	update: function() {
//		alert(this.updating);
		if(this.updating)
			return;
		
		var	pos = this.get_position();
		var url = this.manager.create_region_url(pos[0], pos[1]);
		
		//this.innerHTML += url;
		
		///	request
		this.updating = true;
		this.request = new Ajax.Request(url, {
			method: 'get',
			onComplete: this.on_complete_update.bind(this),
			onException: this.on_exception_update.bind(this)
		});
	},
	
	/**
	 *	自分の位置
	 */
	get_position: function() {
		var	foo = this.id.split('_', 3);
		return [parseInt(foo[1]), parseInt(foo[2])];
	},
	
	/**
	 *	
	 */
	on_complete_update: function(transport, json) {
		this.updating = false;
		this.innerHTML = transport.responseText;
		this.manager.on_update_region(this, json);
	},
	
	on_exception_update: function(e) {
		this.updating = false;
	}
};

TimetableManager = Class.create();
TimetableManager.prototype = {
	initialize: function() {
		this.tableEl = $('timetable');
		this.topEl = $('top-caption');	///< 時刻を表示する
		this.leftEl = $('left-caption');	///< 人の顔とか日付とか
		this.contentEl = $('table-content');	///< 吹き出し
		
		this.scroller = new TableScroller(this);
		this.top_base = 0;

		///	contentをAjaxるときにtrue
		this.ajax_content = false;
		
		///	左と右をSync
		window.setInterval(this.sync_scroll.bind(this), 50);
	},
	
	/**
	 *	body.onLoad時に読んでくだしあ
	 */
	on_page_load: function() {
		///	fill content
		this.update_view();
		this.update_left_caption();
	},
	
	/**
	 *	Regionが更新されたときに呼ばれる
	 */
	on_update_region: function(regionEl, json) {
	},
	
	/**
	 *	regionを作る
	 */
	create_region: function(x, y) {
		if(!this.on_before_create_region(x, y))
			return false;
		
		var	lefttop = this.scroller.get_scroll();
		var	regionEl = document.createElement('div');
		Element.addClassName(regionEl, 'table-region');
		regionEl.id = 'region_'+x+'_'+y;
		//regionEl.innerHTML = x+', '+y;
		
		regionEl.style.left = (lefttop[0] + x * this.region_size[0]) + 'px';
		regionEl.style.top = (lefttop[1] + y * this.region_size[1]) + 'px';
		
		this.contentEl.appendChild(regionEl);
		Object.extend(regionEl, Region);
		regionEl.manager = this;
		
		regionEl.update();
		
		this.on_after_create_region(regionEl);
		
		return regionEl;
	},
	
	set_height: function(height) {
		var	sizedummy = $('size-dummy');
		if(!sizedummy) {
			sizedummy = document.createElement('div');
			sizedummy.setAttribute('id', 'size-dummy');
			
			this.contentEl.appendChild(sizedummy);
		}
		sizedummy.style.height = (height-1) + 'px';
	},
	
	/**
	 *	子リージョンをリストする
	 */
	get_regions: function() {
		var children = $A(this.contentEl.childNodes).findAll(function(node) {
			return node.nodeType==1 && Element.hasClassName(node, 'table-region');
		});
		return children;
	},
	
	/**
	 *	上キャプションの時刻をセットする
	 */
	set_hour_origin: function(hour) {
		var newbase = FUKIDASI_SIZE[0] * (23 - hour % 24);

		this.top_base = newbase;
		this.topEl.scrollLeft = newbase;
	},
	
	/**
	 *	Viewの位置が更新されたときに呼ばれる
	 *		半リージョン近づいたら読み込み
	 *		１リージョン遠ざかったら削除
	 */
	update_view: function() {
		var	self = this;
		var	region_size = this.region_size;
		var	regions = this.get_regions();
		var	scroll = this.scroller.get_scroll();
		
		if(this.ajax_content) {
			///	見えてる領域
			var	view = [
				0, this.contentEl.scrollTop,
				this.contentEl.clientWidth, this.contentEl.scrollTop + this.contentEl.clientHeight];
			
			///	削除判定 & 最大領域の計算
			var	maxregion = [99999, 99999, -99999, -99999];
			regions.each(function (node) {
				var	out = false;
				var	noderc = [
					node.offsetLeft, node.offsetTop,
					node.offsetLeft + node.offsetWidth,
					node.offsetTop + node.offsetHeight];
				
				if(noderc[2] < view[0] - region_size[0])
					out = true;
				else if(noderc[0] > view[2] + region_size[0])
					out = true;
				else if(noderc[3] < view[1] - region_size[1])
					out = true;
				else if(noderc[1] > view[3] + region_size[1])
					out = true;
				
				if(out) {
					self.contentEl.removeChild(node);
				} else {
					maxregion[0] = Math.min(maxregion[0], noderc[0]);
					maxregion[1] = Math.min(maxregion[1], noderc[1]);
					maxregion[2] = Math.max(maxregion[2], noderc[2]);
					maxregion[3] = Math.max(maxregion[3], noderc[3]);
				}
			});
			
			///	追加処理
			var	viewX = [
				Math.floor((view[0] - scroll[0] - region_size[0]/2) / region_size[0]),
				Math.floor((view[2] - scroll[0] + region_size[0]/2) / region_size[0])
			];
			var	viewY = [
				Math.floor((view[1] - region_size[1]/2) / region_size[1]),
				Math.floor((view[3] + region_size[1]/2) / region_size[1])
			];
			
			for(var y=viewY[0];y<=viewY[1];++y) {
				for(var x=viewX[0];x<=viewX[1];++x) {
					var	regionEl = $('region_'+x+'_'+y);
					
					if(regionEl) {
					} else {
						///	create region
						this.create_region(x, y);
					}
				}
			}
		}
				
		///	左をあわせる
		this.leftEl.scrollTop = this.contentEl.scrollTop;

		///	上の時計を合わせる
		var	topScroll;

		topScroll = this.top_base - scroll[0];
		if(this.scroller.horizontal_infinite) {
			if(topScroll<0 || topScroll > DAY_WIDTH) {
				//	日付が変わった
				this.on_change_date(topScroll < 0 ? true : false);
				this.top_base += topScroll > DAY_WIDTH ? -DAY_WIDTH : DAY_WIDTH;
				topScroll = (topScroll + DAY_WIDTH) % DAY_WIDTH;
				
				//	scrollerを巻き戻さないとなぁ
				//this.scroller.normalize();
			}
		}
		//topScroll = (topScroll + DAY_WIDTH) % DAY_WIDTH;
		this.topEl.scrollLeft = (topScroll + DAY_WIDTH) % DAY_WIDTH;
		
		this.on_update_view();
	},
	
	/**
	 *	すべての項目を更新する
	 */
	refresh: function() {
		this.get_regions().each(function(region) {
			region.update();
		});
		this.update_left_caption();
	},

	/**
	 *	スクロールバーの変化と同期させる
	 */	
	sync_scroll: function() {
		var	changed = false;
		if(this.leftEl.scrollTop!=this.contentEl.scrollTop) {
			this.leftEl.scrollTop = this.contentEl.scrollTop;
			changed = true;
		}
		
		//	Safariは横スクロールバーが出ちゃうので・・・
		if(this.contentEl.scrollLeft) {
			this.scroller.scroll_x(-this.contentEl.scrollLeft);
			this.contentEl.scrollLeft = 0;
			changed = true;
		}

		if(changed)
			this.update_view();
	}
};

function niketa(x) {
	return (x<10 ? '0' : '') + x;
}

function date_to_24oclock(date, dateonly) {
	var	s = date.getUTCFullYear() + '-' +
		niketa(date.getUTCMonth()+1) + '-' +
		niketa(date.getUTCDate());
	if(!dateonly) {
		s += 'T' + niketa(date.getUTCHours());
	}
	return s;
}

function date_to_human(date) {
	return date.getFullYear() + '-' +
		niketa(date.getMonth()+1) + '-' +
		niketa(date.getDate());
}

/**
 *	左右はpositionで、上下はscrollでやるのでゴチャゴチャ
 */
TableScroller = Class.create();
TableScroller.prototype = {
	initialize: function(tablemanager) {
		this.manager = tablemanager;
		this.contentEl = tablemanager.contentEl;
		
		this.offset = [0, 0];	///<	現在のスクロール位置
		
		this.scrolling = false;
		this.prevpos = [0, 0];	///<	直前のカーソル位置

		Event.observe(document.body, 'mousedown',
			this.on_mousedown.bindAsEventListener(this), false);
		Event.observe(document.body, 'mouseup',
			this.on_mouseup.bindAsEventListener(this), false);
		Event.observe(document.body, 'mouseover',
			this.on_mouseover.bindAsEventListener(this), false);
		Event.observe(document.body, 'mousemove',
			this.on_mousemove.bindAsEventListener(this), false);
//		Event.observe(this.contentEl, 'mousemove',
//			this.on_mousemove.bindAsEventListener(this), false);
	},

	/**
	 *	現在のスクロール値を得る
	 */	
	get_scroll: function() {
		return [this.offset[0], 0];
		//return [this.offset[0], this.contentEl.scrollTop];
	},
	
	/**
	 *	縦スクロールの範囲を設定
	 *	@param range [min, max]をピクセルで
	 */
	set_vertical_scroll: function(range) {
		this.vertical_range = range;
	},
	
	/**
	 *	横スクロールが、無限か一日かを設定
	 *	@param infinite 無限ですかー？
	 */
	set_horizontal_infinite: function(infinite) {
		this.horizontal_infinite = infinite;
	},
	
	/**
	 *	横スクロールの値を正規化する
	 */
	normalize: function() {
		this.offset[0] = (this.offset[0] + DAY_WIDTH) % DAY_WIDTH;
	},
	
	start_scrolling: function(cursorPos) {
		this.scrolling = true;
		this.prevpos = cursorPos;
	},
	stop_scrolling: function() {
		if(!this.scrolling)
			return;
		this.scrolling = false;
	},
	scroll_by_cursor: function(cursorPos) {
		delta = [cursorPos[0] - this.prevpos[0], cursorPos[1] - this.prevpos[1]]
		this.prevpos = cursorPos;
		
		///	スクロール
		this.scroll_x(delta[0]);
		
		//	縦スクロール
		if(this.vertical_range) {
			var	newpos = this.contentEl.scrollTop - delta[1];
			newpos = Math.max(this.vertical_range[0], Math.min(newpos, this.vertical_range[1] - this.contentEl.clientHeight));
			this.contentEl.scrollTop = newpos;
		} else {
			;//this.contentEl.scrollTop -= delta[1];
		}
		
		///	sync
		this.manager.update_view();
	},
	scroll_x: function(delta) {
		var	self = this;
		var	regions = this.manager.get_regions();
		var	viewwidth = this.contentEl.clientWidth;
		
		///	X座標をずらす
		var	left = this.offset[0] + delta;
		
		if(!this.horizontal_infinite) {
			///	rangeが決まっている場合、スクロール範囲を制限
			left = Math.max(
				Math.min(viewwidth - DAY_WIDTH, 0),
				Math.min(left, 0));
		}
		delta = left - this.offset[0];
		if(delta==0)
			return;

		regions.each(function(node){
			var	curleft = parseFloat(node.style.left);
			if(!curleft)
				curleft = 0.;
			node.style.left = (curleft + delta) +'px';
		});
		
		this.offset[0] = left;
	},
	
	on_mousedown: function(event) {
		Position.prepare();
		if(Position.within(this.contentEl, event.clientX, event.clientY)) {
			this.start_scrolling([event.screenX, event.screenY]);
			Event.stop(event);
		}
	},
	on_mouseup: function(event) {
		if(this.scrolling) {
			this.stop_scrolling();
			Event.stop(event);
		}
	},
	on_mouseover: function(event) {
//		$('debug').innerHTML = window.keyCode+', '+event.button +', '+event.which;
	},
	on_mousemove: function(event) {
		if(!this.scrolling)
			return;
		if(!Event.isLeftClick(event)) {
			this.stop_scrolling();
		} else {
			this.scroll_by_cursor([event.screenX, event.screenY]);
		}
		Event.stop(event);
	}
};

/**
 *	IE6とかで、CSSがうまく設定できないのでだるいからJavaScriptでやる
 */
function on_resize_window() {
	var	htmlEl = document.getElementsByTagName('html');
	var	viewSize = [document.body.clientWidth, document.body.clientHeight];

	if(htmlEl && htmlEl[0]) {
		viewSize = [htmlEl[0].clientWidth, htmlEl[0].clientHeight];
	}

	///	#timetableのサイズ
	var	timetable = $('timetable');
	var	timetableSize = [
		viewSize[0],
		Math.max(200, viewSize[1] - timetable.offsetTop)];

	timetable.style.width = timetableSize[0] + 'px';
	timetable.style.height = timetableSize[1] + 'px';

	///	caption
	var	topcaption = $('top-caption');
	var	leftcaption = $('left-caption');
	var	tablecontent = $('table-content');
	var	contentSize = [
		timetableSize[0] - topcaption.offsetLeft,
		timetableSize[1] - leftcaption.offsetTop];

  tablecontent.setStyle({
    'width': contentSize[0] + 'px',
    'height': contentSize[1] + 'px'
  });
  topcaption.setStyle({ 'width': contentSize[0] + 'px' });
  leftcaption.setStyle({ 'height': contentSize[1] + 'px' });
}

/**
 *	tinyrulのanchorの上にきたら、それを解決する
 */
function resolve_tinyurl(event) {
	var	anchor = Event.element(event);
	if(anchor.tagName!='A')
		return;
	
	///	tinyurlですか？
	if(!anchor.href.match(/http:\/\/tinyurl.com\/\w+/i))
		return;
	
	///	resolve中ですか？
	if(anchor.resolving)
		return;
	
	anchor.resolving = true;
	new Ajax.Request(
		'/api/resolve_tinyurl', {
			parameters: 'url='+escape(anchor.href),
			onComplete: function(transport) {
				url = transport.responseText;
				if(url) {
					anchor.href = url;
					window.status = url;
				}
			}
	});
}

/**
 *	onLoadで呼ばれ、いろいろと初期化する
 */
function page_loaded() {
  if(Prototype.Browser.IE && typeof document.body.style.maxHeight == "undefined") {
    alert("Sorry IE6 is not supported.");
    return;
  }
  
	gIsSafari = /Konqueror|Safari|KHTML/.test(navigator.userAgent) ? true : false;
	gIsLegacyBrowser = !(typeof document.body.style.maxHeight!="undefined");

	var	contentEl = $('table-content');
	var	topEl = $('top-caption');
	var	leftEl = $('left-caption');

	///	leftEl, topElをスクロールバー分縮める
	topEl.style.right = (topEl.clientWidth - contentEl.clientWidth) + 'px';
	leftEl.style.bottom = (leftEl.clientHeight - contentEl.clientHeight) + 'px';
	
	///	Timetableマネージャ
	gTimetableManager = new gTableClass();
	gTimetableManager.on_page_load();
	
	///	tinyurlを解決するハンドラ
//	Event.observe(window, 'mouseover', resolve_tinyurl, false);	
	
	///	IE6だとCSSがしょぼいので、JSでリサイズを処理する
	if(gIsLegacyBrowser) {
		on_resize_window();
		Event.observe(window, 'resize', on_resize_window, false);
	}
}

document.observe('dom:loaded', page_loaded);