define(
	['utils'],
	function (utils) {
		"use strict";

		var groups = {},
			types = {},
			aliases = {
				checkbox: 'cb',
				textarea: 'ta',
				text: 'tx',
				radio: 'ra',
				select: 'st'
			};

		/**
		 * @param {object||object[]} data
		 * @param {string} data.type
		 * @param {object[]} data.labels [optional]
		 */
		var create = function(data) {
			createNamespace(data);

			if (Array.isArray(data) && data.length > 1) {
				return createGroup(data);
			}
			else {
				data = Array.isArray(data) ? data[0] : data;

				if (data.type == 'radio' && !data.name) {
					throw new Error('Name required with type radio.');
				}

				return createSingle.call(types[data.type], data);
			}
		};

		var createNamespace = function(data) {
			data = Array.isArray(data) ? data : [data];

			data.forEach(function(data) {
				//check alias exists and throw error if not
				if (typeof aliases[data.type] == 'undefined') {
					throw new Error('Unsupported input type: ' + data.type);
				}

				//create namespace for type if undefined
				if (typeof types[data.type] == 'undefined') {
					types[data.type] = {
						index: 0
					};
				}
			}.bind(this));
		};

		/**
		 * @param {object} data
		 * @param {string} data.type
		 * @param {string} data.name [optional]
		 * @param {string} data.value [optional]
		 * @param {boolean} data.disabled [optional]
		 * @param {boolean} data.checked [optional]
		 * @param {object[]} data.options [optional]
		 * @param {function} data.change [optional]
		 * @param {object[]} data.classes [optional]
		 * @param {boolean} group
		 */
		var createSingle = function(data, group) {
			var tag = getTagName(data.type);
			var input = document.createElement(tag);
			var label = createLabel.call(this, input, data);
			var tmp, option;

			this.index++;
			input.className = 'ou-input';

			if (group) {
				groups[data.name] = ++groups[data.name] || 0;
				input.setAttribute('data-input-group', (Object.keys(groups).length - 1).toString());
				input.setAttribute('data-input-group-index', groups[data.name]);
			}

			if (tag == 'input') {
				input.type = data.type;
			}

			if (data.value) {
				input.value = data.value;
			}

			if (data.name) {
				input.name = data.name;
			}

			if (data.disabled) {
				input.disabled = data.checked;
			}

            if (data.hidden) {
                (data.label instanceof Element ? input : label).style.display = 'none';
            }

            if (Array.isArray(data.classes)) {
                (data.label instanceof Element ? input : label).className = data.classes.join(' ');
            }

            if (data.change) {
                input.addEventListener('change', data.change);
            }

			//switch for all specific stuff...
			switch (data.type) {
				case 'radio':
				case 'checkbox':
					if (data.checked) {
						input.checked = data.checked;
					}
					break;
				case 'select':
					tmp = [];

					data.options.forEach(function(opt, i) {
						if (i === 0) {
							option = document.createElement('option');
							option.textContent = 'Select...';
							option.selected = true;
							option.disabled = true;
							option.hidden = true;
							input.appendChild(option);
						}

						option = document.createElement('option');
						option.textContent = opt;
						option.value = i;
						tmp.push(option);
					});

					if (data.shuffle) {
						utils.shuffle(tmp);
					}

					tmp.forEach(function(option) {
						input.appendChild(option);
					});
					break;
				case 'text':
					if (data.valid == 'number') {
						input.addEventListener('input', function() {
							this.value = this.value.replace(/[^\d]+/g, '');
						});
					}
			}

			data.el = input;
			//data.el = data.el || [];
			//data.el.push(input);
			//console.log(data.el);

			//return label if newly created, otherwise the input
            return (data.label instanceof Element) ? input : label;
		};

		var createGroup = function(data) {
			var group = document.createElement('div');
			var num = Object.keys(groups).length;

			group.className = 'input-group';

			data.forEach(function(data) {
				data.name = data.name || 'input-group' + num;
				group.appendChild(createSingle.call(types[data.type], data, true));
			});

			return group;
		};

		/**
		 * @type {HTMLElement}
		 * @param {HTMLElement} input
		 * @param {object} data
		 * @param {string|object} data.label
		 * @param {string} [data.label.prefix]
		 * @param {string} [data.label.suffix]
		 */
		var createLabel = function(input, data) {
            if (!data.label) {
                throw new Error('Label data missing!');
            }

            var id = aliases[data.type] + this.index;
            var label, span, val;

            input.id = id;

            if (data.label instanceof Element) {
                label = data.label;
            }
            else {
                label = document.createElement('label');
                label.appendChild(input);

                if (typeof data.label == 'string') {
                    label.insertAdjacentHTML('beforeend', data.label);
                }
                else {
                    //stick spans around each part to help with styling later
                    span = label.appendChild(document.createElement('span'));
                    span.appendChild(input);

                    if (data.label.prefix) {
                        label.insertAdjacentHTML('afterbegin', '<span class="label-prefix">' + data.label.prefix + '</span>');
                    }
                    if (data.label.suffix) {
                        label.insertAdjacentHTML('beforeend', '<span class="label-suffix">' + data.label.suffix + '</span>');
                    }
                }
            }

            //wrap label in array (if not already) to work with it
            label = Array.isArray(label) ? label : [label];

            label.forEach(function(label) {
                val = label.getAttribute('for') || '';
                label.setAttribute('for', (val ? val + ' ' : '') + id);
            });

            return (label.length == 1) ? label[0] : label;
		};

		var isInput = function(type) {
			return !!aliases[type];
		};

		var getTagName = function(type) {
			return (type == 'radio' || type == 'checkbox' || type == 'text' || type == 'range') ? 'input' : type;
		};

		return {
			create: create,
			isInput: isInput
		};
	}
);
