var optSpecs = [];
var nashi = {opts: []}, inputMinSize = 300, fullWidthExpansion = false;
var shortcutKeys = {};

function extract_num(q) {
	return parseInt(q.attr('id'), 10);
}

function parent_post($el) {
	return $el.closest('article, section');
}

function parent_model($el) {
	var $a = parent_post($el);
	var op = extract_num($a);
	if (!op)
		return null;
	if ($a.is('section'))
		return Threads.get(op);
	var $s = $a.parent('section');
	if (!$s.length) {
		// when we have better hover/inline expansion we will have to
		// deal with this, probably by setting data-op on the post
		console.warn($a, "'s parent is not thread?!");
		return null;
	}
	var num = op;
	op = extract_num($s);
	return Threads.lookup(num, op);
}

(function () {

/* OPTIONS LIST */
optSpecs.push(option_inline_expansion);
if (window.devicePixelRatio > 1)
	optSpecs.push(option_high_res);
optSpecs.push(option_thumbs);
optSpecs.push(option_autocomplete);
optSpecs.push(option_backlinks);
optSpecs.push(option_reply_at_right);
optSpecs.push(option_theme);
optSpecs.push(option_last_n);


_.defaults(options, {
	lastn: config.THREAD_LAST_N,
	inlinefit: 'width',
});
options = new Backbone.Model(options);


nashi.upload = !!$('<input type="file"/>').prop('disabled');

if (window.screen && screen.width <= 480) {
	inputMinSize = 50;
	fullWidthExpansion = true;
}

function load_ident() {
	try {
		var id = JSON.parse(localStorage.ident);
		if (id.name)
			$name.val(id.name);
		if (id.email)
			$email.val(id.email);
	}
	catch (e) {}
}

function save_ident() {
	try {
		var name = $name.val(), email = $email.val();
		if (email == 'misaki') {
			$email.val('');
			$.getScript(mediaURL + 'js/login.js');
			email = false;
		}
		else if (is_sage(email) && !is_noko(email))
			email = false;
		var id = {};
		if (name || email) {
			if (name)
				id.name = name;
			if (email)
				id.email = email;
			localStorage.setItem('ident', JSON.stringify(id));
		}
		else
			localStorage.removeItem('ident');
	}
	catch (e) {}
}

options.on('change', function () {
	try {
		localStorage.options = JSON.stringify(options);
	}
	catch (e) {}
});

/* LAST N CONFIG */

function option_last_n(n) {
	if (!reasonable_last_n(n))
		return;
	$.cookie('lastn', n);
	// should really load/hide posts as appropriate
}
option_last_n.id = 'lastn';
option_last_n.label = '[Last #]';
option_last_n.type = 'positive';

oneeSama.lastN = options.get('lastn');
options.on('change:lastn', function (model, lastN) {
	oneeSama.lastN = lastN;
});

/* THEMES */

var themes = [
	'moe',
	'gar',
	'mawaru',
	'moon',
	'ashita',
	'console',
	'tea',
	'higan',
	'homu',
	'me'
];

function option_theme(theme) {
	if (theme) {
		var css = theme + '.css?v=' + themeVersion;
		$('#theme').attr('href', mediaURL + 'css/' + css);
	}
}
option_theme.id = 'board.$BOARD.theme';
option_theme.label = 'Theme';
option_theme.type = themes;

/* THUMBNAIL OPTIONS */

var revealSetup = false;

function option_thumbs(type) {
	$.cookie('thumb', type);
	// really ought to apply the style immediately
	// need pinky/mid distinction in the model to do properly
	oneeSama.thumbStyle = type;
	var hide = type == 'hide';
	if (hide)
		$('img').hide();
	else
		$('img').show();

	if (hide && !revealSetup)
		$DOC.on('click', 'article', reveal_thumbnail);
	else if (!hide && revealSetup)
		$DOC.off('click', 'article', reveal_thumbnail);
	revealSetup = hide;
}
option_thumbs.id = 'board.$BOARD.thumbs';
option_thumbs.label = 'Thumbnails';
option_thumbs.type = thumbStyles;

/* Alt-click a post to reveal its thumbnail if hidden */
function reveal_thumbnail(event) {
	if (!event.altKey)
		return;
	var $article = $(event.target);
	var $img = $article.find('img');
	if ($img.length) {
		with_dom(function () { $img.show(); });
		return false;
	}

	/* look up the image info and make the thumbnail */
	var thread = Threads.get(extract_num($article.closest('section')));
	if (!thread)
		return;
	var post = thread.get('replies').get(extract_num($article));
	if (!post)
		return;
	var info = post.get('image');
	if (!info)
		return;

	with_dom(function () {
		var img = oneeSama.gazou_img(info, false);
		var $img = $.parseHTML(flatten(img.html).join(''));
		$article.find('figcaption').after($img);
	});
	return false;
}

/* REPLY AT RIGHT */

function option_reply_at_right(r) {
	if (r)
		$('<style/>', {
			id: 'reply-at-right',
			text: 'aside { margin: -26px 0 2px auto; }',
		}).appendTo('head');
	else
		$('#reply-at-right').remove();
}
option_reply_at_right.id = 'replyright';
option_reply_at_right.label = '[Reply] at right';
option_reply_at_right.type = 'checkbox';

/* AUTOCOMPLETE */

function option_autocomplete(b) {
	if (postForm)
		postForm.model.set('autocomplete', b);
}
option_autocomplete.id = 'autocomplete';
option_autocomplete.label = 'Auto-complete';
option_autocomplete.type = 'checkbox';

/* BACKLINKS */

function option_backlinks(b) {
	if (b)
		$('small').remove();
	else
		show_backlinks();
}
option_backlinks.id = 'nobacklinks';
option_backlinks.label = 'Backlinks';
option_backlinks.type = 'revcheckbox';

function show_backlinks() {
	if (load_thread_backlinks) {
		with_dom(function () {
			$('section').each(function () {
				load_thread_backlinks($(this));
			});
		});
		load_thread_backlinks = null;
		return;
	}

	Threads.each(function (thread) {
		thread.get('replies').each(function (reply) {
			if (reply.has('backlinks'))
				reply.trigger('change:backlinks');
		});
	});
}

var load_thread_backlinks = function ($section) {
	var op = extract_num($section);
	var replies = Threads.get(op).get('replies');
	$section.find('blockquote a').each(function () {
		var $a = $(this);
		var m = $a.attr('href').match(/^\d*#(\d+)$/);
		if (!m)
			return;
		var destId = parseInt(m[1], 10);
		if (!replies.get(destId)) // local backlinks only for now
			return;
		var src = replies.get(extract_num(parent_post($a)));
		if (!src)
			return;
		var update = {};
		update[destId] = op;
		add_post_links(src, update, op);
	});
};

/* INLINE EXPANSION */

function option_inline_expansion() {
	/* TODO: do it live */
}
option_inline_expansion.id = 'inlinefit';
option_inline_expansion.label = 'Expansion';
option_inline_expansion.type = ['none', 'full', 'width', 'height', 'both'];
option_inline_expansion.labels = ['no', 'full-size', 'fit to width',
		'fit to height', 'fit to both'];

function option_high_res() {
}
option_high_res.id = 'nohighres';
option_high_res.label = 'High-res expansions';
option_high_res.type = 'revcheckbox';

$DOC.on('mouseup', 'img, video', function (event) {
	/* Bypass expansion for non-left mouse clicks */
	if (options.get('inlinefit') != 'none' && event.which > 1) {
		var img = $(this);
		img.data('skipExpand', true);
		setTimeout(function () {
			img.removeData('skipExpand');
		}, 100);
	}
});

$DOC.on('click', 'img, video', function (event) {
	if (options.get('inlinefit') != 'none') {
		var $target = $(this);
		if (!$target.data('skipExpand'))
			toggle_expansion($target, event);
	}
});

function toggle_expansion(img, event) {
	var href = img.parent().attr('href');
	if (/^\.\.\/outbound\//.test(href))
		return;
	if (event.metaKey)
		return;
	event.preventDefault();
	var expand = !img.data('thumbSrc');
	img.closest('article').toggleClass('expanded', expand);
	var $imgs = img;
	if (THREAD && (event.altKey || event.shiftKey)) {
		var post = img.closest('article');
		if (post.length)
			$imgs = post.nextAll(':has(img):lt(4)').andSelf();
		else
			$imgs = img.closest('section').children(
					':has(img):lt(5)');
		$imgs = $imgs.find('img');
	}

	with_dom(function () {
		$imgs.each(function () {
			var $img = $(this);
			if (expand)
				expand_image($img);
			else {
				contract_image($img, event);
				event = null; // de-zoom to first image only
			}
		});
	});
}

function contract_image($img, event) {
	var thumb = $img.data('thumbSrc');
	if (!thumb)
		return;
	// try to keep the thumbnail in-window for large images
	var h = $img.height();
	var th = parseInt($img.data('thumbHeight'), 10);
	if (event) {
		var y = $img.offset().top, t = $(window).scrollTop();
		if (y < t && th < h)
			window.scrollBy(0, Math.max(th - h,
					y - t - event.clientY + th/2));
	}
	if (fullWidthExpansion)
		contract_full_width(parent_post($img));
	$img.replaceWith($('<img>')
			.width($img.data('thumbWidth')).height(th)
			.attr('src', thumb));
}

function expand_image($img) {
	if ($img.data('thumbSrc'))
		return;
	var a = $img.parent();
	var href = a.attr('href');
	if (!href)
		return;
	var cap = a.siblings('figcaption').text();
	var dims = cap.match(/(\d+)x(\d+)/);
	var video = /^Video/.test(cap);
	if (!dims)
		return;
	var tw = $img.width(), th = $img.height();
	var w = parseInt(dims[1], 10), h = parseInt(dims[2], 10);
	// if this is a high-density screen, reduce image size appropriately
	var r = window.devicePixelRatio;
	if (!options.get('nohighres') && !video && r && r > 1) {
		var min = 1000;
		if ((w > min || h > min) && w/r > tw && h/r > th) {
			w /= r;
			h /= r;
		}
	}

	$img.remove();
	$img = $(video ? '<video>' : '<img>', {
		src: href,
		width: w, height: h,
		data: {
			thumbWidth: tw, thumbHeight: th,
			thumbSrc: $img.attr('src'),
		},
		prop: video ? {autoplay: true, loop: true} : {},
	}).appendTo(a);

	var fit = options.get('inlinefit');
	if (fit != 'none') {
		var both = fit == 'both';
		fit_to_window($img, w, h, both || fit == 'width',
				both || fit == 'height');
	}
}

function fit_to_window($img, w, h, widthFlag, heightFlag) {
	var $post = parent_post($img);
	var overX = 0, overY = 0;
	if (widthFlag) {
		var innerWidth = $(window).innerWidth();
		var rect = $post.length && $post[0].getBoundingClientRect();
		if ($post.is('article')) {
			if (fullWidthExpansion && w > innerWidth) {
				overX = w - innerWidth;
				expand_full_width($img, $post, rect);
				heightFlag = false;
			}
			else {
				overX = rect.right - innerWidth;

				// right-side posts might fall off the left side of the page
				// account for them + left margin
				if (rect.left < 0) {
					// there has to be a better way...
					function m($el) {
						var m = parseInt($el.css('marginLeft'), 10);
						var p = parseInt($el.css('paddingLeft'), 10);
						return (m || 0) + (p || 0);
					}
					var $sec = $post.closest('section');
					var margin = m($('body')) + m($post) + m($sec) + 13;
					overX -= rect.left - margin;
				}
			}
		}
		else if ($post.is('section'))
			overX = w - (innerWidth - rect.left*2);
	}
	if (heightFlag) {
		overY = h - ($(window).innerHeight() - 20);
	}

	var aspect = h / w;
	var newW, newH;
	if (overX > 0) {
		newW = w - overX;
		newH = aspect * newW;
	}
	if (overY > 0) {
		// might have to fit to both width and height
		var maybeH = h - overY;
		if (!newH || maybeH < newH) {
			newH = maybeH;
			newW = newH / aspect;
		}
	}

	if (newW > 50 && newH > 50)
		$img.width(newW).height(newH);
}

function expand_full_width($img, $post, rect) {
	if ($post.hasClass('floop')) {
		$post.removeClass('floop');
		$post.data('crouching-floop', true);
	}
	var img = $img[0].getBoundingClientRect();
	$img.css('margin-left', -img.left + 'px');
	var over = rect.right - img.right;
	if (over > 0) {
		$post.css({
			'margin-right': -over+'px',
			'padding-right': 0,
			'border-right': 'none',
		});
	}
}

function contract_full_width($post) {
	if ($post.css('margin-right')[0] == '-') {
		$post.css({
			'margin-right': '',
			'padding-right': '',
			'border-right': '',
		});
	}
	if ($post.data('crouching-floop')) {
		$post.addClass('floop');
		$post.removeData('crouching-floop');
	}
}

/* SHORTCUT KEYS */

var shortcuts = [
	{label: 'New post', name: 'new', which: 78},
	{label: 'Image spoiler', name: 'togglespoiler', which: 73},
	{label: 'Finish post', name: 'done', which: 83},
	{label: 'Flip side', name: 'flip', which: 70},
];

function toggle_shortcuts(event) {
	event.preventDefault();
	var $shortcuts = $('#shortcuts');
	if ($shortcuts.length)
		return $shortcuts.remove();
	$shortcuts = $('<div/>', {
		id: 'shortcuts',
		click: select_shortcut,
		keyup: change_shortcut,
	});
	shortcuts.forEach(function (s) {
		var value = String.fromCharCode(shortcutKeys[s.name]);
		var $label = $('<label>', {text: s.label});
		$('<input>', {
			id: s.name, maxlength: 1, val: value,
		}).prependTo($label);
		$label.prepend(document.createTextNode('Alt+'));
		$shortcuts.append($label, '<br>');
	});
	$shortcuts.appendTo('#options-panel');
}

function select_shortcut(event) {
	if ($(event.target).is('input'))
		$(event.target).val('');
}

function change_shortcut(event) {
	if (event.which == 13)
		return false;
	var $input = $(event.target);
	var letter = $input.val();
	if (!(/^[a-z]$/i.exec(letter)))
		return;
	var which = letter.toUpperCase().charCodeAt(0);
	var name = $input.attr('id');
	if (!(name in shortcutKeys))
		return;
	shortcutKeys[name] = which;

	var shorts = options.get('shortcuts')
	if (!_.isObject(shorts)) {
		shorts = {};
		shorts[name] = which;
		options.set('shortcuts', shorts);
	}
	else {
		shorts[name] = which;
		options.trigger('change'); // force save
	}

	$input.blur();
}

_.defer(function () {
	load_ident();
	var save = _.debounce(save_ident, 1000);
	function prop() {
		if (postForm)
			postForm.propagate_ident();
		save();
	}
	$name.input(prop);
	$email.input(prop);

	optSpecs.forEach(function (spec) {
		spec.id = spec.id.replace(/\$BOARD/g, BOARD);
	});

	$('<a id="options">Options</a>').click(function () {
		var $opts = $('#options-panel');
		if (!$opts.length)
			$opts = make_options_panel().appendTo('body');
		if ($opts.is(':hidden'))
			oneeSama.trigger('renderOptions', $opts);
		$opts.toggle('fast');
	}).insertAfter('#sync');

	optSpecs.forEach(function (spec) {
		spec(options.get(spec.id));
	});

	var prefs = options.get('shortcuts') || {};
	shortcuts.forEach(function (s) {
		shortcutKeys[s.name] = prefs[s.name] || s.which;
	});
});

function make_options_panel() {
	var $opts = $('<div/>', {"class": 'modal', id: 'options-panel'});
	$opts.change(function (event) {
		var $o = $(event.target), id = $o.attr('id'), val;
		var spec = _.find(optSpecs, function (s) {
			return s.id == id;
		});
		if (!spec)
			return;
		if (spec.type == 'checkbox')
			val = !!$o.prop('checked');
		else if (spec.type == 'revcheckbox')
			val = !$o.prop('checked');
		else if (spec.type == 'positive')
			val = Math.max(parseInt($o.val(), 10), 1);
		else
			val = $o.val();
		options.set(id, val);
		with_dom(function () {
			spec(val);
		});
	});
	optSpecs.forEach(function (spec) {
		var id = spec.id;
		if (nashi.opts.indexOf(id) >= 0)
			return;
		var val = options.get(id), $input, type = spec.type;
		if (type == 'checkbox' || type == 'revcheckbox') {
			var b = (type == 'revcheckbox') ? !val : val;
			$input = $('<input type="checkbox" />')
				.prop('checked', b ? 'checked' : null);
		}
		else if (type == 'positive') {
			$input = $('<input />', {
				width: '4em',
				maxlength: 4,
				val: val,
			});
		}
		else if (type instanceof Array) {
			$input = $('<select/>');
			var labels = spec.labels || {};
			type.forEach(function (item, i) {
				var label = labels[i] || item;
				$('<option/>')
					.text(label).val(item)
					.appendTo($input);
			});
			if (type.indexOf(val) >= 0)
				$input.val(val);
		}
		var $label = $('<label/>').attr('for', id).text(spec.label);
		$opts.append($input.attr('id', id), ' ', $label, '<br>');
	});
	if (!nashi.shortcuts) {
		$opts.append($('<a/>', {
			href: '#', text: 'Shortcuts',
			click: toggle_shortcuts,
		}));
	}
	oneeSama.trigger('initOptions', $opts);
	return $opts.hide();
}

})();
