(function($) {

    var defaults  ={
        container_cls:'sortable-repeated-field',
        field_cls:'sortable-field',
        flist_cls:'sortable-field-list',
        stub_cls:'sortable-field-stub',
        msg_cls:'sortable-field-message',
    };

    $.fn.SortableRepeatedField = function(options) {
        var opts = $.extend({}, defaults, options);

        // Remove those already initialized
        var $this = $(this)
            .filter(function() {
                return $(this).data('SortableRepeatedField') == undefined; });

        // Initialize data
        $this.each(function() {
            $(this).data('SortableRepeatedField', {
                $flist:$(),
                $stub:$(),
                $msg:$(),
                $add_buttons:$(),
                $delete_buttons:$()})
        });

        // Collect flists, stubs, msgflds, and buttons into each container's data
        function __collect(name) {
            return function() {
                var $this = $(this);
                var data = $this.closest('.'+opts.container_cls).data('SortableRepeatedField');
                data[name] = data[name].add($this);
            }
        }
        $this
            .find('.'+opts.flist_cls).each(__collect('$flist')).end()
            .find('.'+opts.stub_cls).each(__collect('$stub')).end()
            .find('.'+opts.msg_cls).each(__collect('$msg')).end()
            .find(':button.add').each(__collect('$add_buttons')).end()
            .find(':button.delete').each(__collect('$delete_buttons')).end();

        // Create objects
        $this
            .map(function() {
                var data = $(this).data('SortableRepeatedField');
                return new SortableRepeatedField(this, opts);
            })
            .each(function() {
                this.activate() });
        return $this;
    }
    $.fn.SortableRepeatedField.defaults = defaults;

    function SortableRepeatedField(container, opts) {
        var self = this;
        $.extend(self, {
            container:container,
            $container:$(container),
            opts:opts,
            data:null,
            activate: function() {
                self.data.$add_buttons.one('click', _addField);
                self.data.$delete_buttons.one('click', _deleteField);
                self.data.$flist.sortable({stop:_renumberFields});
                self.data.$stub.hide();
                _manageMessages();
            },
            fld_name: function() {
                return self.$container.attr('data-name');
            }
        });
        self.data = self.$container.data('SortableRepeatedField');
        function _addField() {
            var tpl_name = self.fld_name() + '#';
            var $new_field = self.data.$stub
                .clone()
                .removeClass(self.opts.stub_cls)
                .addClass(self.opts.field_cls)
                .show()
                .find(':input[name^='+tpl_name+']').each(function() {
                    var $this = $(this);
                    var name = $this.attr('name');
                    $this.attr('name', name.replace(
                        tpl_name,
                        self.fld_name() + '-0'));
                }).end();
            $new_field
                .find('[data-name*="' + tpl_name + '"]')
                .each(function() {
                    var $this = $(this);
                    $this.attr('data-name',
                        $this.attr('data-name')
                        .replace(tpl_name, self.fld_name() + '-0'));
                });
            $new_field.find('.hasDatepicker').removeClass('hasDatepicker');
            self.data.$flist.prepend($new_field);
            _renumberFields();
            _manageMessages();
            // Trigger event reattachment, etc.
            self.$container.removeData('SortableRepeatedField');
            $(document).trigger('clone');
        }
        function _deleteField() {
            var fld = $(this).closest('.'+self.opts.field_cls);
            fld.remove();
            _manageMessages();
        }
        function _manageMessages() {
            var attr_name = self.data.$flist.children().length > 1
                    ? 'data-nonempty-message'
                    : 'data-empty-message';
            var new_text = self.data.$msg.attr(attr_name);
            self.data.$msg.html(new_text);
        }
        function _renumberFields() {
            var prefix = self.fld_name() + '-';
            var regex = new RegExp(prefix + /\d+/.source)
            self.data.$flist.children().each(function(index) {
                $(this).find(':input[name^='+prefix+']').each(function() {
                    var $this=$(this);
                    var name=$this.attr('name');
                    var newname = name.replace(regex, prefix + index);
                    $this.attr('name', newname);
                });
                $(this).find('[data-name*="' + prefix + '"]')
                    .each(function() {
                        var $this = $(this);
                        $this.attr('data-name',
                            $this.attr('data-name')
                            .replace(regex, prefix + index));
                    });
            });
        }
    };
}(jQuery));
