App.Form = new Class({

    initialize: function(spec) {
        this.element = new Element('div', { 'class': 'form' });
        this.fields = [];
        this.buttons = [];

        spec.each(function(item) {
            if ($type(item) == 'array') {
                this.createFields(item, this.element);
            } else {
                if (item.type == 'actions') {
                    var actions = new App.Form.Actions;
                    this.createButtons(item.buttons, actions.element);
                    actions.element.inject(this.element);
                } else {
                    var section = new App.Form.Section(item.title);
                    this.createFields(item.fields, section.element);
                    section.element.inject(this.element);
                }
            }
        }, this);
    },

    createFields: function(fieldsSpec, parent) {
        fieldsSpec.each(function(spec) {
            var fieldClass = spec.type.capitalize() + 'Field';
            var field = new App.Form[fieldClass](this, spec);
            field.element.inject(parent);
            if (spec.value) field.setValue(spec.value);
        }, this);
    },

    createButtons: function(buttonsSpec, parent) {
        buttonsSpec.each(function(spec) {
            var button = new App.Form.Button(this, spec);
            button.element.inject(parent);
        }, this);
    },

    _getItemById: function(listType, id) {
        var list = this[listType], result;
        for (var i = 0, len = list.length; i < len; i++) {
            var item = list[i];
            if (item.options.id == id) {
                result = item;
                break;
            }
        }
        return result;
    },

    getFieldById: function(id) { return this._getItemById('fields', id); },
    getButtonById: function(id) { return this._getItemById('buttons', id); },

    getValues: function() {
        var result = {};
        this.fields.each(function(field) {
            result[field.options.id] = field.getValue();
        });
        return result;
    },

    clear: function() {
        this.fields.each(function(field) {
            field.clear();
        });
    }
});

App.Form.FieldsManager = new Class({

    initialize: function(field) {
        this.bound = {
            updateFieldButtonsState: function(event) { this.updateFieldButtonsState(event.target.retrieve('field')); }.bind(this),
            createNewField: function(event) { this.createNewField(event.target.retrieve('field')); }.bind(this),
            deleteField: function(event) { this.deleteField(event.target.retrieve('field')); }.bind(this)
        };

        this.fields = new Elements;
        this.addField(field);
    },

    addField: function(field) {
        field.buttons = {
            'add': new Element('span', { 'class': 'button add disabled', title:App.i18n('Add'), 'events': { 'click': this.bound.createNewField } }).store('field', field).inject(field.element),
            'delete': new Element('span', { 'class': 'button delete disabled', title:App.i18n('Delete'), 'events': { 'click': this.bound.deleteField } }).store('field', field).inject(field.element)
        };
        field.control.store('field', field).addEvent('keyup', this.bound.updateFieldButtonsState);
        this.fields.push(field);

        this.updateAllButtonsState();
    },

    updateAllButtonsState: function() {
        if (this.fields.length > 1) {
            this.fields.each(function(field) {
                field.buttons['add'].addClass('disabled');
                field.buttons['delete'][ (field.control.get('disabled')) ? 'addClass' : 'removeClass']('disabled');
            });
        } else {
            this.fields[0].buttons['delete'].addClass('disabled');
        }

        var lastField = this.fields.getLast();
        if (lastField.control.get('value')) {
            lastField.buttons.add.removeClass('disabled');
        }
    },

    updateFieldButtonsState: function(field) {
        if (field == this.fields.getLast()) {
            field.buttons.add[field.control.get('value') ? 'removeClass' : 'addClass']('disabled');
        }
    },

    createNewField: function(field) {
        var fieldClass = field.options.type.capitalize() + 'Field';
        var newField = new App.Form[fieldClass](field.form, field.options);
        newField.element.inject(field.element, 'after');
        this.addField(newField);
        this.updateAllButtonsState();
    },

    deleteField: function(field) {
        this.fields.erase(field);
        field.element.destroy();
        this.updateAllButtonsState();
    },

    setValues: function(values) {
        this.clear();
        if ($type(values) != 'array') values = [values];
        for (var i = 0, len = values.length; i < len; i++) {
            if (!this.fields[i]) {
                this.createNewField(this.fields.getLast());
            }
            this.fields[i].setValue(values[i], true);
        }
        this.updateAllButtonsState();
    },

    getValues: function() {
        return this.fields.map(function(field) {
            return field.getValue(true);
        }).clean();
    },

    clear: function() {
        while (this.fields.length > 1) {
            this.deleteField(this.fields.pop());
        }
        this.fields[0].control.set('value', '');
        this.updateFieldButtonsState(this.fields[0]);
    }
});

App.Form.Section = new Class({

    initialize: function(title) {
        this.element = new Element('div', { 'class': 'section' });
        this.title = new Element('span', { 'class': 'title', 'text': title }).inject(this.element);
    }
});

App.Form.Actions = new Class({

    Extends: App.Form.Section,

    initialize: function() {
        this.parent();
        this.element.addClass('actions');
    }
});

App.Form.Button = new Class({

    initialize: function(form, options) {
        this.form = form;
        this.options = options;

        this.form.buttons.push(this);
        this.element = new Element('button', { text: this.options.title });

        if (this.options.onClick) {
            this.element.addEvent('click', this.options.onClick);
        }

        if (this.options['class']) {
            this.element.addClass(this.options['class']);
        }
    },

    show: function() { this.element.removeClass('hidden'); },
    hide: function() { this.element.addClass('hidden'); }
});

App.Form.Field = new Class({

    initialize: function(form, options) {
        this.form = form;
        this.options = options;

        this.form.fields.push(this);
        this.element = new Element('div', { 'class': 'field' });

        if ($type(this.options.label) == 'array') {
            var label = this.options.label;
            this.label = new Element('select');
            for (var i = 0, len = label.length; i < len; i++) {
                new Element('option', { 'value': label[i].toLowerCase(), 'text': App.i18n(label[i]) }).inject(this.label);
            }
            this.label.inject(this.element, 'top');
            if (this.options.selectedLabel) this.setLabel(this.options.selectedLabel);
            /*if (this.options.value) {
                this.options.value.each(function(item){
                   if (item.selectedLabel) selectedLabel = item.selectedLabel;
                });

                if ( selectedLabel ) {
                    this.setLabel(selectedLabel);
                }
            }*/
        } else {
            this.label = new Element('label', { 'text': this.options.label }).inject(this.element, 'top');
        }

        if (this.options.id) {
            if ($type(this.options.label) == 'array') this.label.set('name', 'label_' + this.options.id + (this.options.cloneable ? '[]' : ''));
            if (this.control) this.control.set('name', this.options.id + (this.options.cloneable ? '[]' : ''));
            if (this.options.setid) this.control.set('id', this.options.id);
        }

        if (this.options.cloneable) {
            if (!this.options.manager) {
                this.manager = new App.Form.FieldsManager(this);
                this.options.manager = this.manager;
            } else {
                this.manager = this.options.manager;
            }
        }

        if (this.options['class']) {
            this.element.addClass(this.options['class']);
        }

        if (this.options.disabled) {
            this.disable();
        }
    },

    destroy: function() {
        this.form.fields.erase(this);
    },

    setLabel: function(label) {
        if (this.label.get('tag') == 'select') {
            this.label.set('value', label);
        } else {
            this.label.set('text', label);
        }
        return this;
    },

    getLabel: function() {
        if ($type(this.options.label) == 'array') {
            return this.label.get('value');
        }
    },

    setValue: function(value, ignoreClones) {
        if (this.options.cloneable && !ignoreClones) {
            this.manager.setValues(value);
        } else {
            if ($type(value) == 'array') value = value[0];
            if ($type(value) == 'object') {
                this.setLabel(value.label);
                this.control.set('value', value.value);
                this.label.set('disabled', Boolean(value.disabled));
                this.control.set('disabled', Boolean(value.disabled));
                this.control.store('disabled', Boolean(value.disabled));
                if (value.selected) {
                    for ( i = 0; i < this.label.options.length; i++ ) {
                        if ( this.label.options[i].value == value.selected ) {
                            this.label.selectedIndex = i;
                            break;
                        } else {
                            continue;
                        }
                    }
                }
            } else {
                this.control.set('value', value);
            }
        }
        return this;
    },

    getValue: function(ignoreClones) {
        if (this.options.cloneable && !ignoreClones) {
            return this.manager.getValues();
        }
        var disabled = this.control.retrieve('disabled');
        var label = this.getLabel();
        var value = this.control.get('value');
        return (!disabled ? value ? (label ? { label: label, value: value } : value) : null : null);
    },

    clear: function() {
        if (this.options.cloneable) {
            this.manager.clear();
        } else {
            this.control.set('value', '');
        }
    },

    disable: function() { this.control.set('disabled', true); },
    enable: function() { this.control.set('disabled', false); }
});

App.Form.TextField = new Class({

    Extends: App.Form.Field,

    initialize: function(form, options) {
        if (!this.control) { // could be created in child class
            var _class = 'class';
            if (options && options['class']) _class += options['class'];
            this.control = new Element('input', { 'class': _class, 'type': 'text'});
            if (options && options.disabled) this.control.disabled = true;
            if (options && options.id) this.control.id = options.id;
            // this.options.setid
        }
        this.parent(form, options); // should be called after control is created
        if (this.label) {
            this.control.inject(this.label, 'after');
        } else {
            this.control.inject(this.element, 'top');
        }
    }
});

App.Form.TextareaField = new Class({

    Extends: App.Form.TextField,

    initialize: function(form, options) {
        this.control = new Element('textarea', { 'class': 'control' });
        this.parent(form, options);
    }
});

App.Form.RadioField = new Class({

    Extends: App.Form.Field,

    setValue: function(value) {
        this.controls.each(function(control){
            //if (control.checked)
	    //control.set('value', value);
        });
    },
    getValue: function(ignoreClones) {
    },

    initialize: function(form, options) {
        this.parent(form, options);
        this.controls = new Elements;
        $H(this.options.values).each(function(value, index){
            var control = new Element('input', {'value': index, 'class': 'radio', 'type': 'radio', 'name': this.options.id, 'checked': (options.value == index) });
            control.setAttribute('value', index); // Fix for Opera
            this.controls.push(control);
            this.element.grab(control).grab(new Element('span', {'class':'radio_text', 'text': value })).appendText(' ');
        }.bind(this));
    }
});

App.Form.SelectField = new Class({

    Extends: App.Form.Field,

    _so_proces: function(e) {
        if (this.element.hasClass('so_on'))
        {
          var dTrg = e.target;
          if (dTrg && dTrg.get('tag') == 'a' && undefined != dTrg._value)
          {
            this.setValue(dTrg._value);
          }
          this.element.removeClass('so_on');
          document.removeEvent('click', this._d_click);
        }
        else
        {
          this.element.addClass('so_on');
          document.addEvent('click', this._d_click);
          if (Browser.Engine.trident)
            this.element.parentNode.style.zIndex = 9;
        }
        e && e.stop();
        return false;
    },

    setValue: function(value) {
        this.controls.each(function(control){
            var aOpts = control.options;
            for (var i = 0; i < aOpts.length; i ++)
            {
                aOpts[i].selected = (aOpts[i].value == value);
                if (aOpts[i].value == value)
                    this._current.innerHTML = aOpts[i].innerHTML;
            }
            control.value = value;
        }.bind(this));
        this._field.fireEvent('change', {target:this._field});
    },

    getValue: function(ignoreClones) {
        return this._field.value;
    },

    initialize: function(form, options) {
        this.parent(form, options);
        this.controls = new Elements;
        this.element
            .grab(new Element('span', {'class':'so input'})
                .addEvent('click', this._so_proces.bind(this))
                .grab(this._current = new Element('a', {href:'javascript:void(0)', 'class':'so_cur'}))
                .grab(new Element('span', {'class':'so_i_dd'}))
                .grab(new Element('span', {'class':'so_dd'})
                    .grab(this._ghost = new Element('span', {'class':'so_opt', styles:{'max-height':'150px'}}))
                    .grab(new Element('span', {'class':'so_dd_hlp', html:'<span class="hlp_tr"></span><span class="hlp_br"></span><span class="hlp_bl"></span>'}))
                  )
                .grab(this._field = new Element('select', {'class': 'select', 'name': this.options.id, 'id': this.options.id }))
                .setStyle('float', 'left')
              )
            .grab(new Element('br', {'class':'cb'}))
            .setStyle('overflow', 'visible');
        $H(this.options.values).each(function(value, index){
            new Element('option', {'value': index, 'text': value, 'selected': (options.selected == index) }).inject(this._field);
            var d = new Element('a', {href:'javascript:void(0)', 'text': value, 'class': ((options.selected == index) ? 'on' : '') }).inject(this._ghost);
            d._value = index;
            if (index == options.selected)
                this._current.innerHTML = value;
        }.bind(this));
        this.controls.push(this._field);
        this._d_click = this._so_proces.bind(this);
    }
});

App.Form.PasswordField = new Class({
    Extends: App.Form.Field,

    setValue: function(value) {
        this.controls.each(function(control){
            //if (control.checked) control.set('value', value);
        });
    },

    getValue: function(ignoreClones) {
    },

    initialize: function(form, options) {
        this.parent(form, options);
        this.controls = new Elements;
        ['old', 'new', 'repeat'].each(function(i){
            var control = new Element('input', { 'class': 'password', 'type': 'password', 'name': i, 'id': 'password-'+i });
            new Element('div')
                .grab(new Element('span', {'text': App.i18n(i)}))
                .grab(control)
                .inject(this.element);
            this.controls.push(control);
        }.bind(this));
        this.element.grab(new Element('br', {clear:'all'}));
        this.element.addClass('password');
    }
});

App.Form.FileField = new Class({

    Extends: App.Form.Field,

    initialize: function(form, options) {
        if (!this.control) { // could be created in child class
            this.control = new Element('input', { 'class': 'control', 'type': 'file' });
        }
        if (options && options.id) this.control.id = options.id;
        this.parent(form, options); // should be called after control is created
        if (this.label) {
            this.control.inject(this.label, 'after');
        } else {
            this.control.inject(this.element, 'top');
        }
    }
});

App.Form.RawCheckbox = new Class({

    initialize: function() {
        this.element = new Element('span', { 'class': 'checkbox' });
        this.hidden = new Element('input', { 'type': 'hidden' }).inject(this.element);
        this.image = new Element('img', {
            'src': App.baseurl + 'themes/' + App.theme + '/images/checkbox_off.png',
            'width': 12, 'height': 12,
            'events': {
                'click': this.toggle.bind(this)
            }
        }).inject(this.element);
    },

    check: function() {
        this.hidden.set('value', 1);
        this.image.set('src', App.baseurl + 'themes/' + App.theme + '/images/checkbox_on.png');
    },

    uncheck: function() {
        this.hidden.set('value', 0);
        this.image.set('src', App.baseurl + 'themes/' + App.theme + '/images/checkbox_off.png');
    },

    toggle: function() {
        this[(this.hidden.get('value').toInt() ? 'uncheck' : 'check')]();
    }
});

App.Form.CheckboxField = new Class({

    Extends: App.Form.Field,

    initialize: function(form, options) {
        this.parent(form, options);

        this.control = (Browser.Engine.trident)
        ? new Element(document.createElement('<input type="checkbox" ' + ((options.value) ? ' checked="checked"' : '') + ' name="' + options.id + '" id="' + options.id + '" value="1"/>'))
        : new Element('input', {'type': 'checkbox', 'id': options.id, 'name': options.id, 'value': 1}).set('checked', options.value);

        this.control.inject(this.element);
        this.element.set('id', 'el_' + options.id);
    }
});