/*
 * Nicovideo Advertisement Framework
 */
// requires nicolib.js

var Advertisement = {

	initialize: function (url, options) {
		this.url = url;
		this.options = Object.extend({
			parameters: {},
			onLoaded: Prototype.emptyFunction
		}, options || {});
	},

	load: function () {
		this.options.parameters = this.buildParameters(this.options.parameters);
		new JSONP(this.url, { parameters: this.options.parameters });
		return this;
	},

	buildParameters: function (parameters) {
		return parameters;
	},

	update: function (data) {
		this.options.onLoaded.call(this, data, this);
	}

};

var TemplateAdvertisementMixin = {

	update: function (data) {
		this.options.onLoaded.call(this, data, this);
		var container = $(this.options.container || this.options.parameters.location);
		var template = this.options.template || this.defaultTemplate;
		if (container && template) {
			var html;
			if (typeof template.evaluate == "function") {
				html = template.evaluate(data);
			} else if (typeof template == "function") {
				html = template(data, this);
			}
			if (html)
				container.update(html);
		}
	}

};


var NicoAdvertisement = Class.create();
NicoAdvertisement.API_URL = "http://ad.nicovideo.jp/server/get";
Object.extend(NicoAdvertisement.prototype, Advertisement);
Object.extend(NicoAdvertisement.prototype, {

	initialize: function (options) {
		Advertisement.initialize.call(this, NicoAdvertisement.API_URL, options);
	},

	load: function () {
		this.options.parameters = this.buildParameters(this.options.parameters);
		NicoAdvertisement.load(this);
		return this;
	},

	buildParameters: function (parameters) {
		parameters.carrier = '5';
		parameters.format = 'json';
		return parameters;
	}

});

Object.extend(NicoAdvertisement, {

	packLocations: [],
	packAdvertisements: {},
	packParameters: {},
	packTimer: null,

	load: function (ads) {
		var loc = ads.options.parameters.location;
		if (!loc || typeof loc != "string") return;

		var count = ads.options.count || 1;
		var locations = [];
		for (var i = 0; i < count; i++) {
			locations.push(loc);
		}

		if (this.packAdvertisements[loc]) {
			var adhash = {};
			adhash[loc] = ads;
			this.sendRequest(locations, adhash, ads.options.parameters);
			return;
		}

		this.packLocations = this.packLocations.concat(locations);
		this.packAdvertisements[loc] = ads;
		Object.extend(this.packParameters, ads.options.parameters || {});

		this.wait();
	},

	wait: function () {
		if (this.packTimer) return;

		this.packTimer = setTimeout((function () {
			this.sendRequest(this.packLocations,
					this.packAdvertisements, this.packParameters);
			this.packTimer = null;
			this.packLocations = [];
			this.packAdvertisements = {};
			this.packParameters = {};
		}).bind(this), 500);
	},

	sendRequest: function (locations, advertisements, parameters) {
		parameters = parameters || {};
		parameters.location = locations.join(",");
		parameters.callback = function (r) {
			if (!r || !r.status || !r.data) return;
			$H(r.status).each(function (pair) {
				var data = r.data[pair.key];
				var ads = advertisements[pair.key];
				if (pair.value == 200 && data && ads) {
					if (!ads.options.count || ads.options.count <= 1) {
						data = data[ Math.floor(Math.random() * data.length) ];
					}
					ads.update(data);
				}
			});
		}

		new JSONP(NicoAdvertisement.API_URL, { parameters: parameters });
	}

});


var NicoTemplateAdvertisement = Class.create();
Object.extend(NicoTemplateAdvertisement.prototype, NicoAdvertisement.prototype);
Object.extend(NicoTemplateAdvertisement.prototype, TemplateAdvertisementMixin);


var NicoRotationAdvertisement = Class.create();
Object.extend(NicoRotationAdvertisement.prototype, Advertisement);
Object.extend(NicoRotationAdvertisement.prototype, {

	initialize: function (options) {
		this.updater = null;
		Advertisement.initialize.call(this, NicoAdvertisement.API_URL, options);
	},

	buildParameters: function (parameters) {
		parameters.carrier = '5';
		parameters.format = 'json';
		parameters.all = '1';
		parameters.callback = this.update.bind(this);
		return parameters;
	},

	update: function (r) {
		Advertisement.update.call(this, r);
		if (!r || !r.status || !r.data) return;
		$H(r.status).each((function (pair) {
			var data = r.data[pair.key];
			if (pair.value == 200 && data) {
				this.updater = new NicoRotationUpdater(data, this.options);
				throw $break;
			}
		}).bind(this));
	},

	next: function (counting, round) {
		return this.updater ? this.updater.next(counting, round) : false;
	},

	prev: function (counting, round) {
		return this.updater ? this.updater.prev(counting, round) : false;
	},

	pause: function () {
		if (this.updater) this.updater.pause();
	},

	resume: function () {
		if (this.updater) this.updater.resume();
	}

});


var NicoRotationUpdater = Class.create();
Object.extend(NicoRotationUpdater.prototype, TemplateAdvertisementMixin);
Object.extend(NicoRotationUpdater.prototype, {

	SECONDS_TO_SETTLE_VIEW: 3,

	initialize: function (rows, options) {
		if (!(rows instanceof Array) || rows.length == 0) {
			throw new Exception('Illegal rotation ad data');
		}

		NicoRotationUpdater.initialize();
		this.rows = rows;
		this.current = 0;
		this.options = Object.extend({
			onLoaded: Prototype.emptyFunction,
			onValidate: Prototype.emptyFunction,
			onTick: Prototype.emptyFunction,
			onMove: Prototype.emptyFunction,
			parameters: {},
			viewParameters: {},
			stopsOnEdge: true
		}, options || {});
		this.options.onLoaded =
			this.options.onBannerLoaded || Prototype.emptyFunction;
		this.container = $(this.options.container || this.options.parameters.location);
		this.timer = null;
		this.seconds = 0;
		this.nextSeconds = 0;
		this.currentData = null;
		this.ticker = null;

		this.validate(true);
	},

	validate: function (counting) {
		var data = this.rows[this.current % this.rows.length];
		this.currentData = data;
		this.update(data);
		this.options.onValidate(this, data);

		this.seconds = 0;
		this.nextSeconds = data.rotation_time || 30;
		this.resume();

		if (data && data.view) {
			var url = data.view;
			if (this.options.viewParameters) {
				var params = Hash.toQueryString(this.options.viewParameters);
				if (params) url += (url.indexOf("?") < 0 ? "?" : "&") + params;
			}
			if (counting) {
				new JSONP(url);
			} else {
				var sec = this.SECONDS_TO_SETTLE_VIEW;
				this.ticker = function () {
					if (--sec == 0) {
						this.ticker = null;
						new JSONP(url);
					}
				};
			}
		}

		return data;
	},

	tick: function () {
		var active = this.isActive();
		this.options.onTick(this, active);
		if (!active) {
			return;
		}
		if (this.ticker) {
			this.ticker();
		}
		if (this.nextSeconds > 0 && ++this.seconds >= this.nextSeconds) {
			this.nextSeconds = 0;
			this.next(true, true);
		}
	},

	getData: function (index, relative) {
		if (index === false || index === undefined) {
			index = this.current;
		} else if (relative) {
			index = index + this.current;
			if (index < 0) index += this.rows.length;
			if (index >= this.rows.length) index -= this.rows.length;
			if (index < 0) index = 0;
			if (index >= this.rows.length) index = this.rows.length;
		}
		if (index < 0 || index >= this.rows.length) return null;
		return this.rows[index];
	},

	show: function (index, counting) {
		if (index < 0 || index >= this.rows.length) {
			return false;
		}

		this.current = index;
		return this.validate(counting);
	},

	next: function (counting, round) {
		var nextIndex = this.current + 1;
		if (nextIndex >= this.rows.length) {
			if (!round) return false;
			nextIndex = 0;
		}
		var oldIndex = this.current;
		this.current = nextIndex;

		try {
			this.options.onMove(this, "next", oldIndex, counting);
		} catch (e) {
			if (e === $break) return false;
			throw e;
		}

		return this.validate(counting);
	},

	prev: function (counting, round) {
		var prevIndex = this.current - 1;
		if (prevIndex < 0) {
			if (!round) return false;
			prevIndex = this.rows.length - 1;
		}
		var oldIndex = this.current;
		this.current = prevIndex;

		try {
			this.options.onMove(this, "prev", oldIndex, counting);
		} catch (e) {
			if (e === $break) return false;
			throw e;
		}

		return this.validate(counting);
	},

	isActive: function () {
		return (NicoRotationUpdater.hasFocus() && this.inScreen());
	},

	inScreen: function () {
		var el = this.container;
		var p1 = Position.cumulativeOffset(el), p2 = Position.realOffset(el);
		var d = el.getDimensions();

		if (this.options.stopsOnEdge) {
			if (p1[0] < p2[0] || p1[1] < p2[1]) return false;
		} else {
			if (p1[0] + d.width < p2[0] || p1[1] + d.height < p2[1]) return false;
		}

		var p = { };
		p.left   = p1[0] - p2[0];
		p.top    = p1[1] - p2[1];
		p.right  = p.left + d.width;
		p.bottom = p.top + d.height;
		var w = {
			width: document.documentElement.clientWidth
				|| document.body.clientWidth
				|| document.body.offsetWidth
				|| window.innerWidth,
			height: document.documentElement.clientHeight
				|| document.body.clientHeight
				|| document.body.offsetHeight
				|| window.innerHeight
		};

		if (this.options.stopsOnEdge) {
			if (p.right > w.width || p.bottom > w.height) return false;
		} else {
			if (p.left > w.width || p.top > w.height) return false;
		}

		return true;
	},

	pause: function () {
		if (this.timer) {
			clearInterval(this.timer);
			this.timer = null;
		}
	},

	resume: function () {
		this.pause();
		if (this.rows && this.rows.length > 1) {
			this.timer = setInterval(this.tick.bind(this), 1000);
		}
	}

});

NicoRotationUpdater.initialized = false;
NicoRotationUpdater.initialize = function () {
	if (this.initialized) return;
	this.initialized = true;

	if (typeof document.hasFocus != "undefined") {
		NicoRotationUpdater.hasFocus = function () {
			return document.hasFocus();
		}
	} else {
		var focused = true;
		Event.observe(window, "focus", function (ev) { focused = true; });
		Event.observe(window, "blur", function (ev) { focused = false; });
		NicoRotationUpdater.hasFocus = function () { return focused; };
	}
}


NicoTemplateAdvertisement.prototype.defaultTemplate =
NicoRotationUpdater.prototype.defaultTemplate = function (data, ad) {
	if (!data || !data.url || (!data.image && !data.text)) return;
	ad = ad || {}; ad.options = ad.options || {};

	var html;
	if (data.image) {
		if (data.type == "swf" && typeof SWFObject != "undefined") {
			var so = new SWFObject(
				data.image,
				"ads_" + data.code,
				ad.options.width || "100%",
				ad.options.height || "100%"
			);
			so.addVariable("clickTAG", data.url);
			if (data.flashvars) {
				$H(data.flashvars).each(function (pair) {
					so.addVariable(pair.key, pair.value);
				});
			}
			html = so.getSWFHTML();
			delete so;
		} else {
			html = '<a href="' + data.url.escapeHTML() + '" target="_blank">'
				+ '<img src="' + data.image.escapeHTML() + '"'
				+ (data.text ? ' alt="' + data.text.escapeHTML() + '"' : '')
				+ (ad.options.width ? ' width="' + ad.options.width + '"' : '')
				+ (ad.options.height ? ' height="' + ad.options.height + '"' : '')
				+ '>'
				+ '</a>';
		}
	} else {
		html = '<a href="' + data.url.escapeHTML() + '" target="_blank">'
			+ (data.text || "").escapeHTML()
			+ '</a>';
	}
	return html;
}


var ChannelAdvertisement = Class.create();
ChannelAdvertisement.API_URL = "http://anime-ch.nicovideo.jp/api/getBanner.php";
Object.extend(ChannelAdvertisement.prototype, Advertisement);
Object.extend(ChannelAdvertisement.prototype, TemplateAdvertisementMixin);
Object.extend(ChannelAdvertisement.prototype, {

	initialize: function (options) {
		Advertisement.initialize.call(this, ChannelAdvertisement.API_URL, options);
	},

	buildParameters: function (parameters) {
		parameters.li = '1';
		parameters.cb = this.update.bind(this);
		return parameters;
	},

	defaultTemplate: function (data) {
		if (!data || !data.l || !data.b) return;
		return '<a href="' + data.l.escapeHTML() + '" target="_blank">' +
			'<img src="' + data.b.escapeHTML() +  '"></a>';
	}

});


function getAds(locations, options) {
	if (!locations || locations.length <= 0) return;

	options = options || {};
	options.parameters = options.parameters || {};

	if (locations instanceof Array) {
		locations.each(function (loc) {
			getAds(loc, options);
		});
	} else {
		options.parameters.location = locations;
		new NicoTemplateAdvertisement(options).load();
	}
}


var validateBanner = Prototype.emptyFunction;
var rotationBannerWidth = undefined;
var rotationBannerHeight = undefined;

function rotationBannerMotionStart() {
	$("rotation_anime_container").style.width = rotationBannerWidth;
	$("rotation_anime_container").style.height = rotationBannerHeight;
}
function rotationBannerMotionEnd() {
	validateBanner();
	setTimeout(function () {
		$("rotation_anime_container").style.width = "1px";
		$("rotation_anime_container").style.height = "1px";
		$("rotation_anime").cleanUp();
	}, 100);
}
function rotationBannerIOError() {
	validateBanner();
	$("rotation_anime_container").style.width = "1px";
	$("rotation_anime_container").style.height = "1px";
	$("rotation_anime").cleanUp();
}

var rotationAdvertisement = undefined;

function getRotationAdsFor468x60(parameters, viewParameters, migiue) {
	rotationBannerWidth = $("rotation_anime_container").style.width;
	rotationBannerHeight = $("rotation_anime_container").style.height;
	$("rotation_anime_container").style.width = "1px";
	$("rotation_anime_container").style.height = "1px";

	rotationAdvertisement = new NicoRotationAdvertisement({
		width: '468',
		height: '60',
		parameters: parameters,
		viewParameters: viewParameters,
		stopsOnEdge: false,
		onMove: function (updater, dir, oldIndex, counting) {
			$(parameters.location).innerHTML = "";
			var data = updater.getData();
			var anime = $("rotation_anime");
			$("rotation_next_image").src = "/img/_.gif";
			$("rotation_prev_image").src = "/img/_.gif";
			validateBanner = function () {
				updater.validate(counting);
			}
			try {
				if (dir == "next") {
					anime.slideNext(data.image);
				} else {
					anime.slidePrev(data.image);
				}
			} catch (e) {
				return;
			}
			throw $break;
		},
		onValidate: function (updater, data) {
			var prev = updater.getData(-1, true);
			var next = updater.getData(+1, true);
			$("rotation_prev_image").src = prev ? prev.left_image : '';
			$("rotation_next_image").src = next ? next.right_image : '';
		},
		onTick: function (updater, active) {
			["prev", "next"].each(function (dir) {
				var el = $("rotation_" + dir + "_link");
				var cls = "alt_" + dir + "_" + (active ? "on" : "off");
				if (el && el.className != cls) el.className = cls;
			});
		},
		onLoaded: function (r) {
			if (migiue && r && r.data && r.data[parameters.location]) {
				var ads = r.data[parameters.location];
				ads.each(function (ad) {
					ad.flashvars = { "migiue": migiue };
				});
			}
		}
	}).load();

	["prev", "next"].each(function (dir) {
		var el = $("rotation_"+dir+"_link");
		el.observe("click", function (ev) {
			el.blur();
			if (typeof rotationAdvertisement != "undefined")
				rotationAdvertisement[dir](false, true);
			window.focus();
			Event.stop(ev);
		}.bindAsEventListener());
	});
}
