/**
 * 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") {
            // Radio buttons or checkboxes - at least one of them can be selected
            if (thisField.type == "radio" || thisField.type == "checkbox") {
                // Was one found?
                var countSelected = 0;
                // Store last field position in this group
                var lastFieldPos = 0;
                // Count the selected options that have the same name
                for (var k=0; k<this.fields.length; k++) {
                    if (this.fields[k].name == thisField.name)
                    {
                        if (this.fields[k].checked)
                        {
                            countSelected++;
                        }
                        lastFieldPos = k;
                    }
                }
                // None found?
                if (countSelected == 0) {
                    // Add 'required' warning to last occurring field element in this group.
                    fieldErrors[lastFieldPos] = "Dieses Feld ist erforderlich.";
                    continue;
                }
            } else
            // Anything else must have a value
            if (!thisField.value) {
                fieldErrors[i] = "Dieses Feld ist erforderlich.";
                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] = "Dieses Feld kann nur Buchstaben enthalten.";
                break;
            }
            case "num" : {
                if (FormMailNGUtil.getRegexNum() && !FormMailNGUtil.getRegexNum().test(thisField.value))
                    fieldErrors[i] = "Dieses Feld kann nur Zahlen enthalten.";
                break;
            }
            case "alphanum" : {
                if (FormMailNGUtil.getRegexAlphaNum() && !FormMailNGUtil.getRegexAlphaNum().test(thisField.value))
                    fieldErrors[i] = "Dieses Feld kann nur Buchstaben und Zahlen enthalten.";
                break;
            }
            case "email" : {
                if (!FormMailNGUtil.isEmail(thisField.value))
                    fieldErrors[i] = "Ung&uuml;ltige E-Mail Adresse";
                break;
            }
            case "phone" : {
                if (!FormMailNGUtil.isPhone(thisField.value))
                    fieldErrors[i] = "Bitte eine g&uuml;ltige Telefonnummer eingeben";
                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];
        // Do I already have an error div for this item?
        if (!thisField2.errorDiv) {
            // Create the element
            thisField2.errorDiv = document.createElement("span");
            if(thisField2.id != null && thisField2.id != "")
            {
                thisField2.errorDiv.id = thisField2.id + "_error";
            }
            else
            {
                thisField2.errorDiv.id = thisField2.name + "_error";
            }
            thisField2.errorDiv.className = "fmngError "+ thisField2.className;
            // Inject it to the parent, immediately after the this field (by inserting it before this field, and flipping them)
            // or after the associated label (if it exists) - label must directly follow field and reference field id
            // via the 'for' attribute
            var targetElem = thisField2.nextSibling;
            if(targetElem != null && targetElem.tagName == "LABEL" && targetElem.getAttribute("for") == thisField2.id)
            {
                thisField2.parentNode.insertBefore(thisField2.errorDiv, targetElem.nextSibling );
            }
            else
            {
                thisField2.parentNode.insertBefore( thisField2.errorDiv, thisField2 );
                thisField2.parentNode.insertBefore( thisField2, thisField2.errorDiv );
            }
        }
        // Is this field valid?
        if (fieldErrors[j]) {
            // Set the "invalid" CSS style
            if (!FormMailNGUtil.containsCSSClass("invalid", thisField2.className)) {
                thisField2.className += " invalid";
            }
            if (!FormMailNGUtil.containsCSSClass("invalid", thisField2.errorDiv.className)) {
                thisField2.errorDiv.className += " invalid";
            }
            // Set the title to the error message
            thisField2.title = fieldErrors[j];
            thisField2.errorDiv.innerHTML = 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;
            }
            if (FormMailNGUtil.containsCSSClass("invalid", thisField2.errorDiv.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.errorDiv.className = className;
            }
            // Remove the message
            thisField2.title = "";
            thisField2.errorDiv.innerHTML = "";
        }
    }
    // 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 = "Die folgenden Fehler wurden gefunden:\n\n";
        for (var i=0; i<fieldErrors.length; i++) {
            errorMessage += "["+ fieldErrors[i].field.name +"] "+ fieldErrors[i].message +"\n";
        }
        errorMessage += "\nBitte korrigieren und nochmal versuchen.";
        // 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
            var parent = thisVar.formEle.parentNode;
            parent.innerHTML = successHTML;     
            // Process any javascript in the response // TODO: check any security implications of this
            var scripts = parent.getElementsByTagName("script");
            for (var i=0; i<scripts.length; i++) {
                eval( scripts[i].text );
            }
        }, 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));
    },
    isPhone: function(str) {
        /*
         * The phone number regular expression accepts phone number in both:
         * local format (eg. 02 1234 5678 or 123 123 4567)
         * or international format (eg. +61 (0) 2 1234 5678 or +1 123 123 4567).
         * It also accepts an optional extention of up to five digits prefixed
         * by x or ext (eg. 123 123 4567 x89).
         * (http://javascript.about.com/library/blre.htm)
         */
        var phoneRegEx = /^((\+\d{1,3}(-| )?\(?\d\)?(-| )?\d{1,7})|(\(?\d{2,6}\)?))(-| )?(\d{3,4})(-| )?(\d{4})(\s)?((x|ext)(\s)?\d{1,5}){0,1}$/
        // A string will match this if it has 6 or more of the same digits in a row
        var repeatDigit = new RegExp(".*([\\d])\\1{5}.*");
        // Matches strings with 1234567 anywhere in them
        var consecutiveDigit = new RegExp(".*1234567.*");
        // not valid part of phone number
        var invalidSequence = new RegExp("000|555|111");

        // Check to see if it is empty
        if (str.replace(/^\s+|\s+$/g,"").length <= 0) return false;

        var temp = str.replace(/[^\d]/g, '');
        if (invalidSequence.test(temp.substring(0, 3)) ||
            invalidSequence.test(temp.substring(3, 6)) ||
            consecutiveDigit.test(temp) ||
            repeatDigit.test(temp) ||
            !phoneRegEx.test(str))
        {
            return false;
        }

        return true;
    }
}

