ProspectDirector.Types.Core.Validator = Class.extend({

    init: function (options) {

        this.options = options;
        this.validate = this.options.validation;

    },

    message: function (name) {

        if (name === undefined || typeof name !== "string") {
            return this.options.message;
        }
        else {
            return this.options.message.format(name);
        }

    },

    validate: null

});

ProspectDirector.Types.Core.Validators = Class.extend({
});

ProspectDirector.Types.Core.Validators.date =
new ProspectDirector.Types.Core.Validator({
    message: "[0] is not a valid date.",
    validation: function (input) {
        return ProspectDirector.Types.Core.Validators.isDate(input.val());
    }
});

ProspectDirector.Types.Core.Validators.required =
new ProspectDirector.Types.Core.Validator({
    message: "[0] requires a value.",
    validation: function (input) {
        //TODO: write logic for this validator.
    }
});

ProspectDirector.Types.Core.Validators.integer =
new ProspectDirector.Types.Core.Validator({
    message: "[0] must be a non-decimal number.",
    validation: function (input) {
        return ProspectDirector.Types.Core.Validators.isInteger(input.val());
    }
});

ProspectDirector.Types.Core.Validators.decimal =
new ProspectDirector.Types.Core.Validator({
    message: "[0] must be a decimal number.",
    validation: function (input) {
        return ProspectDirector.Types.Core.Validators.isDecimal(input.val());
    }
});

ProspectDirector.Types.Core.Validators.isInteger = function (value) {
    return (parseFloat(value) == parseInt(value)) && !isNaN(value);
};

ProspectDirector.Types.Core.Validators.isDecimal = function (value) {
    value = parseFloat(value);
    return !isNaN(value);
};

ProspectDirector.Types.Core.Validators.isDate = function (value) {

    var aoDate,           // needed for creating array and object        
        ms,               // date in milliseconds        
        month, day, year, // (integer) month, day and year    
        separator = "/";

    // split input date to month, day and year    
    aoDate = value.split(separator);

    // array length should be exactly 3 (no more no less)    
    if (aoDate.length !== 3) {
        return false;
    }

    // define month, day and year from array (expected format is m/d/yyyy)    
    // subtraction will cast variables to integer implicitly    
    month = aoDate[0] - 1;

    // because months in JS start from 0    
    day = aoDate[1] - 0;
    year = aoDate[2] - 0;

    // test year range    
    if (year < 1000 || year > 3000) {
        return false;
    }

    // convert input date to milliseconds    
    ms = (new Date(year, month, day)).getTime();

    // initialize Date() object from milliseconds (reuse aoDate variable)    
    aoDate = new Date();
    aoDate.setTime(ms);

    // compare input date and parts from Date() object    
    // if difference exists then input date is not valid    
    if (aoDate.getFullYear() !== year ||
        aoDate.getMonth() !== month ||
        aoDate.getDate() !== day) {
        return false;
    }

    // date is OK, return true    
    return true;

};

ProspectDirector.Types.Core.FormValidation = Class.extend({

    init: function (options) {

        this.options = $.extend({
            container: $(),
            validator: null,
            validators: []
        }, options);

        this.init_validator();

    },

    init_validator: function () {

        var index = 0,
            rules = {},
            messages = {},
            input = null,
            validator = null;

        for (index = 0; index < this.options.validators.length; index++) {

            input = this.options.validators[index].input;

            validator = this.options.validators[index].validator;

            rules["rule" + index] = this.wrapValidationFunction(input, validator);

            messages["rule" + index] = $.proxy(validator.message, validator);

        }

        this.options.validator = this.options.container.kendoValidator({
            rules: rules,
            messages: messages,
            errorTemplate: "",
            validateOnBlur: false
        }).data("kendoValidator");

    },

    validate: function (include) {

        var index = 0,
            input = null,
            errorIndex = 0,
            errorsIndex = 0,
            errors = [],
            result = {
                errors: []
            },
            fieldHighlighter = new PD.Web.InvalidFieldHighlighter();

        this.options.validator._errors = {};

        include = include ? include : [];

        if (include.length === 0) {
            include = this.options.container.find(":input").toArray();
        }

        for (index = 0; index < include.length; index++) {

            input = $(include[index]);

            if (!this.options.validator.validateInput(input)) {

                errors = this.options.validator.errors().slice(errorsIndex);

                for (errorIndex = 0; errorIndex < errors.length; errorIndex++) {

                    result.errors[result.errors.length] = {
                        key: input.attr("name"),
                        message: errors[errorIndex].replace("[0]", this.getInputLabel(input))
                    };

                }

                errorsIndex = this.options.validator.errors().length;

            }

        }

        if (result.errors.length > 0) {

            fieldHighlighter.apply(result.errors, this.options.container);
            //TODO: Remove this code => ProspectDirector.Types.Core.Validation.notify(result.errors);

        }

        return result;

    },

    getInputLabel: function (input) {

        var name = input.attr("name");

        return this.options.container.find("LABEL[for=" + name + "]").text();

    },

    wrapValidationFunction: function (input, validator) {

        return $.proxy(function (field) {

            if (input.is(field)) {
                return validator.validate(field);
            }

            return true;

        }, validator);

    }

});