
/*
 * $Id: shinkansen.js 52 2007-12-11 12:01:15Z ujihisa $
 */

var shinkansens = $A([]);

var Shinkansen = Class.create();
Shinkansen.prototype = {
	initialize: function(line_name, train_names, stations, icons, checkboxes, points, trains)
	{
		this.line_name   = line_name;
		this.train_names = train_names;
		this.stations    = stations;
		this.icons       = icons;
		this.checkboxes  = checkboxes;
		this.points      = points.map(function(item) {
			return {
				lat: item[0],
				lng: item[1],
				station: item[2]
			};
		});
		this.trains      = trains.map(function(item) {
			var icon_no   = item[0];
			var icon      = this.icons[icon_no];
			var name_no   = item[1];
			var name      = this.train_names[name_no];
			var number    = item[2];
			var weekday   = (item[3] != 0);
			var saturday  = (item[4] != 0);
			var holiday   = (item[5] != 0);
			var timetable = item[6];
			return new Train(this, icon_no, icon, name_no, name, number, weekday, saturday, holiday, timetable);
		}.bind(this));

		this.routes_cache    = $H({});
		this.distances_cache = $H({});
	},

	get_routes: function(start_station, end_station)
	{
		var cache_key   = [start_station, end_station];
		var cache_value = this.routes_cache[cache_key];
		if ( cache_value != undefined ) return cache_value;

		var routes = $A([]);

		if ( start_station == end_station )
		{
			this.points.each(function(point) {
				if ( point.station == start_station )
				{
					routes.push(point);
					throw $break;
				}
			});
		}
		else if ( start_station > end_station )
		{
			// 上りの場合
			this.points.reverse(false).each(function(point) {
				if ( point.station == start_station && routes.size() == 0 )
				{
					routes.push(point);
				}
				else if ( point.station == end_station )
				{
					routes.push(point);
					throw $break;
				}
				else if ( routes.size() > 0 )
				{
					routes.push(point);
				}
			});
		}
		else if ( start_station < end_station )
		{
			// 下りの場合
			this.points.each(function(point) {
				if ( point.station == start_station && routes.size() == 0 )
				{
					routes.push(point);
				}
				else if ( point.station == end_station )
				{
					routes.push(point);
					throw $break;
				}
				else if ( routes.size() > 0 )
				{
					routes.push(point);
				}
			});
		}
		else
		{
			alert("BUG");
		}

		this.routes_cache[cache_key] = routes;

		return routes;
	},

	get_distances: function(routes)
	{
		// for debug
		//if ( routes.size() < 1 ) alert("BUG");

		if ( routes.size() == 1 )
		{
			return $A([{
				lat1: routes[0].lat,
				lng1: routes[0].lng,
				lat2: routes[0].lat,
				lng2: routes[0].lng,
				distance: 0.0,
				total_distance: 0.0
			}]);
		}

		var i = 0;
		var distances = $A([]);
		var total_distance = 0.0;
		for ( i = 0; i < routes.size() - 1; i++ )
		{
			var lat1 = routes[i + 0].lat;
			var lng1 = routes[i + 0].lng;
			var lat2 = routes[i + 1].lat;
			var lng2 = routes[i + 1].lng;
			var lat_d = Math.abs(lat1 - lat2);
			var lng_d = Math.abs(lng1 - lng2);
			var distance = Math.sqrt((lat_d * lat_d) + (lng_d * lng_d));

			total_distance += distance;

			distances.push({
				lat1: lat1,
				lng1: lng1,
				lat2: lat2,
				lng2: lng2,
				distance: distance,
				total_distance: total_distance
			});
		}

		return distances;
	},

	get_distances_with_cache: function(start_station, end_station)
	{
		var cache_key   = [start_station, end_station];
		var cache_value = this.distances_cache[cache_key];
		if ( cache_value != undefined ) return cache_value;

		var routes    = this.get_routes(start_station, end_station);
		var distances = this.get_distances(routes);

		this.distances_cache[cache_key] = distances;

		return distances;
	},

	get_position: function(distances, start_station, end_station, time)
	{
		// for debug
		//if ( distances.size() == 0 ) alert("BUG");
		//if ( start_station.time > time ) alert("BUG");
		//if ( end_station.time   < time ) alert("BUG");
		//if ( start_station.time > end_station.time ) alert("BUG");

		if ( start_station.station == end_station.station )
		{
			return {
				lat: distances[0].lat1,
				lng: distances[0].lng1
			};
		}

		var time_d = end_station.time - start_station.time;
		var time_p = (time - start_station.time) / time_d;

		var distance_d = distances.last().total_distance;
		var distance_c = distance_d * time_p;

		// MEMO: 呼び出される回数が多く、かつ繰り返し回数が多いため、eachではなくforを使用する
		var result = undefined;
		var i = 0, length = distances.length;
		for ( i = 0; i < length; i++ )
		{
			var item = distances[i];

			if ( item.total_distance >= distance_c )
			{
				var delta   = item.total_distance - distance_c;
				var delta_p = 1.0 - (delta / item.distance);

				var lat = undefined;
				if ( item.lat1 < item.lat2 )
				{
					// 北向き:
					lat = item.lat1 + (item.lat2 - item.lat1) * delta_p;
				}
				else
				{
					// 南向き:
					lat = item.lat1 - (item.lat1 - item.lat2) * delta_p;
				}

				var lng = undefined;
				if ( item.lng1 < item.lng2 )
				{
					// 東向き:
					lng = item.lng1 + (item.lng2 - item.lng1) * delta_p;
				}
				else
				{
					// 西向き:
					lng = item.lng1 - (item.lng1 - item.lng2) * delta_p;
				}

				result = {
					lat: lat,
					lng: lng
				};

				break;
			}
		}

		return result;
	},

	create_checkboxes: function(visible_trains)
	{
		var div = document.createElement('div');
		div.checkboxes = [];

		div.appendChild(document.createTextNode(this.line_name));
		div.appendChild(document.createTextNode(': [ '));

		var i;
		for ( i = 0; i < this.checkboxes.length; i++ )
		{
			var info = this.checkboxes[i];

			if ( i > 0 )
			{
				div.appendChild(document.createTextNode(' | '));
			}

			var checkbox = document.createElement('input');
			checkbox.type        = 'checkbox';
			checkbox.id          = info[0];
			checkbox.train_names = $A(info[2]);
			div.appendChild(checkbox);
			div.checkboxes.push(checkbox);

			// MEMO: Internet Explroer 6では、appendChildの後にcheckedを設定しないと状態が失われてしまう。
			checkbox.checked     = info[1];

			checkbox.train_names.each(function(name) {
				visible_trains[name] = checkbox.checked;
			}.bind(this));

			Event.observe(checkbox, 'click', function() {
				this.train_names.each(function(name) {
					visible_trains[name] = this.checked;
				}.bind(this));
			}.bindAsEventListener(checkbox), false);

			div.appendChild(document.createTextNode(' '));

			var image = document.createElement('img');
			image.src    = info[3];
			image.width  = info[4];
			image.height = info[5];
			div.appendChild(image);

			div.appendChild(document.createTextNode(' '));

			var label = document.createElement('label');
			var target = document.createAttribute('for');
			target.value = checkbox.id;
			label.setAttributeNode(target)
			label.innerHTML = info[2].first();
			div.appendChild(label);
		}

		div.appendChild(document.createTextNode(' ]'));

		return div;
	},

	end: null
};

var Train = Class.create();
Train.prototype = {
	initialize: function(shinkansen, icon_no, icon, name_no, name, number, weekday, saturday, holiday, timetable)
	{
		this.shinkansen = shinkansen;
		this.icon_no    = icon_no;
		this.icon       = icon;
		this.name_no    = name_no;
		this.name       = name;
		this.number     = number;
		this.weekday    = weekday;
		this.saturday   = saturday;
		this.holiday    = holiday;
		this.timetable  = timetable.map(function(item) {
			return {
				station: item[0],
				time: item[1] * 60
			};
		});

		// 始発駅と終点駅で59秒間停車する
		// MEMO: 九州新幹線に23:59到着の列車があり、24:00を超えるのを防ぐため1分間ではない
		this.timetable.unshift({
			station: this.timetable.first().station,
			time: this.timetable.first().time - 59
		});
		this.timetable.push({
			station: this.timetable.last().station,
			time: this.timetable.last().time + 59
		});

		// ハッシュのキーを生成
		// MEMO: 列車名+列車番号は一意ではないため、始発駅、始発時間を付加
		this.key       = this.name + this.number + this.timetable.first().station + this.timetable.first().time;

		this.full_name = this.name + this.number + '号';
		this.departure = {
			time: this.timetable.first().time,
			station: this.timetable.first().station,
			station_name: this.shinkansen.stations[this.timetable.first().station]
		};
		this.arrival = {
			time: this.timetable.last().time,
			station: this.timetable.last().station,
			station_name: this.shinkansen.stations[this.timetable.last().station]
		};
	},

	available: function(time)
	{
		var departure = this.timetable.first().time;
		var arrival   = this.timetable.last().time;

		return (departure <= time && arrival >= time);
	},

	get_prev_station: function(time)
	{
		var station   = undefined;
		var timetable = this.timetable;

		// MEMO: 呼び出される回数が多く、かつ繰り返し回数が多いため、eachではなくforを使用する
		var i = 0, length = timetable.length;
		for ( i = 0; i < length; i++ )
		{
			if ( timetable[i].time <= time )
			{
				station = timetable[i];
			}
		}

		return station;
	},

	get_next_station: function(time)
	{
		var station   = undefined;
		var timetable = this.timetable;

		// MEMO: 呼び出される回数が多く、かつ繰り返し回数が多いため、eachではなくforを使用する
		var i = 0, length = timetable.length;
		for ( i = length - 1; i >= 0; i-- )
		{
			if ( timetable[i].time >= time )
			{
				station = timetable[i];
			}
		}

		return station;
	},

	get_position: function(time)
	{
		var prev_station = this.get_prev_station(time);
		var next_station = this.get_next_station(time);
		var distances    = this.shinkansen.get_distances_with_cache(prev_station.station, next_station.station);
		var position     = this.shinkansen.get_position(distances, prev_station, next_station, time);
		return position;
	},

	get_icon: function()
	{
		var icon = new GIcon();
		icon.image      = this.icon[0];
		icon.iconSize   = new GSize(this.icon[1], this.icon[2]);
		icon.iconAnchor = new GPoint(this.icon[3], this.icon[4]);
		return icon;
	},

	end: null
};
