/**
 * Isotope LayoutMode
 */

(function(window, factory) {
	// universal module definition
	/* jshint strict: false */ /*globals define, module, require */
	if (typeof define == 'function' && define.amd) {
		// AMD
		define(['get-size/get-size', 'outlayer/outlayer'], factory);
	} else if (typeof module == 'object' && module.exports) {
		// CommonJS
		module.exports = factory(require('get-size'), require('outlayer'));
	} else {
		// browser global
		window.Isotope = window.Isotope || {};
		window.Isotope.LayoutMode = factory(window.getSize, window.Outlayer);
	}
})(window, function factory(getSize, Outlayer) {
	'use strict';

	// layout mode class
	function LayoutMode(isotope) {
		this.isotope = isotope;
		// link properties
		if (isotope) {
			this.options = isotope.options[this.namespace];
			this.element = isotope.element;
			this.items = isotope.filteredItems;
			this.size = isotope.size;
		}
	}

	var proto = LayoutMode.prototype;

	/**
	 * some methods should just defer to default Outlayer method
	 * and reference the Isotope instance as `this`
	 **/
	var facadeMethods = [
		'_resetLayout',
		'_getItemLayoutPosition',
		'_manageStamp',
		'_getContainerSize',
		'_getElementOffset',
		'needsResizeLayout',
		'_getOption',
	];

	facadeMethods.forEach(function(methodName) {
		proto[methodName] = function() {
			return Outlayer.prototype[methodName].apply(
				this.isotope,
				arguments,
			);
		};
	});

	// -----  ----- //
	// for horizontal layout modes, check vertical size
	proto.needsVerticalResizeLayout = function() {
		// don't trigger if size did not change
		var size = getSize(this.isotope.element);
		// check that this.size and size are there
		// IE8 triggers resize on body size change, so they might not be
		var hasSizes = this.isotope.size && size;
		return hasSizes && size.innerHeight != this.isotope.size.innerHeight;
	};

	// ----- measurements ----- //
	proto._getMeasurement = function() {
		this.isotope._getMeasurement.apply(this, arguments);
	};

	proto.getColumnWidth = function() {
		this.getSegmentSize('column', 'Width');
	};

	proto.getRowHeight = function() {
		this.getSegmentSize('row', 'Height');
	};

	/**
	 * get columnWidth or rowHeight
	 * segment: 'column' or 'row'
	 * size 'Width' or 'Height'
	 **/
	proto.getSegmentSize = function(segment, size) {
		var segmentName = segment + size;
		var outerSize = 'outer' + size;
		// columnWidth / outerWidth // rowHeight / outerHeight
		this._getMeasurement(segmentName, outerSize);
		// got rowHeight or columnWidth, we can chill
		if (this[segmentName]) {
			return;
		}
		// fall back to item of first element
		var firstItemSize = this.getFirstItemSize();
		this[segmentName] =
			(firstItemSize && firstItemSize[outerSize]) ||
			// or size of container
			this.isotope.size['inner' + size];
	};

	proto.getFirstItemSize = function() {
		var firstItem = this.isotope.filteredItems[0];
		return firstItem && firstItem.element && getSize(firstItem.element);
	};

	// ----- methods that should reference isotope ----- //
	proto.layout = function() {
		this.isotope.layout.apply(this.isotope, arguments);
	};

	proto.getSize = function() {
		this.isotope.getSize();
		this.size = this.isotope.size;
	};

	// -------------------------- create -------------------------- //
	LayoutMode.modes = {};

	LayoutMode.create = function(namespace, options) {
		function Mode() {
			LayoutMode.apply(this, arguments);
		}

		Mode.prototype = Object.create(proto);
		Mode.prototype.constructor = Mode;

		// default options
		if (options) {
			Mode.options = options;
		}

		Mode.prototype.namespace = namespace;
		// register in Isotope
		LayoutMode.modes[namespace] = Mode;

		return Mode;
	};

	return LayoutMode;
});
