(function (window_, document_, $) {

// jQueryが存在しない場合、警告を出して終了する。
if (!$) {
	alert('jQuery not found. Get jQuery from http://jquery.com/ .');
	return;
}

var IS_MSIE = !!document_.uniqueID,
    IS_MSIE67 = IS_MSIE && !document_.querySelector;

var komicviewerize_called = false, //< komicviewerizeが呼び出されたかどうか。
    komicviewerize_loaded = false, //< window.onloadが呼び出されたかどうか。
    uniqueNumber = -1;

// komicviewerize
// オプションを設定するとき、この関数を呼び出す。
// ex. komicviewerize({ selector: '#comic' });
window_.komicviewerize = komicviewerize;

function komicviewerize(opt) {
	komicviewerize_called = true;

	// オプションの初期値の設定。
	opt = $.extend({
		selector: '.komicviewer' //< 対象となるセレクター
	}, opt || {});

	// DOM構築後呼び出しを行う。
	$(function () {
		var $komicviewer = $(opt.selector);
		delete opt.selector;
		// komicvierizeメソッドを全部に適用する。
		$komicviewer.komicviewerize(opt);
	});
}

// $#komicviewerize
// 指定された要素をkomicviewer形式に落とし込む。
$.fn.komicviewerize = $_komicviewerize;

function $_komicviewerize(opt) {
	return this.each(function () {
		opt.uniqueNumber = ++uniqueNumber;
		$.data(this, 'komicviewer', new Komicviewer(this, opt));
	});
}

// Komicviewerクラス
function Komicviewer() {
	this.init.apply(this, arguments);
}

Komicviewer.prototype = {
	// 初期化。ノードとオプションを引数に持つ。
	init: Komicviewer_init,
	// ページの見開き順序のクラスを設定する。
	setup: Komicviewer_setup,
	// 各画像サイズの調整を行う。
	fitting_: Komicviewer_fitting_,
	// 全画像サイズの調整を行う。
	fitting: Komicviewer_fitting,
	// ID名を取得する。
	getID: Komicviewer_getID
};

function Komicviewer_init(elm, opt) {
	var that = this;

	// オプションの初期化。
	opt = $.extend({
		maxWidth: 0, //< 拡大の最大値。px。
		maxHeight: 0,
		minWidth: 0, //< 縮小の最大値。px。
		minHeight: 0,
		idprefix: 'p', //< ID文字列の接頭辞。
		idsurfix: '', //< ID文字列の接尾辞。
		aspectRatio: 1 //< 縦長横長の基準。
	}, opt || {});
	opt.idformat = opt.idformat || (opt.idprefix + '{:num:-}{:page:}' + opt.idsurfix);

	this.element = elm;
	this.options = opt;
	this.$element = $(elm);
	this.$img = this.$element.find('img');

	// コミックビューワの設定。
	/*
	  <div class="komicviewer komicviewer-js" id="komicviewer-N">
	    <div class="komicviewer-pages [komicviewer-spread]">
	      <div class="komicviewer-page"><a href="#"><img /></a></div>
	      ..
	    </div>
	  </div>
	 */
	this.$element.attr('id', 'komicviewer-' + opt.uniqueNumber).addClass('komicviewer-js');

	var $pages = this.$pages = $('<div>').appendTo(elm).addClass('komicviewer-pages');
	this.$img.each(function (i) {
		var page = i + 1; //< ページ番号を1から始めたいため。
		var $page = $('<div>').appendTo($pages).addClass('komicviewer-page').attr('id', that.getID(page));
		$.data($page[0], 'komicviewer-page', page);
		// とりあえず、仮に見開きの先頭を決めておく。
		if (page % 2) {
			$page.addClass('komicviewer-firstpage');
		}
		var $a = $('<a>').appendTo($page).attr('href', '#').append(this);
	});

	// いらない要素の削除。
	this.$element.contents().not($pages).remove();

	var $window = $(window_);

	// 画像全てが、読み込み済み。
	if (komicviewerize_loaded) {
		this.setup();
	}
	// 画像が読み込み済みでない。
	else {
		// 読み込まれたタイミングで各種再計算。
		$window.load(function (evt) {
			that.setup();
			that.fitting(null);
			$window.unbind(evt); //< 念のため。
		});
	}

	// 画像サイズの再調整。
	this.fitting();
	$window.resize(function (evt) {
		// 要素が存在する。
		if (document_.getElementById('komicviewer-' + that.options.uniqueNumber)) {
			that.fitting();
		}
		// 無くなっている場合はイベントを削除。
		else {
			$window.unbind(evt);
		}
	});
}

function Komicviewer_setup() {
	var firstpage = false,
	    aspectRatio = this.options.aspectRatio;
	this.$img.each(function (i) {
		// アスペクト比が設定よりも大きい場合は、結合されたページと見做す。
		var spreadpage = this.naturalWidth / this.naturalHeight > aspectRatio;
		if (spreadpage) {
			firstpage = true;
		}
		// それ以外は、見開き分割ページと見做す。
		else {
			firstpage = !firstpage;
		}
		// クラスの再設定。
		$(this).closest('.komicviewer-page')
			[spreadpage ? 'addClass' : 'removeClass']('komicviewer-spreadpage')
			[firstpage ? 'addClass' : 'removeClass']('komicviewer-firstpage');
		if (spreadpage) {
			firstpage = false;
		}
	});
}

function Komicviewer_fitting_(elem, width, height) {
	imageCallback(elem, function (image) {
		if (image.naturalWidth / image.naturalHeight > width / height) {
			image.style.width = width + 'px';
			image.style.height = '';
		}
		else {
			image.style.width = '';
			image.style.height = height + 'px';
		}
	});
}

function Komicviewer_fitting(flag) {
	var that = this;
	var ie_pages_style, ie_width_bak, ie_width = 0, ie_width_tmp = 0;

	if (IS_MSIE67) {
		ie_pages_style = this.$pages[0].style;
		ie_width_bak = ie_pages_style.width;
		ie_pages_style.width = '1px';
		ie_pages_style.overflow = 'hidden';
	}

	// 要素の幅とウィンドウの高さ。
	var width = Math.min($(window_).width(), this.$element.width()),
	    height = $(window_).height(),
	    opt = this.options;

	// 自動設定の場合。
	if (flag == null) {
		// 更新なし。
		if (flag !== null && this.lastWidth_ === width && this.lastHeight_ === height) {
			if (IS_MSIE67) {
				ie_pages_style.width = ie_width_bak;
				ie_pages_style.overflow = '';
			}
			return;
		}
		this.lastWidth_ = width;
		this.lastHeight_ = height;
		flag = width / height >= opt.aspectRatio;
	}
	if (IS_MSIE67) {
		ie_pages_style.width = '';
		ie_pages_style.overflow = '';
	}

	// アスペクト比が一定以上なら、見開きモードにする。
	if (flag) {
		this.$pages.addClass('komicviewer-spread');
		this.$element.find('.komicviewer-page').each(function () {
			var $this = $(this),
			    page = $.data(this, 'komicviewer-page'),
			    firstpage = $this.hasClass('komicviewer-firstpage'),
			    spreadpage = $this.hasClass('komicviewer-spreadpage'),
			    id = that.getID(firstpage && !spreadpage ? page - 1 : page + 1);
			$this.find('a').attr('href', document_.getElementById(id) ? '#' + id : '#' + that.getID(page));
		});
		width /= 2;
		width = width | 0;
	}
	else {
		this.$pages.removeClass('komicviewer-spread');
		this.$element.find('.komicviewer-page').each(function () {
			var $this = $(this),
			    page = $.data(this, 'komicviewer-page')
			    id = that.getID(page + 1);
			$this.find('a').attr('href', document_.getElementById(id) ? '#' + id : '#');
		});
	}

	if (opt.maxWidth && opt.maxWidth < width) width = opt.maxWidth;
	if (opt.maxHeight && opt.maxHeight < height) height = opt.maxHeight;
	if (opt.minWidth && opt.minWidth > width) width = opt.minWidth;
	if (opt.minHeight && opt.minHeight > height) height = opt.minHeight;

	this.$img.each(function () {
		var $page;
		if (flag) {
			$page = $(this).closest('.komicviewer-page');
		}
		that.fitting_(this, $page && $page.hasClass('komicviewer-spreadpage') ? width * 2 : width, height);
		if (flag && IS_MSIE67) {
			if ($page.hasClass('komicviewer-firstpage')) {
				ie_width_tmp = this.width;
			}
			else {
				ie_width_tmp += this.width;
			}
			if (ie_width < ie_width_tmp) {
				ie_width = ie_width_tmp;
			}
		}
	});
	if (flag && IS_MSIE67) {
		ie_pages_style.width = ie_width + 'px';
	}
}

function Komicviewer_getID(page) {
	var opt = this.options;
	return opt.idformat.replace(/{(.*?):(.*?):(.*?)}/g, function (all, prefix, name, surfix) {
		switch (name) {
			case 'num': return opt.uniqueNumber ? prefix + opt.uniqueNumber + surfix : '';
			case 'page': return prefix + page + surfix;
		}
		return all;
	});
}

// komicviewerizeの自動読み込み
$(function () { komicviewerize_called || komicviewerize(); });
$(window_).load(function () { komicviewerize_loaded = true; });

// 画像読み込み制御
function imageCallback(image, success, failure, complete) {
	if (!image.finish && (image.complete || image.readyState === 'complete')) {
		image.finish = true;
		setNaturalSize(image);
	}

	if (image.finish) {
		image.naturalWidth ? success && success(image)
		                   : failure && failure(image);
		complete && complete(image);
		return;
	}

	image.finish = false;
	image.onload = load;
	image.onabort = image.onerror = error;

	function error() {
		if (image.finish) return;
		image.finish = true;
		failure && failure(image);
		complete && complete(image);
	}

	function load() {
		image.finish = true;
		if (image.complete || image.readyState === 'complete') {
			setNaturalSize(image);
			success && success(image);
		}
		else {
			failure && failure(image);
		}
		complete && complete(image);
	}

	function setNaturalSize(image) {
		if ('naturalWidth' in image) {}
		else if (IS_MSIE) {
			var rs = image.runtimeStyle,
			    md = rs.display,
			    mw = rs.width,
			    mh = rs.height;
			rs.display = 'inline-block';
			rs.width = 'auto';
			rs.height = 'auto';
			image.naturalWidth = image.width;
			image.naturalHeight = image.height;
			rs.display = md;
			rs.width = mw;
			rs.height = mh;
		}
		else {
			var mw = image.width,
			    mh = image.height;
			image.removeAttribute('width');
			image.removeAttribute('height');
			image.naturalWidth = image.width;
			image.naturalHeight = image.height;
			image.width = mw;
			image.height = mh;
		}

		image.removeAttribute('width');
		image.removeAttribute('height');
	}
}


} (this, document, jQuery));

