/**
 * Create a new Form Mail NG
 */

//* FireBug Lite X
if (!window.console || !console.firebug) {
    var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd",
                 "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
    var console = window.console = {};
    for (var i = 0; i < names.length; ++i)
        window.console[names[i]] = function() {};
}
/**/

var FormMailNG = function (id, formEle) {
    this.id = id;
    this.formEle = formEle;
    // Find each field with a name and add it to the array below
    this.fields = [];
    for (var i=0; i<this.formEle.elements.length; i++) {
        var eachField = this.formEle.elements[i];
        // Skip fields without a name
        if (!eachField.name) continue;
        // Augment the field
        eachField.onchange = function () { this.form.formMailNG.validateFields(); }
        eachField.onkeyup = function () { this.form.formMailNG.validateFields(); }
        // Add the field to the pack
        this.fields.push( eachField );
    }
    // Run the validation
    this.validateFields();
    // Add this instance to the store and to the form
    FormMailNG.instances.push(this);
    this.formEle.formMailNG = this;
    console.debug("Created FormMailNG item: ", this);
};
FormMailNG.prototype.validateFields = function () {
    var fieldErrors = {};
    // Process each field
    for (var i=0; i<this.fields.length; i++) {
        var thisField = this.fields[i];
        // Does this field need filling in?
        if (thisField.getAttribute("required") == "true" && !thisField.value) {
            fieldErrors[i] = "This field is required.";
            continue;
        }
        // Validate the data (if there is a validator)
        switch (thisField.getAttribute("validation")) {
            case "alpha" : {
                if (FormMailNGUtil.getRegexAlpha() && !FormMailNGUtil.getRegexAlpha().test(thisField.value))
                    fieldErrors[i] = "This field can only contain letters.";
                break;
            }
            case "num" : {
                if (FormMailNGUtil.getRegexNum() && !FormMailNGUtil.getRegexNum().test(thisField.value))
                    fieldErrors[i] = "This field can only contain numbers.";
                break;
            }
            case "alphanum" : {
                if (FormMailNGUtil.getRegexAlphaNum() && !FormMailNGUtil.getRegexAlphaNum().test(thisField.value))
                    fieldErrors[i] = "This field can only contain letters and numbers.";
                break;
            }
            case "email" : {
                if (!FormMailNGUtil.isEmail(thisField.value))
                    fieldErrors[i] = "Invalid Email Address.";
                break;
            }
        }
    }
    // Postprocessing of styles, titles (and messages into an array)
    var errorMessages = [];
    for (var j=0; j<this.fields.length; j++) {
        var thisField2 = this.fields[j];
        // Is this field valid?
        if (fieldErrors[j]) {
            // Set the "invalid" CSS style
            if (!FormMailNGUtil.containsCSSClass("invalid", thisField2.className)) {
                thisField2.className += " invalid";
            }
            // Set the title to the error message
            thisField2.title = fieldErrors[j];
            // Add the error message
            errorMessages.push( { field: thisField2, message: fieldErrors[j] } );
        } else {
            // Unset the "invalid" CSS style
            if (FormMailNGUtil.containsCSSClass("invalid", thisField2.className)) {
                var className = thisField2.className;
                // Remove the invalid reference
                while (className.indexOf("invalid") > -1) className = className.replace("invalid", "");
                // Clean up any spaces hanging around
                className = FormMailNGUtil.cleanSpaces(className);
                // Set the class
                thisField2.className = className;
            }
            // Remove the title
            thisField2.title = "";
        }
    }
    // Return the errors
    return errorMessages;
};
// Disable / Enable allt he form fields
FormMailNG.prototype.setDisabled = function (value) {
    for (var i=0; i<this.formEle.elements.length; i++) {
        this.formEle.elements[i].disabled = value;
    }
};
FormMailNG.prototype.submit = function () {
    // Validate the fields
    var fieldErrors = this.validateFields();
    if (fieldErrors.length > 0) {
        var errorMessage = "The following errors were found:\n\n";
        for (var i=0; i<fieldErrors.length; i++) {
            errorMessage += "["+ fieldErrors[i].field.name +"] "+ fieldErrors[i].message +"\n";
        }
        errorMessage += "\nPlease correct the above and retry.";
        // TODO: A nicer way of displaying error messages would be good?
        alert(errorMessage);
        return;
    }
    // Build the data model
    var dataModel = { id: this.id, fields: [] };
    // Process each field
    for (var j=0; j<this.fields.length; j++) {
        var thisField = this.fields[j];
        var fieldData = { name: thisField.name, dataType: thisField.getAttribute("dataType") };
        // Skip checkbox / radio fields that aren't selected
        if (thisField.type == "checkbox" || thisField.type == "radio") {
            if (!thisField.checked) continue;
        }
        // Grab the data
        switch (fieldData.dataType) {
            case "String"  : fieldData.valueString = thisField.value; break;
            case "int"     : fieldData.valuePInt = thisField.value; break;
            case "long"    : fieldData.valuePLong = thisField.value; break;
            case "boolean" : fieldData.valuePBoolean = thisField.value; break;
            case "Boolean" : fieldData.valueBoolean = thisField.value; break;
            case "Date"    : fieldData.valueDate = thisField.value; break;
        }
        dataModel.fields.push(fieldData);
    }
    // Fire off to DWR
    var thisVar = this;
    // Disable the form
    this.setDisabled(true);
    // TODO: Show a spinner with message
    FormMailDWR.submit( dataModel, { callback: function (successHTML) {
        // Delay the execution by a very small period so that DWR doesn't catch any exception
        setTimeout( function () {
            // TODO: Hide the spinner
            // Show the Success HTML
            thisVar.formEle.parentNode.innerHTML = successHTML;
        }, 50 );
    }, errorHandler: function(errorString, exception) {
        // TODO: Hide the spinner
        // Re-enable the form
        thisVar.setDisabled(false);
        // Show the message
        console.error("DWR Transaction Failure: "+ errorString, exception);
        alert("An error occurred, please contact an Administrator.\n\n"+ errorString);
    } } );
};

// Form Store
FormMailNG.instances = [];
FormMailNG.getInstance = function (targetEle, recursed) {
    if (!targetEle) return false;
    // Does this element have a FormMailNG instance?
    if (targetEle.formMailNG) return targetEle.formMailNG;
    // Check the parent
    if (targetEle.parentNode) {
        var result = FormMailNG.getInstance(targetEle.parentNode, true);
        // Only return a value (value or false), if I've recursed
        if (result || recursed) return result;
    }
    // I should have found something by now
    console.error("Unable to find a FormMailNG instance against the hierarchy starting with: ", targetEle);
    return false;
};

// Initalising Stack
FormMailNG.initStack = [];
FormMailNG.init = function () {
    // Reset the system
    FormMailNG.instances = [];
    // Initalise
    for (var i=0; i<FormMailNG.initStack.length; i++) {
        new FormMailNG( FormMailNG.initStack[i].id, FormMailNG.initStack[i].formEle );
    }
};

// Util Methods
var FormMailNGUtil = {
    // These functions will replace themselves with the value
    regexSupported: function () {
        var supported = false;
        if (window.RegExp) {
            var tempStr = "a";
            var tempReg = new RegExp(tempStr);
            if (tempReg.test(tempStr)) supported = true;
        }
        FormMailNGUtil.regexSupported = function () { return supported; };
        return supported;
    },
    getRegexAlpha: function () {
        if (!FormMailNGUtil.regexSupported()) return false;
        var regex = new RegExp("^[0-9]*$");
        FormMailNGUtil.getRegexAlphaNum = function () { return regex; };
        return regex;
    },
    getRegexNum: function () {
        if (!FormMailNGUtil.regexSupported()) return false;
        var regex = new RegExp("^[0-9]*$");
        FormMailNGUtil.getRegexAlphaNum = function () { return regex; };
        return regex;
    },
    getRegexAlphaNum: function () {
        if (!FormMailNGUtil.regexSupported()) return false;
        var regex = new RegExp("^[a-zA-Z0-9]*$");
        FormMailNGUtil.getRegexAlphaNum = function () { return regex; };
        return regex;
    },
    // These are static functions
    containsCSSClass: function (needle, haystack) {
        return (haystack == needle || haystack.indexOf(" "+ needle +" ") > -1 || haystack.indexOf(needle +" ") == 0 ||
            haystack.indexOf(" "+ needle) == haystack.length - (" "+ needle).length);
    },
    cleanSpaces: function (string) {
        var boom = string.split(" "); string = "";
        for (var j=0; j<boom.length; j++) {
            if (boom[j].length) {
                if (string.length) string += " ";
                string += boom[j];
            }
        }
        return string;
    },
    isEmail: function (str) {
        // Check to see if it contains a space
        if (str.indexOf(" ") > -1) return false;
        // If regular expressions aren't supported
        if (!FormMailNGUtil.regexSupported())
            return (str.indexOf(".") > 2) && (str.indexOf("@") > 0);
        // If they are
        var r1 = new RegExp("(@.*@)|(\\.\\.)|(@\\.)|(^\\.)");
        var r2 = new RegExp("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$");
        return (!r1.test(str) && r2.test(str));
    }
}

