(function($) {
    if (window.formple)
        return;

    window.formple = {
        configKey: "formple-"
        , validationKey: "-validation"
        , compareKey: "compare-"
        , maxLengthKey: "maxlength-"
        , requiredIfKey: "requiredif-"
        , vlad: {
            /* validators */
            IsComplete: function(f) {
                f = $(f);
                return (f.is(":not(:checkbox,:radio)") && f.val().length > 0) || f.is(":checkbox:checked, :radio:checked");
            }
            , IsOnlyDigits: function(s) {
                // Only digits
                return /^\d*$/.test(s);
            }
            , IsOnlyAlpha: function(s) {
                // Only characters a to z
                return /^[A-Za-z]*$/.test(s);
            }
            , IsValidName: function(s) {
                // Only name valid characters
                return /^[-A-Za-z' ]*$/.test(s);
            }
            , IsValidPhoneNumber: function(s) {
                // Optional +XX followed by an optional space, followed by an optional bracketed set of numbers, followed by any combination of spaces and numbers
                return /^\+?\d* ?(\(\d*\))?[ \d]*$/.test(s);
            }
            , IsValidUKPostcode: function(s) {
                return /^([a-zA-Z]{1,2}\d{1,2}[a-zA-Z]?|[Gg][Ii][Rr]) ?\d[a-zA-Z]{2}$/.test(s);
            }
            , IsValidEmailAddress: function(s) {
                return /^[a-z0-9][^\(\)\<\>\@\,\;\:\\\"\[\]]*\@[a-z0-9][a-z0-9\-\.]*\.[a-z]{2,}$/i.test(s); // thanks to visibone browser book ?+ À-ßà-ÿ ???
            }
            , IsCurrency: function(s) {
                return /^[£$€]?\d+(?:[\.\;\:]\d{1,2})?$/i.test(s); // (\d{1,3}\,)?(\d{3}\,)? commas ??
            }
            , IsDate: function(date) {
                var intDay, intMonth, intYear, re, re1, aryParts;
                re = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/;

                if ($.trim(date).length == 0 || !re.test(date)) {
                    return false;
                }
                else {
                    re = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/;
                    re1 = /^0/;
                    aryParts = re.exec(date);
                    intDay = parseInt(aryParts[1].replace(re1, ''));
                    intMonth = parseInt(aryParts[2].replace(re1, ''));
                    intYear = parseInt(aryParts[3].replace(re1, ''));
                    re = /^0/;

                    function newDate(dteYear) {
                        this.m1 = this.m3 = this.m5 = this.m7 = this.m8 = this.m10 = this.m12 = 31;
                        this.m4 = this.m6 = this.m9 = this.m11 = 30;
                        this.m2 = (dteYear % 4 == 0) ? ((dteYear % 100 == 0) ? ((dteYear % 1000 == 0) ? 29 : 28) : 29) : 28;
                    }

                    dteDate = new newDate(intYear);
                    return (intDay <= dteDate['m' + intMonth.toString().replace(re, '')]);
                }
            }
            , IsValidFile: function(s) {
                // allow pdf, word, text, xl, csv, images
                return /.+\.(txt|pdf|doc|docx|xls|xlsx|csv|gif|jpg|jpeg|png)$/.test(s);
            }
        }
        , validate: function(frm) {
            // set up err array
            var errors = [];
            // get all active, i.e. visible, non file inputs in this form
            var fields = $(":input:not(:file):visible", frm);
            // get validation fields
            $(":input[name$=" + formple.validationKey + "]", frm).each(function(idx) {
                // get related field
                var name = this.name.replace(formple.validationKey, "");
                var field = fields.filter(function(idx) { return $(this).attr("name") == name; });
                //console.log(name + ": " + field.length);
                if (field.length > 0 && $.trim(this.value).length > 0) {
                    // validate accordingly
                    var validation = this.value.split(",");
                    for (var idx = 0; idx < validation.length; idx++) {
                        switch ($.trim(validation[idx].toLowerCase())) {
                            case "required":
                                if (!formple.vlad.IsComplete(field)) {
                                    errors.push("The field " + name + " is required.");
                                }
                                break;
                            case "email":
                                if (formple.vlad.IsComplete(field) && !formple.vlad.IsValidEmailAddress(field.val())) {
                                    errors.push("The field " + name + " is not a valid email address.");
                                }
                                break;
                            case "ukpostcode":
                                if (formple.vlad.IsComplete(field) && !formple.vlad.IsValidUKPostcode(field.val())) {
                                    errors.push("The field " + name + " is not a valid UK postcode.");
                                }
                                break;
                            case "phone":
                                if (formple.vlad.IsComplete(field) && !formple.vlad.IsValidPhoneNumber(field.val())) {
                                    errors.push("The field " + name + " is not a valid phone number, you may only enter numbers, parentheses, + and spaces.");
                                }
                                break;
                            case "name":
                                if (formple.vlad.IsComplete(field) && !formple.vlad.IsValidName(field.val())) {
                                    errors.push("The field " + name + " is not a valid name, you may only enter letters, apostrophes, spaces and hyphens.");
                                }
                                break;
                            case "currency":
                                if (formple.vlad.IsComplete(field) && !formple.vlad.IsCurrency(field.val())) {
                                    errors.push("The field " + name + " is not valid currency, it may have a currency symbol (\u00A3, $ or \u20AC), be numeric and may have 1 or 2 decimal places.");
                                }
                                break;
                            case "date":
                                if (formple.vlad.IsComplete(field) && !formple.vlad.IsDate(field.val())) {
                                    errors.push("The field " + name + " is not a valid date, it must be in the format dd/mm/yyyy and contain no spaces.");
                                }
                                break;
                            case "numbers":
                                if (formple.vlad.IsComplete(field) && !formple.vlad.IsOnlyDigits(field.val())) {
                                    errors.push("The field " + name + " is not valid, you may only enter numbers.");
                                }
                                break;
                            case "letters":
                                if (formple.vlad.IsComplete(field) && !formple.vlad.IsOnlyAlpha(field.val())) {
                                    errors.push("The field " + name + " is not valid, you may only enter letters.");
                                }
                                break;
                            default:
                                var valType = $.trim(validation[idx]);
                                // check for compare-
                                if (valType.toLowerCase().indexOf(formple.compareKey.toLowerCase()) == 0) {
                                    if (formple.vlad.IsComplete(field)) {
                                        var targetName = valType.replace(formple.compareKey, "");
                                        var targetField = fields.filter(function(idx) { return $(this).attr("name") == targetName; });
                                        if (targetField.length == 1 && (field.val() != targetField.val())) {
                                            errors.push("The entries for the fields " + targetName + " and " + name + " do not match.");
                                        }
                                    }
                                }
                                // check for requiredif-<Field Name>=<Value>
                                else if (valType.toLowerCase().indexOf(formple.requiredIfKey.toLowerCase()) == 0 && valType.indexOf("=") > -1) {
                                    // get name/value combo
                                    var rule = valType.replace(formple.requiredIfKey, "").split("=");
                                    var parent = fields.filter(function(idx) { return $(this).attr("name") == rule[0]; });
                                    if (parent.length == 1 && $.trim(parent.val()) == rule[1] && !formple.vlad.IsComplete(field)) {
                                        errors.push("The field " + name + " is required.");
                                    }
                                }
                                break;
                        }
                    }
                }
            });
            // replace any captcha related errors with "spam prevention"
            for (var idx = 0; idx < errors.length; idx++) {
                if (errors[idx].indexOf("captcha response") > -1)
                    errors[idx] = errors[idx].replace("captcha response", "spam prevention");
            }
            // validate file inputs
            $("input:file", frm).each(function(idx) {
                if (formple.vlad.IsComplete(this) && !formple.vlad.IsValidFile($(this).val())) {
                    errors.push("The file selected for " + this.name + " is not valid, you may only select text, CSV, PDF, MS Word, MS XL, GIF, Jpeg or PNG files.");
                }
            });

            // check for errors
            if (errors.length > 0) {
                // build and show error message
                var errMsg = "Oops!\n\nThe following errors have been found with your entries:\n";
                for (var idx = 0; idx < errors.length; idx++) {
                    errMsg += "\n- " + errors[idx];
                }
                errMsg += "\n\nPlease change your entries and try again."
                alert(errMsg);
                // stop form submitting
                return false;
            } else {
                // go go go!
                return true;
            }
        }
        , debug: false
    };
    if (location.search.indexOf("formple=debug") > -1) {
        formple.debug = true;
    }
})(jQuery);

$(function() {
    var forms = $("form.formple").submit(function(e) {
        return formple.validate(this);
    });
    // text area maxlength
    $("input:hidden", forms)
        .filter(function(idx) { return $(this).val().indexOf(formple.maxLengthKey) > -1; })
        .each(function(idx) {
            var targetName = this.name.replace(formple.validationKey, "");
            var matches = this.value.match(/.*maxlength-(\d+)/);
            if (matches.length > 0) {
                var maxLength = parseInt(matches[1], 10);
                var tarea = $("textarea", this.form).filter(function(idx) { return $(this).attr("name") == targetName; });
                if (tarea.length > 0) {
                    tarea.each(function(idx) {
                        var maxLengthLabel = $("<div><span>" + (maxLength - $(this).val().length) + "</span> characters left&nbsp;</div>")
                                            .css({
                                                fontSize: "0.8em"
                                                , width: $(this).outerWidth()
                                                , color: "#fff"
                                                , textAlign: "right"
                                                , backgroundColor: "#7F9DB9"
                                                , marginTop: "-2px"
                                            });
                        $(this)
                        .keyup(function(e) {
                            var val = $(this).val();
                            if (val.length > maxLength) {
                                $(this).val(val.substring(0, maxLength));
                            }
                            maxLengthLabel.find("span").text(maxLength - $(this).val().length);
                        })
                        .after(maxLengthLabel);
                    });
                }
            }
        });

    if (window.formple.debug == true) {
        // load debugger
        var formples = $("form.formple");
        if (formples.length == 0) {
            alert("No formple forms were found!\n\nPlease make sure all formple forms have the class \"formple\".");
            return;
        }
        formples.each(function(idx) {
            // add debug div
            var offset = $(this).offset();
            var dbg = $("<div />", {
                "class": "formple-debug"
                            , css: {
                                backgroundColor: "#333"
                                , fontSize: "100%"
                                , fontFamily: "Tahoma"
                                , padding: "1px 10px"
                            }
                        }).append(
                            $("<h1 />", {
                        css: { color: "#fff" }
                                , text: "formple: Debug: \u201C" + $(":input[name=formple-Name]", this).val() + "\u201D"
                            })
                        ).append(
                            $("<hr />", {
                                css: { color: "#fff", backgroundColor: "#fff" }
                            })
                        ).append(
                            $("<h2 />", {
                                css: { color: "#fff" }
                                , text: "Configuration"
                            })
                        ).append(
                            $("<ul />", {
                                "class": "formple-config"
                                , css: { color: "#fff" }
                            })
                        ).append(
                            $("<h2 />", {
                                css: { color: "#fff" }
                                , text: "Field configuration"
                            })
                        ).append(
                            $("<ul />", {
                                "class": "formple-fields"
                                , css: { color: "#fff" }
                            })
                        ).append(
                            $("<h2 />", {
                                css: { color: "#fff" }
                                , text: "Field validation rules"
                            })
                        ).append(
                            $("<ul />", {
                                "class": "formple-validation"
                                , css: { color: "#fff" }
                            })
                        );
            var configErrBucket = $("ul.formple-config", dbg).empty();
            // Validate form:
            var frm = $(this);
            if (frm.attr("action").indexOf("/other/formple/formple.handler.asp") == -1) {
                configErrBucket.append($("<li />", { text: "The form action attribute must point to \"/other/formple/formple.handler.asp\".", css: { fontSize: "150%", color: "#f33"} }));
            }
            if (frm.attr("method").toLowerCase() != "post") {
                configErrBucket.append($("<li />", { text: "The form method attribute must be set to \"post\".", css: { fontSize: "150%", color: "#f33"} }));
            }
            if (frm.attr("enctype").toLowerCase() != "multipart/form-data") {
                configErrBucket.append($("<li />", { text: "The form enctype attribute must be set to \"multipart/form-data\".", css: { fontSize: "150%", color: "#f33"} }));
            }
            // Validate configuration:
            var configFields = $("input:hidden[name^=" + formple.configKey + "]", frm);
            if (configFields.length > 0) {
                // Name
                var test = $("input[name=" + formple.configKey + "Name]", frm);
                if (test.length != 1 || test.val().length == 0) {
                    configErrBucket.append($("<li />", { text: "The name field (formple-Name) is missing.", css: { fontSize: "150%", color: "#f33"} }));
                }
                // Email config
                test = $("input[name=" + formple.configKey + "EmailFrom], input[name=" + formple.configKey + "EmailToField], input[name=" + formple.configKey + "EmailBody]", frm);
                if (test.length > 0) {
                    // Part of the Email config is set so validate it
                    if (test.length != 3) {
                        // something's missing: find out
                        if ($("input[name=" + formple.configKey + "EmailFrom]", frm).length == 0) {
                            configErrBucket.append($("<li />", { text: "The email from address field (formple-EmailFrom) is missing.", css: { fontSize: "150%", color: "#f33"} }));
                        }
                        if ($("input[name=" + formple.configKey + "EmailToField]", frm).length == 0) {
                            configErrBucket.append($("<li />", { text: "The email to field field (formple-EmailToField) is missing.", css: { fontSize: "150%", color: "#f33"} }));
                        }
                        if ($("input[name=" + formple.configKey + "EmailBody]", frm).length == 0) {
                            configErrBucket.append($("<li />", { text: "The email body field (formple-EmailBody) is missing.", css: { fontSize: "150%", color: "#f33"} }));
                        }
                    } else {
                        // check the to field exists
                        var name = $("input[name=" + formple.configKey + "EmailToField]", frm).val();
                        if ($("input:not(:hidden)", frm).filter(function(idx) { return $(this).attr("name") == name; }).length == 0) {
                            configErrBucket.append($("<li />", { text: "The field specified as the email to field (in formple-EmailToField's value) is missing.", css: { fontSize: "150%", color: "#f33"} }));
                        }
                        // check the from address is valid
                        if (!formple.vlad.IsValidEmailAddress($("input[name=" + formple.configKey + "EmailFrom]", frm).val())) {
                            configErrBucket.append($("<li />", { text: "The email address specified as the email from address (in formple-EmailFrom's value) is invalid.", css: { fontSize: "150%", color: "#f33"} }));
                        }
                    }
                }
            }
            if (configErrBucket.children().length == 0) {
                configErrBucket.append($("<li />", { text: "The form is configured correctly.", css: { fontSize: "150%", color: "#3f3"} }));
            }

            // check for fields with the same name:
            var fieldConfig = $("ul.formple-fields", dbg);
            var names = [];
            var inputs = $(":input", this);
            inputs.each(function(idx) {
                var name = $(this).attr("name");
                var type = $(this).attr("type");
                if (type != "radio" && $.inArray(name, names) > -1) {
                    fieldConfig.append($("<li />", { text: "The field name \u201C" + name + "\u201D has been used more than once.", css: { fontSize: "150%", color: "#f33"} }));
                }
                else {
                    names.push(name);
                }
            });
            if (fieldConfig.children().length == 0) {
                fieldConfig.append($("<li />", { text: "All fields have been configured correctly.", css: { fontSize: "150%", color: "#3f3"} }));
            }
            
            // Display validation rules:
            var ruleBucket = $("ul.formple-validation", dbg);
            var validation = $("input:hidden[name$=" + formple.validationKey + "]", frm);
            validation.each(function(idx) {
                if ($.trim($(this).val()).length > 0)
                    ruleBucket.append($("<li />", { text: "The field " + $(this).attr("name").replace(formple.validationKey, "") + " has the following validation: " + $(this).val().replace(",", ", ") + ".", css: { fontSize: "150%", color: "#fff"} }));
            });

            // Display
            $(this).prepend(dbg);
        });
    }
});
