/* Cabinet | 0.0.1 | https://github.com/notclive/Cabinet | https://github.com/notclive/Cabinet/blob/master/licence */

var cabinet = (function () {
	"use strict";

	/************ Container list ************/

	var containers = (function () {
		var pub = {},
		containers = [];

		pub.insert = function (container) {
			containers.push(container);
		};

		pub.find = function (index) {
			for (var c in containers) {
				if (containers.hasOwnProperty(c) && containers[c].getIndex() === index) {
					return containers[c];
				}
			}
			return null;
		};

		pub.remove = function (index) {
			for (var c in containers) {
				if (containers.hasOwnProperty(c) && containers[c].getIndex() === index) {
					containers = containers.splice(c, 1);
					break;
				}
			}
		};

		pub.getAllOfType = function (type) {
			var allOfType = [];
			for (var c in containers) {
				if (containers.hasOwnProperty(c) && containers[c].getType() === type) {
					allOfType.push(containers[c])
				}
			}
			return allOfType;
		}

		return pub;

	}());

	/************ Container object ************/

	var container = function (index, type, dimension) {
		var	container = {},
		pub = {},
		element,
		childType,
		children = [];

		if (index === null || index === undefined || !element instanceof HTMLElement) {
			throw 'first argument needs to be an HTMLElement';
		}

		if (type === "container") {
			element = $('<div></div>');
			$(index).append(element);
		} else {
			element = $(index);
		}

		var className = 'cabinet' + type.charAt(0).toUpperCase() + type.substr(1);
		$(element).addClass(className);

		/* getters */

		container.getElement = function () {
			return element;
		};

		container.getPublic = function () {
			return pub;
		};

		container.getType = function () {
			return type;
		};

		container.getChildren = function () {
			return children;
		};

		/* methods */

		container.rebuild = function () {
			if (children.length > 0) {
				rebuild(container, childType);
				for (var c in children) {
					children[c].rebuild();
				}
			}
		};

		container.getDimensionType = function () {
			if (dimension === undefined || dimension === null) {
				return 'fill';
			} else if (dimension.match(/\dpx/i)) {
				return 'pixels';
			} else if (dimension.match(/\d%/)) {
				return 'percent';
			} else {
				throw 'Invalid dimension';
			}
		};

		container.getDimensionValue = function () {
			var type = container.getDimensionType();
			if (type === 'pixels') {
				return Number(dimension.replace(/px/i, ''));
			} else if (type === 'percent') {
				return Number(dimension.replace('%', ''));
			}
			throw 'Can\'t get value of dimension';
		};

		container.setWidth = function (width) {
			setWidthOrHeight('width', width)
		};

		container.setHeight = function (height) {
			setWidthOrHeight('height', height)
		};

		function setWidthOrHeight(type, value) {
			var side1 = type === 'width' ? 'left' : 'top';
			var side2 = type === 'width' ? 'right' : 'bottom';
			value = Math.round(value);
			value -= $(element).css('border-' + side1 + '-width').replace(/px/i, '');
			value -= $(element).css('border-' + side2 + '-width').replace(/px/i, '');
			value -= $(element).css('margin-' + side1).replace(/px/i, '');
			value -= $(element).css('margin-' + side2).replace(/px/i, '');
			value -= $(element).css('padding-' + side1).replace(/px/i, '');
			value -= $(element).css('padding-' + side2).replace(/px/i, '');
			$(element)[type](value);
		}

		/* public */

		container.getIndex = pub.getElement = function () {
			return index;
		}

		container.insert = pub.insert = function (childPub) {
			var child = containers.find(childPub.getElement());
			childType = insertionType(children, child, childType);
			children.push(child);
			$(element).append(child.getElement());
			container.rebuild();
		};

		function insertionType(children, child, contains) {
			if (children.length > 0 && child.getType() !== contains) {
				throw 'Can\'t insert this container';
			}
			return child.getType();
		}

		container.remove = pub.remove = function () {
			for (var c in children) {
				if (children.hasOwnProperty(c)) {
					children[c].remove();
				}
			}
			containers.remove(container);
			$(index).remove();
		//TODO: 'deleted' boolean variable or delete object itself
		};

		return container;
	};

	/************ Rebuild ************/

	function rebuild(parent, contains) {
		var element = parent.getElement(),
		height = $(element).height(),
		width = $(element).width(),
		children = parent.getChildren(),	split = splitContainers(children),
		remainingDimension, setWidthOrHeight;
		styleContainers(children);
		if (contains === 'row') {
			allDimensions(children, setWidth, width);
			setWidthOrHeight = setHeight;
			remainingDimension = height;
		} else {
			allDimensions(children, setHeight, height);
			setWidthOrHeight = setWidth;
			remainingDimension = width;
		}
		remainingDimension -= pixelDimensions(split.pixel, setWidthOrHeight);
		remainingDimension -= percentDimensions(split.percent, height, setWidthOrHeight);
		fillDimensions(split.fill, remainingDimension, setWidthOrHeight);
		positionContainers(children, contains);
	}

	function splitContainers(containers) {
		var split = {
			pixel : [],
			percent : [],
			fill : []
		}, dimension;
		for (var c in containers) {
			if (containers.hasOwnProperty(c)) {
				dimension = containers[c].getDimensionType();
				if (dimension === 'pixels') {
					split.pixel.push(containers[c]);
				} else if (dimension === 'percent') {
					split.percent.push(containers[c]);
				} else if (dimension === 'fill') {
					split.fill.push(containers[c]);
				}
			}
		}
		return split;
	}

	function styleContainers(containers) {
		for (var c in containers) {
			if (containers.hasOwnProperty(c)) {
				$(containers[c].getElement()).css('position', 'absolute');
			}
		}
	}

	function allDimensions(containers, setWidthOrHeight, dimension) {
		for (var c in containers) {
			if (containers.hasOwnProperty(c)) {
				setWidthOrHeight(containers[c], dimension);
			}
		}
	}

	function pixelDimensions(containers, setWidthOrHeight) {
		var sumDimensions = 0;
		for (var c in containers) {
			if (containers.hasOwnProperty(c)) {
				var dimension = containers[c].getDimensionValue();
				sumDimensions += dimension;
				setWidthOrHeight(containers[c], dimension);
			}
		}
		return sumDimensions;
	}

	function percentDimensions(containers, height, setWidthOrHeight) {
		var sumDimensions = 0, c, dimension;
		for (c in containers) {
			if (containers.hasOwnProperty(c)) {
				dimension = containers[c].getDimensionValue() * height / 100;
				sumDimensions += dimension;
				setWidthOrHeight(containers[c], dimension);
			}
		}
		return sumDimensions;
	}

	function fillDimensions(containers, remainingDimension, setWidthOrHeight) {
		var dimension = remainingDimension / containers.length, c;
		for (c in containers) {
			if (containers.hasOwnProperty(c)) {
				setWidthOrHeight(containers[c], dimension);
			}
		}
	}

	function positionContainers(children, contains) {
		var top = 0, left = 0;
		for (var c in children) {
			if (children.hasOwnProperty(c)) {
				var element = children[c].getElement();
				$(element).css('top', top);
				$(element).css('left', left);
				if (contains === 'row') {
					top += $(element).outerHeight(true);
				} else {
					left += $(element).outerWidth(true);
				}
			}
		}
	}

	function setHeight(container, height) {
		container.setHeight(height);
	}

	function setWidth(container, width) {
		container.setWidth(width);
	}

	/************ Window Resize ************/

	$(window).resize(windowResize);

	function windowResize() {
		var allContainers = containers.getAllOfType('container');
		for (var c in allContainers) {
			if (allContainers.hasOwnProperty(c)) {
				allContainers[c].rebuild();
			}
		}
	}

	/************ Cabinet Object ************/

	var cabinet;

	cabinet = function (element) {
		if (containers.find(element) === null) {
			containers.insert(container(element, "container"));
		}
		return containers.find(element).getPublic();
	}

	cabinet.row = function (height) {
		return containerWithFreshDiv('row', height);
	};

	cabinet.column = function (width) {
		return containerWithFreshDiv('col', width);
	};

	function containerWithFreshDiv(type, dimension) {
		var element = document.createElement('div'),
		newContainer = container(element, type, dimension);
		containers.insert(newContainer);
		return newContainer.getPublic();
	}

	return cabinet;

}());

(function () {
	var CSSRules = '.cabinetContainer {position: relative; width: 100%; height: 100%; border: none; padding: 0px; margin: 0px} ';
	$('<style type="text/css">' + CSSRules + '</style>').appendTo('head');
}());

