'use strict';

var assign           = require('es5-ext/object/assign')
  , validValue       = require('es5-ext/object/valid-value')
  , d                = require('d')
  , autoBind         = require('d/auto-bind')
  , toArray          = require('es5-ext/array/to-array')
  , memoize          = require('memoizee/plain')
  , memoizeMethods   = require('memoizee/methods-plain')
  , getNormalizer    = require('memoizee/normalizers/get-1')
  , clear            = require('dom-ext/element/#/clear')
  , replaceContent   = require('dom-ext/element/#/replace-content')
  , DOMRadio         = require('./_controls/radio')
  , DOMSelect        = require('./_controls/select')
  , DOMMultiple      = require('./_multiple')
  , DOMMultipleChBox = require('./_multiple/checkbox')
  , setup            = require('./')

  , defineProperties = Object.defineProperties
  , defineProperty = Object.defineProperty
  , createOption = DOMSelect.prototype.createOption
  , createRadio = DOMRadio.prototype.createOption

  , Radio, Select, Multiple;

Select = function (document, type/*, options*/) {
	var options = arguments[2];
	this.type = type;
	options = this._resolveOptions(options);
	DOMSelect.call(this, document, type, options);
	this.customLabels = Object(options.labels);
	if (options.group) {
		this.group = options.group;
		validValue(this.group.name);
	}
	this.dbOptions = toArray(type.members);
	if (options.only) {
		this.onlyFilter = options.only;
		if (this.onlyFilter.on) this.onlyFilter.on('change', this.reload);
	}
	this.append = options.append;
	this.reload();
};
Select.prototype = Object.create(DOMSelect.prototype, assign({
	constructor: d(Select)
}, memoizeMethods({
	createOption: d(function (name) {
		var item = this.type.meta[name];
		return createOption.call(this, name,
			this.customLabels[name] || (item && item.label) || name);
	}),
	createOptgroup: d(function (name) {
		var el = this.document.createElement('optgroup');
		el.setAttribute('label', this.group.set[name].label);
		return el;
	})
}), autoBind({
	reload: d(function () {
		var options = this.dbOptions, els, done;
		if (this.onlyFilter) {
			options = options.filter(this.onlyFilter.has, this.onlyFilter);
		}
		if (this.group) {
			els = [];
			done = {};
			options.forEach(function (name) {
				var meta = this.type.meta[name], group, optgroup, option;
				if (!meta) {
					console.warn("No meta found for", name);
					return;
				}
				group = meta[this.group.name];
				if (!group) {
					console.warn("No group found for", name);
					return;
				}
				optgroup = this.createOptgroup(group);
				option = this.createOption(name);
				if (!done.hasOwnProperty(group)) {
					clear.call(optgroup);
					done[group] = true;
					els.push(optgroup);
				}
				optgroup.appendChild(option);
			}, this);
		} else {
			els = options.map(this.createOption);
		}
		replaceContent.call(this.dom, this.chooseOption, els, this.append);
	})
})));

Radio = function (document, type/*, options*/) {
	var options = arguments[2];
	this.type = type;
	options = this._resolveOptions(options);
	this.controlOptions = Object(options.controls);
	DOMRadio.call(this, document, type, options);
	this.dom.classList.add('enum');
	this.customLabels = Object(options.labels);
	this.dbOptions = toArray(type.members);
	if (options.only) {
		this.onlyFilter = options.only;
		if (this.onlyFilter.on) this.onlyFilter.on('change', this.reload);
	}
	this.reload();
};
Radio.prototype = Object.create(DOMRadio.prototype, assign({
	constructor: d(Radio),
	createOption: d(function (name) {
		var item = this.type.meta[name];
		return createRadio.call(this, name,
			this.customLabels[name] || (item && item.label) || name, this.controlOptions[name]);
	})
}, autoBind({
	reload: d(function () {
		var options = this.dbOptions;
		if (this.onlyFilter) {
			options = options.filter(this.onlyFilter.has, this.onlyFilter);
		}
		replaceContent.call(this.dom, options.map(this.createOption, this));
	})
})));

Multiple = function (document, type/*, options*/) {
	var meta = type.meta;
	this.dbList = [];
	type.members.forEach(function (name) {
		var item = meta[name];
		this.push({ label: (item && item.label) || name, value: name });
	}, this.dbList);
	DOMMultipleChBox.apply(this, arguments);
};

Multiple.prototype = Object.create(DOMMultipleChBox.prototype, {
	constructor: d(Multiple)
});

module.exports = exports = memoize(function (Type) {
	setup(Type.database);
	defineProperties(Type, {
		DOMRadio: d(Radio),
		DOMSelect: d(Select),
		DOMMultipleInput: d(Multiple),
		toDOMInput: d(function (document/*, options*/) {
			var options = Object(arguments[1]);
			if (options.multiple) {
				if (options.multiType === 'base') {
					return new DOMMultiple(document, this, options);
				}
				return new this.DOMMultipleInput(document, this, options);
			}
			if (options.type === 'radio') {
				return new this.DOMRadio(document, this, options);
			}
			return new this.DOMSelect(document, this, options);
		})
	});
	if (Type.chooseLabel == null) {
		defineProperty(Type, 'chooseLabel', d("Choose:"));
		defineProperty(Type.$getOwn('chooseLabel'), 'required', d(true));
	}
	return Type;
}, { normalizer: getNormalizer() });

exports.Select = Select;
exports.Radio = Radio;
exports.Multiple = Multiple;

@@ -60,24 +60,88 @@
 ct/assign')%0A
+  , callable         = require('es5-ext/object/valid-callable')%0A
   , validVal
@@ -4093,27 +4093,131 @@
 meta
-;%0A%09this.dbList = %5B%5D
+, options = Object(arguments%5B2%5D), filter;%0A%09this.dbList = %5B%5D;%0A%09if (options.filter != null) filter = callable(options.filter)
 ;%0A%09t
@@ -4264,16 +4264,64 @@
 var item
+;%0A%09%09if (filter && !filter(name)) return;%0A%09%09item 
  = meta%5B
@@ -4434,28 +4434,41 @@
 Box.
-apply
+call
 (this, 
-arg
+doc
 ument
+, type, option
 s);%0A

Support filter on enum multiple checkboxes list