var AjaxForm = function(formElement,options)
{
    /** 
     * Configuration constants
     */
    this.config = {};
    
    /**
     * Language
     */
    this.lang = {
        validationFailed: 'There seem to be a couple of problems with the information you entered. Please check and try again.', //#Lang
        validationFailedOkButton: 'OK' //#Lang
    }
    
    /** 
     * Default options
     */
    this.options = {
        autoAjax: true,
        ajaxRequestAdditionalParameters: {},
        onSubmit: null,
        onSuccess: null,
        onFailure: null,
        //stopSubmitEvent: true,
        checkValidationOnSubmit: true,
        validators: {}
    };
    Object.extend(this.options, options || {});
    
    /**
     * Define class properties
     */
    this.formElement = $(formElement);
    this.formFields = [];
    this.formFieldsByFieldName = {};
    this.buttonsElement = null;
    this.formLoader = null;
    this._onSubmitHandler = null;
    this.actionUrl = null;
    this.disableSubmit = false;
    
    /**
     * Register the form
     */
    this.register();
};

AjaxForm.prototype = {
    
    /**
     * Sets up the form, registers it onSubmit handler, and any inline element
     * validators
     */
    register: function()
    {
        this.actionUrl = this.formElement.readAttribute('action');

        this.buttonsElement = this.formElement.select('.formButtons')[0];
        this.loaderImage = this.buttonsElement.select('.loader')[0];

      // Register form fields
        this.formFields = new Array();
        this.formElement.select('.formRow').each(function(formRow){
            if (formRow.hasClassName('ajaxFormIgnore')) {
                return;
            }
            var ajaxFormField = new AjaxFormField(formRow);
            ajaxFormField.observeBlur(function(){
                if (this.options.validators && this.options.validators[ajaxFormField.fieldName]) {
                    ajaxFormField.loading(0);
                    //if (!ajaxFormField.hasUserChanges()) {
                    //    return;
                    //}
                    this.options.validators[ajaxFormField.fieldName].validate(ajaxFormField);
                }
            }.bind(this));
            this.formFields.push(ajaxFormField);
            this.formFieldsByFieldName[ajaxFormField.fieldName] = ajaxFormField;
        }.bind(this));
                
        // On submit handling
        this._onSubmitHandler = this.onSubmitHandler.bindAsEventListener(this);
        this.formElement.observe('submit',this._onSubmitHandler);
    },

    getFormField: function(fieldName)
    {
        if (this.formFieldsByFieldName[fieldName]) {
            return this.formFieldsByFieldName[fieldName];
        }
        return null;
    },
    
    onSubmitHandler: function(event)
    {
        if (this.options.onSubmit) {
            this.options.onSubmit(event);
        }
        
        if (!this.disableSubmit) {
            var validationFailed = false;
            
            if (this.options.checkValidationOnSubmit) {
                var waitForFormField = false;
                this.formFields.each(function(formField){

                    // If the form field is loading (ie, validation is being
                    // checked, then wait for it to finish, and run the on
                    // submit handler again
                    if (formField.isLoading) {
                        // Tell the form field to run this submit handler when
                        // it's done loading
                        formField.options.onValidationSuccess = function(){
                            formField.options.onValidationSuccess = null;
                            this.onSubmitHandler(event);
                        }.bind(this);
                        waitForFormField = true;
                        return;
                    }
                    else {
                        formField.options.onValidationSuccess = null;
                    }

                    if (!formField.isValid() && !modal.isOpen()) {
                        var modalContents = new Element('p').insert(this.lang.validationFailed);

                        var okButton = new Element('input', {
                            'type': 'button',
                            'class': 'modalButton',
                            'value': this.lang.validationFailedOkButton
                        });
                        okButton.observe('click', function(){
                            modal.close();
                        });
                        modalContents.insert(new Element('p').insert(okButton));
                        
                        modal.error(modalContents, {
                            style: 'modalWarning'
                        });
                        validationFailed = true;
                        event.stop();
                    }
                }.bind(this));

                if (waitForFormField) {
                    event.stop();
                    return;
                }
            }
        
            // Clear any prefilled form fields that haven't been updated
            this.formFields.each(function(formField){
                if (formField.prefilled) {
                    formField.fieldElements.each(function(fieldElement){
                        if (fieldElement.hasClassName('prefilled')) {
                            fieldElement.value = '';
                            fieldElement.removeClassName('prefilled');
                        }
                    });                 
                }
            });
        
            if (this.options.autoAjax) {
                event.stop();
                if (!validationFailed) {
                    this.loading(true);
                    var ajaxRequestParams = this.formElement.serialize(true);
                    Object.extend(ajaxRequestParams, this.options.ajaxRequestAdditionalParameters || {});
                    new AjaxRequest(this.actionUrl, {
                        parameters: ajaxRequestParams,
                        onSuccess:function(json){
                            this.loading(false);
                            this.options.onSuccess(json);
                        }.bind(this),
                        onFailure: this.options.onFailure
                    });
                }
            }
        }
        else {
            event.stop();
        }

    },
    
    loading: function(showLoader)
    {
        if (showLoader) {
            this.loaderImage.show();
        }
        else {
            this.loaderImage.hide();
        }
    },

    disableForm: function()
    {
        var submitBtn = this.buttonsElement.down('input.submit');
        if (submitBtn) {
            submitBtn.addClassName('disabled');
        }

        this.disableSubmit = true;
    },
    
    enableForm: function()
    {
        var submitBtn = this.buttonsElement.down('input.submit');
        if (submitBtn) {
            submitBtn.removeClassName('disabled');
        }
        this.disableSubmit = false;
    }
    
};

/**
 * AjaxFormField class
 */
var AjaxFormField = function(formRowElement,options)
{
    this.config = {
        loadingDoneImage: '/images/graze/en/loader-done.gif'
    };
    
    this.options = {
        onValidationSuccess: null // On validation success handler
    };
    Object.extend(this.options, options || {});
    
    this.formRowElement = $(formRowElement);
    this.fieldElements = null;
    this.notificationElement = null;
    this.fieldName = null;
    this.notificationClassName = null;
    this.loaderImage = null;

    this.isLoading = false;

    this.prefilled = false;

    this.validationError = false;
    
    this.register();
};

AjaxFormField.prototype = {
    register: function()
    {
        this.fieldElements = this.formRowElement.select('input,textarea,select');
        
        if (this.fieldElements.length > 0) {
            this.fieldName = this.fieldElements[0].name;
        }
        
        this.notificationElement = this.formRowElement.down('.notification');
        if (this.notificationElement) {
            this.hideNotification();            
        }
        
        this.loaderImage = this.formRowElement.down('.loader');
        
        // Look for prefilled class, and add focus/blur listeners if so
        this.fieldElements.each(function(fieldElement){
            if (fieldElement.hasClassName('prefilled')) {
                this.prefilled = true;
                fieldElement.observe('focus', function(){
                    if (fieldElement.value==fieldElement.defaultValue) {
        				fieldElement.value = '';
        				fieldElement.removeClassName('prefilled');                        
                    }
                });
            }            
        }.bind(this))
    },
    
    getValue: function(elementIndex)
    {
        return this.fieldElements[elementIndex || 0].getValue();
    },

    setValue: function(val, elementIndex)
    {
        this.fieldElements[elementIndex || 0].value = val;
    },

    setNotificationMessage: function(message, className)
    {
        if (!this.notificationElement) {
            return; 
        }
        
        this.notificationElement.update(message);
        if (this.notificationClassName) {
            this.notificationElement.removeClassName(this.notificationClassName);
            this.notificationClassName = null;
        }
        if (className) {
            this.notificationElement.addClassName(className);            
        }
    },
    
    showNotification: function()
    {
        if (!this.notificationElement || this.notificationElement.visible()) {
            return;
        }
        new Effect.BlindDown(this.notificationElement,{
            duration:0.1
        })
    },
    
    hideNotification: function()
    {
        this.notificationElement.hide();
    },
    
    loading: function(showLoader)
    {
        if (!this.loaderImage) {
            return; 
        }
        
        if (showLoader) {
            this.isLoading = true;
            this.loaderImage.show();
        }
        else {
            this.isLoading = false;
            this.loaderImage.hide();
        }
    },

    /**
     * Show a tick icon and mark the field as loading complete
     */
    loadingDone: function()
    {
        if (!this.loaderImage) {
            return;
        }
        this.isLoading = false;
        this.loaderImage.writeAttribute('src',this.config.loadingDoneImage);
    },
    
    validationFailure: function(message) 
    {
        this.loading(false);
        this.setNotificationMessage(message,'formValidationError');
        this.showNotification();
        this.hasValidationError = true;
    },
    
    validationSuccess: function()
    {
        this.hideNotification();
        this.hasValidationError = false;
        this.loadingDone();
        if (this.options.onValidationSuccess) {
            this.options.onValidationSuccess();
        }
    },
    
    processValidatorResponse: function(isValid, message)
    {
        if (!isValid) {
            this.validationFailure(message);
        } else {
            this.validationSuccess();
        }
    },
    
    hasUserChanges: function()
    {
        /*
        if (this.getValue()=='') {
            return false;
        }
        */
        if (this.getValue() == this.fieldElements[0].defaultValue) {
            return false;
        }
        return true;
    },
    
    isValid: function()
    {
        if (this.hasValidationError) {
            return false;
        }
        return true;
    },

    focus: function()
    {
        if (this.fieldElements.length == 0) {
            return;
        }
        this.fieldElements[0].focus();
    },

    observeBlur: function(blurHandler)
    {
        if (this.fieldElements.length==0) {
            return;
        }
        this.fieldElements.each(function(fieldElement){
            fieldElement.observe('blur', blurHandler);
        });
    },

    observeFocus: function(focusHandler)
    {
        if (this.fieldElements.length==0) {
            return;
        }
        this.fieldElements.each(function(fieldElement){
            fieldElement.observe('focus', focusHandler);
        });
    }
};

/**
 * AjaxValidator class
 *
 * Performs a validation on a particular value against a defined
 * AJAX validator at the specified URL
 */
var AjaxFormValidator = function(options)
{
    this.options = {
        type:'ajax',
        requestUrl:null,
        ajaxAdditionalParameters:{},
        validatorFunction:null
    };
    Object.extend(this.options, options || {});
};

AjaxFormValidator.prototype = {
    validate: function(ajaxFormField, ajaxForm)
    {
        ajaxFormField.loading(true);
        switch (this.options.type) {
            // AJAX request to validate
            case 'ajax':
                var params = {value:ajaxFormField.getValue()};
                Object.extend(params,this.options.ajaxAdditionalParameters || {});
                new AjaxRequest(this.options.requestUrl,{
                    parameters:params,
                    onSuccess:function(json){
                        ajaxFormField.processValidatorResponse(json.isValid, json.message || null);
                    }
                });
                break;
                
            // Standard JS function to validate
            case 'function': 
                var validationResponse = this.options.validatorFunction(ajaxFormField.getValue());
                ajaxFormField.processValidatorResponse(validationResponse.isValid, validationResponse.message || null);
                break;
        }
    }
};
