

/**
 * 乱数発生のサポートメソッド
 */
Math.random_as_integer = function(max) {
	return Math.floor(Math.random() * max);
}

/**
 * 配列に対するサポートメソッド追加
 */
Array.prototype.any = function(v) {
	for (var i = 0; i < this.length; i++) {
		if (v == this[i]) {
			return true;
		}
	}
	return false;
}

Array.prototype.sample = function() {
	return this[Math.random_as_integer(this.length)];
}

/**
 * キーワードクラスの定義
 */
var Keyword = function (word, st, id) {
	this.word = word;
	this.st = Math.floor(st) % Keyword.JumpTo.length || 0;
	this.id = id;
}
Keyword.JumpTo = [
	"http://pc.dwango.jp/index.php/m/search/a/keyword?item_type=&key=",
	"http://pc.dwango.jp/index.php/m/search/a/keyword?item_type=artist&key=",
	"http://pc.dwango.jp/index.php/m/search/a/keyword?item_type=tieup&key=",
];
Keyword.JumpToID = [
	"http://pc.dwango.jp/index.php/m/portal/a/title/title_id/",
	"http://pc.dwango.jp/index.php/m/portal/a/artist/artist_id/",
	"http://pc.dwango.jp/index.php/m/portal/a/tieup/tieup_id/",
];
Keyword.prototype = {
	href: function() {
		// 一回は一回だけに制限しながら、透過的にURLを返す
		if (this.url) {
			return this.url;
		}
		return this.url = Keyword.JumpToID[this.st] + this.id;
	}
}

/**
 * 停止状態オブジェクトの定義
 */
var Stoped = function(tag) {
	this.tag = tag;
}
Stoped.prototype = {
	timers: [],
	interval: 0,
	restart: function() {
		var timers = jQuery.timers;
		var self = this;
		var t = new Date;
		this.tag.each(function(){
			// reregister
			for (var i = 0; i < self.timers.length; i++){
				timers.push(self.timers[i]);
				self.tag.startTime = t - self.interval;
			}
		});
	}
}
/**
 * Animation Stopping method based on jQuery.extend.stop
 */
Stoped.Stopper = function(clearQueue, gotoEnd) {
	var timers = jQuery.timers;
	if (clearQueue)
		this.queue([]);

	var stoped = new Stoped(this);
	var t = new Date;
	this.each(function(){
		// go in reversen order so anything added to the queue during the loop is ignored
		for ( var i = timers.length - 1; i >= 0; i-- )
			if ( timers[i].elem == this ) {
				if (gotoEnd)
					// force the next step to be the last
					timers[i](true);
				stoped.timers.push(timers[i]);
				stoped.iterval = t - this.startTime;
				timers.splice(i, 1);
			}
	});

	// start the next in the queue if the last step wasn't forced
	if (!gotoEnd)
		this.dequeue();

	return stoped;
}

/**
 * show recommend words
 */
var Recommender = function (pannel, json_url) {
	// 単語情報の読み込み
	this.loadJSON(json_url);

	// 表示用タグ配列の初期化
	pannel.empty();
	this.containers = [];
	for (var i = 0; i < Recommender.QueSize; i++) {
		var tag = $("<a></a>");
		tag.visible = false;
		tag.stop = Stoped.Stopper; // 初期化タイミングの都合によりstop()がオーバーライドされない対策
		pannel.append(tag);
		this.containers.push(tag);
	}

	// 最大文字サイズチェック
	var sample = this.containers[0];
	sample.text("S");
	this.max_leaf_height = 0;
	for (var i = 0; i < Recommender.Sizes.length; i++) {
		sample.addClass(Recommender.Sizes[i]);
		if (this.max_leaf_height < sample.height()) {
			this.max_leaf_height = sample.height();
		}
		sample.removeAttr("class");
	}
	sample.text("");

	// 表示領域の調整
	var height = pannel.height();
	this.left = pannel.width() + "px"
	this.height = height + "px";
	this.lines = Math.floor(height / this.max_leaf_height);
	this.line_height = Math.floor(height / this.lines);
	this.buttom_margin = (height - (this.line_height * this.lines)) / 2;
}
Recommender.Colors = [
	"white", "white", "white", "white", "white", "white", "white", 
	"white", "white", "white", "white", "white", "white", "white",
	"white", "white", "white", "white", "white", "white", "white",
	"purple", "red", "blue", "pink", "orange", "yellow", "green", "cyan",
];
Recommender.Sizes = [
	"keywordSmall", "keywordNormal", "keywordNormal", "keywordNormal", "keywordNormal",
	"keywordNormal", "keywordNormal", "keywordNormal", "keywordNormal", "keywordBig",
];
Recommender.SlowSpeed = 12000; // 文字が１文字の時の速度
Recommender.BaseSpeed = 9000; // 速度の幅の最高値
Recommender.SpeedLength = Recommender.BaseSpeed / 25;
Recommender.QueSize = 18; // 表示用コンテナの数
Recommender.MaxWord = 10; // 一画面に表示される最高単語数の設定
Recommender.IntervalMin = Recommender.SlowSpeed / Recommender.MaxWord; // 実行インターバルは単語数から計算

Recommender.prototype = {
	lastLineNums: new Array(4),
	loadJSON: function(url, self) {
		self = self || this; // setTimeoutに渡すfunctionにおいてthisの束縛に失敗するため、thisをselfとする
		$.getJSON(url, function(json) {
			self.keywords = [];
			var keywords = json.recommends;
			for (var i = 0; i < keywords.length; i++) {
				keyword = keywords[i];
				self.keywords.push(new Keyword(keyword.word, keyword.st, keyword.id));
			}
		});
	},
	/**
	 * あまり重ならないように位置を決定
	 */
	select_line_sample: function () {
		var line;
		do {
			line = Math.random_as_integer(this.lines);
		} while (this.lastLineNums.any(line));
		this.lastLineNums.unshift(line);
		this.lastLineNums.pop();
		return line;
	},
	showWord: function (pannel) {
		// 初期化確認
		if (!this.keywords) {
			// getJSONは並列実行するため初期化完了まで少々遅延するので、this.keywordsの状態を見て実行ブロック
			return;
		}

		// 埋め込み先をプールから一つ選定
		var container = null;
		for (var i = 0; i < this.containers.length; i++) {
			if (!this.containers[i].visible) {
				container = this.containers[i];
				break;
			}
		}
		if (!container) {
			// 選定に失敗した→画面上にキーワードがいっぱいあるはずなので、一回休み
			return;
		}

		// ホットキーワードから一個選択
		var keyword = this.keywords.sample();

		// 表示設定をニュートラルに戻す
		container.removeAttr("class");
		container.addClass("key");
		container.visible = true;

		// リコメンドワードの各種プロパティ
		container.addClass(Recommender.Sizes.sample());
		container.addClass(Recommender.Colors.sample());
		container.attr("href", keyword.href());
		var bottom = ((this.select_line_sample() * this.line_height) + this.buttom_margin) + "px";
		container.css("bottom", bottom);
		container.css("left", this.left);
		container.text(keyword.word);
		var left_end = -container.width() + "px";

		// 単語の流速
		var wordSpeed = Recommender.SlowSpeed - Math.min(Recommender.BaseSpeed, (keyword.word.length - 1) * Recommender.SpeedLength);

		// 流す
		container.animate({left: left_end}, {
			duration: wordSpeed,
			easing:   "linear",
			complete: function () {
				container.visible = false;
			}
		});
	},
	start: function (self) {
		self = self || this; // setTimeoutに渡すfunctionにおいてthisの束縛に失敗するため、thisをselfとする
		if (self.stoped) {
			return self;
		}
		self.showWord(self.pannel);

		// ランダム時間おいて再実行
		var f = arguments.callee; // setTimeout()内で、このメソッドのargumentsを利用するため、別名保存
		var sleep_time = Recommender.IntervalMin + Math.random_as_integer(100);
		setTimeout(function () {f(self)}, sleep_time);
		return self;
	},
	play: function () {
		if (!this.stoped) {
			return this.start();
		}
		for (var i = 0; i < this.stoped.length; i++) {
			this.stoped[i].restart();
		}

		this.stoped = null;
		return this.start();
	},
	stop: function () {
		if (this.stoped) {
			return self;
		}
		this.stoped = [];
		for (var i = 0; i < this.containers.length; i++) {
			if (this.containers[i].visible) {
				// this.containers[i].stop(false, false); // 標準的な実装
				var o = this.containers[i].stop();
				this.stoped.push(o);
			}
		}
		return self;
	}
}



