/*!
 * Simple file path manipulation utility, borrowed from Node.JS
 * Copyright Joyent, Inc. and other Node contributors.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the
 * following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
if (typeof module === 'object' && typeof define !== 'function') {
	var define = function (factory) {
		module.exports = factory(require, exports, module);
	};
}

define(function(require, exports, module) {
	/**
	 * Resolves . and .. elements in a path array with directory names there
	 * must be no slashes, empty elements, or device names (c:\) in the array
	 * (so also no leading and trailing slashes - it does not distinguish
	 * relative and absolute paths)
	 * @param  {Array} parts
	 * @param  {Boolean} allowAboveRoot
	 */
	function normalizeArray(parts, allowAboveRoot) {
		// if the path tries to go above the root, `up` ends up > 0
		var up = 0;
		for (var i = parts.length - 1; i >= 0; i--) {
			var last = parts[i];
			if (last === '.') {
				parts.splice(i, 1);
			} else if (last === '..') {
				parts.splice(i, 1);
				up++;
			} else if (up) {
				parts.splice(i, 1);
				up--;
			}
		}

		// if the path is allowed to go above the root, restore leading ..s
		if (allowAboveRoot) {
			for (; up--; up) {
				parts.unshift('..');
			}
		}

		return parts;
	}

	// Split a filename into [root, dir, basename, ext], unix version
	// 'root' is just a slash, or nothing.
	var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
	var splitPath = function(filename) {
		return splitPathRe.exec(filename).slice(1);
	};

	return {
		sep: '/',
		delimiter: ':',

		resolve: function() {
			var resolvedPath = '', resolvedAbsolute = false;

			for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
				var path = (i >= 0) ? arguments[i] : '';

				// Skip empty and invalid entries
				if (typeof path !== 'string') {
					throw new TypeError('Arguments to path.resolve must be strings');
				} else if (!path) {
					continue;
				}

				resolvedPath = path + '/' + resolvedPath;
				resolvedAbsolute = path.charAt(0) === '/';
			}

			// At this point the path should be resolved to a full absolute path, but
			// handle relative paths to be safe (might happen when process.cwd() fails)

			// Normalize the path
			resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) {
				return !!p;
			}), !resolvedAbsolute).join('/');

			return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
		},

		normalize:  function(path) {
			var isAbsolute = this.isAbsolute(path);
			var trailingSlash = path.substr(-1) === '/';

			// Normalize the path
			path = normalizeArray(path.split('/').filter(function(p) {
				return !!p;
			}), !isAbsolute).join('/');

			if (!path && !isAbsolute) {
				path = '.';
			}

			if (path && trailingSlash) {
				path += '/';
			}

			return (isAbsolute ? '/' : '') + path;
		},

		isAbsolute: function(path) {
			return path.charAt(0) === '/';
		},

		join: function() {
			var paths = Array.prototype.slice.call(arguments, 0);
			return this.normalize(paths.filter(function(p, index) {
				if (typeof p !== 'string') {
					throw new TypeError('Arguments to path.join must be strings');
				}
				return p;
			}).join('/'));
		},

		relative: function(from, to) {
			from = this.resolve(from).substr(1);
			to = this.resolve(to).substr(1);

			function trim(arr) {
				var start = 0;
				for (; start < arr.length; start++) {
					if (arr[start] !== '') break;
				}

				var end = arr.length - 1;
				for (; end >= 0; end--) {
					if (arr[end] !== '') break;
				}

				if (start > end) return [];
				return arr.slice(start, end - start + 1);
			}

			var fromParts = trim(from.split('/'));
			var toParts = trim(to.split('/'));

			var length = Math.min(fromParts.length, toParts.length);
			var samePartsLength = length;
			for (var i = 0; i < length; i++) {
				if (fromParts[i] !== toParts[i]) {
					samePartsLength = i;
					break;
				}
			}

			var outputParts = [];
			for (var i = samePartsLength; i < fromParts.length; i++) {
				outputParts.push('..');
			}

			outputParts = outputParts.concat(toParts.slice(samePartsLength));

			return outputParts.join('/');
		},

		dirname: function(path) {
			var result = splitPath(path);
			var root = result[0];
			var dir = result[1];

			if (!root && !dir) {
				// No dirname whatsoever
				return '.';
			}

			if (dir) {
				// It has a dirname, strip trailing slash
				dir = dir.substr(0, dir.length - 1);
			}

			return root + dir;
		},

		basename: function(path, ext) {
			var f = splitPath(path)[2];
			if (ext && f.substr(-1 * ext.length) === ext) {
				f = f.substr(0, f.length - ext.length);
			}
			return f;
		},

		extname: function(path) {
			return splitPath(path)[3];
		},

		/**
		 * Returns given paths with prettified and shortened names
		 * @param  {Array} list List of paths
		 * @return {Array} List of objects with <code>name</code> and
		 * <code>path</code> properties
		 */
		prettifyPaths: function(list) {
			var lookup = {};
			var storeItem = function(item) {
				if (!(item in lookup)) {
					lookup[item] = 0;
				}

				lookup[item]++;
				return item;
			};

			var isValid = function() {
				return !Object.keys(lookup).some(function(k) {
					return lookup[k] > 1;
				});
			};

			var that = this;
			var out = list.map(function(item) {
				return {
					name: storeItem(that.basename(item)),
					dir: that.dirname(item),
					path: item
				};
			});

			var shouldBreak = false;
			while (!isValid() && !shouldBreak) {
				lookup = {};
				out = out.map(function(item) {
					var name = that.join(that.basename(item.dir), item.name);
					if (name === item.name) {
						shouldBreak = true;
					}

					return {
						name: storeItem(name),
						dir: that.dirname(item.dir),
						path: item.path
					};
				});
			}

			return out;
		}
	};
});