Ox.ArrayInput = function(options, self) {

    self = self || {};
    var that = Ox.Element({}, self)
        .defaults({
            label: '',
            max: 0,
            sort: false,
            value: [],
            width: 256
        })
        .options(options || {});

    if (self.options.value.length == 0) {
        self.options.value = [''];
    }

    if (self.options.label) {
        self.$label = Ox.Label({
            title: self.options.label,
            width: self.options.width
        })
        .appendTo(that);
    }

    self.$element = [];
    self.$input = [];
    self.$removeButton = [];
    self.$addButton = [];
    self.options.value.forEach(function(value, i) {
        addInput(i, value);
    });

    function addInput(i, value, focus) {
        Ox.print('add', i)
        self.$element.splice(i, 0, Ox.Element()
            .css({height: '16px', marginTop: self.options.label || i > 0 ? '8px' : 0})
            .data({index: i}));
        if (i == 0) {
            self.$element[i].appendTo(that);
        } else {
            Ox.print(i, self.$element)
            self.$element[i].insertAfter(self.$element[i - 1]);
        }
        self.$input.splice(i, 0, Ox.Input({
                value: value,
                width: self.options.width - 48
            })
            .css({float: 'left'})
            .bindEvent({
                change: function(data) {
                    self.options.sort && data.value !== '' && sortInputs();
                }
            })
            .appendTo(self.$element[i]));
        focus && self.$input[i].focusInput();
        self.$removeButton.splice(i, 0, Ox.Button({
                disabled: self.$input.length == 1,
                title: 'remove',
                type: 'image'
            })
            .css({float: 'left', marginLeft: '8px'})
            .bind({
                click: function() {
                    removeInput($(this).parent().data('index'));
                }
            })
            .appendTo(self.$element[i]));
        self.$addButton.splice(i, 0, Ox.Button({
                disabled: i == self.options.max - 1,
                title: 'add',
                type: 'image'
            })
            .css({float: 'left', marginLeft: '8px'})
            .bind({
                click: function() {
                    addInput($(this).parent().data('index') + 1, '', true);
                }
            })
            .appendTo(self.$element[i]));
        self.$input.length > 1 && self.$removeButton[0].options({disabled: false});
        self.$input.length == self.options.max && self.$addButton.forEach(function($button) {
            $button.options({disabled: true});
        });
        updateIndices();
    }

    function removeInput(i) {
        Ox.print('remove', i);
        ['input', 'removeButton', 'addButton', 'element'].forEach(function(element) {
            var key = '$' + element;
            self[key][i].removeElement();
            self[key].splice(i, 1);
        });
        self.$input.length == 1 && self.$removeButton[0].options({disabled: true});
        self.$input.length == self.options.max - 1 && self.$addButton.forEach(function($button) {
            $button.options({disabled: false});
        });
        updateIndices();
    }

    function setWidths() {
        self.$label && self.$label.options({width: self.options.width})
        self.$element.forEach(function($element, i) {
            $element.css({width: self.options.width + 'px'});
            self.$input[i].options({width: self.options.width - 48});
        });
    }

    function sortInputs() {
        Ox.sort(self.$element, function($element) {
            return self.$input[$element.data('index')].value();
        }).forEach(function($element) {
            $element.detach();
        });
        self.$element.forEach(function($element, i) {
            $element.data({index: i}).appendTo(that);
        });
    }

    function updateIndices() {
        self.$element.forEach(function($element, i) {
            Ox.print(i, $element)
            $element.data({index: i});
        });
        /*
        self.$input.forEach(function($input, i) {
            $input.options({value: i});
        });
        */
    }

    self.setOption = function(key, value) {
        if (key == 'value') {
            if (self.options.value.length == 0) {
                self.options.value = [''];
            }
            self.$input && self.$input.forEach(function($input, i) {
                removeInput(i);
            });
            self.options.value.forEach(function(value, i) {
                addInput(i, value);
            });
        } else if (key == 'width') {
            setWidths();
        }
    }

    // fixme: can't we generally use options.value for this?
    that.value = function() {
        if (arguments.length == 0) {
            return Ox.map(self.$input, function($input) {
                var value = $input.value();
                return value === '' ? null : value;
            });
        }
    };

    return that;

};