; (function (trv, $, window, document, undefined) {
    "use strict";
    
    trv.ParameterTypes = {
        Integer: "System.Int64",
        Float: "System.Double",
        String: "System.String",
        DateTime: "System.DateTime",
        Boolean: "System.Boolean"
    };
    
    var validator = new Validator();

    function Validator() {
        function validateValues(text, validateCallback) {
            var values = text.split("\n");
            var isCorrect = true;
            $.each(values, function (i, v) {
                if (!validateCallback(v)) {
                    isCorrect = false;
                    return false;
                }
            });
            return isCorrect;
        };

        return {
            isBoolean: function(value) {
                var v = $.trim(value).toLowerCase();
                return (v === "true" || v === "false");
            },

            isFloat: function(value) {
                return !isNaN(parseFloat(value));
            },

            isInteger: function(value) {
                return (/^\-?([0-9]+)$/.test(value));
            },

            onKeyPressValidate: function onKeyPressValidate(e, validatonType) {
                if (e.charCode == 13) {
                    return true;
                }
                var text = e.target.value + String.fromCharCode(e.charCode);
                return validateValues(text, validatonType);
            },

            onPasteValidate: function onPasteValidate(e, validatonType) {
                var pastedText = undefined;
                if (window.clipboardData && window.clipboardData.getData) { // IE
                    pastedText = window.clipboardData.getData('Text');
                } else if (e.clipboardData && e.clipboardData.getData) {
                    pastedText = e.clipboardData.getData('text/plain');
                }
                return validateValues(pastedText, validatonType);
            }
        };
    };

    var defaultOptions = {
        templates: [
            {
                /** Editor with Available Values  */
                match: function (parameter) {
                    return Boolean(parameter.availableValues);
                },

                html: function () {
                    return '<div class="listview"></div>';
                },

                init: function ($placeholder, parameter, valueChangeCallback, $error, $header) {
                    var value = parameter.value,
                        availableValues = parameter.availableValues,
                        isMultivalue = parameter.multivalue
                    ;

                    var listView = $placeholder
                        .find(".listview")
                        .kendoListView({
                            dataSource: { data: availableValues },
                            selectable: parameter.multivalue ? false : true,
                            //navigatable: true,
                            template: "<div class='listviewitem'>${name}</div>",
                        });

                    function updateParameter(selection) {

                        if ($error)
                            $error.hide();

                        var values = $.map(selection, function (item) {
                            return availableValues[$(item).index()].value;
                        });

                        if (!isMultivalue) {
                            values = values[0];
                        }

                        if (listView.oldValues != values) {
                            listView.oldValues = values;
                            valueChangeCallback(parameter, values);
                        }
                    }

                    listView
                        .data("kendoListView")
                        .bind("change", function (e) {
                            var selection = this.select();
                            updateParameter(selection);
                        });

                    if (parameter.multivalue) { // Multi Value


                        var children = listView.children();

                        if (!(value instanceof Array)) {
                            value = [value];
                        }

                        listView.oldValues = value;

                        $.each(availableValues, function (i, av) {
                            $.each(value, function (j, v) {
                                if (v == av.value) {
                                    $(children[i]).addClass('k-state-selected');
                                    return false;
                                }
                                return true;
                            });
                        });

                        var allSelected = "none";
                        $.each(children, function (i, c) {
                            if (!$(children[i]).hasClass('k-state-selected')) {
                                allSelected = "all";
                                return false;
                            }
                        });

                        var $selectAllButton =
                            $("<a class='trv-parameter-select-all k-link' href='#'>all</a>")
                                .on("click",
                                    function (e) {

                                        var children =
                                            listView.data("kendoListView").element.children();

                                        var fn = null;

                                        if ($selectAllButton.html() === "all") {

                                            fn = $.fn.addClass;

                                            $selectAllButton.html("none");

                                        }

                                        else {

                                            fn = $.fn.removeClass;

                                            $selectAllButton.html("all");
                                        }

                                        $.each(children, function (i, child) {
                                            fn.call($(child), ('k-state-selected'));
                                        });

                                        updateParameters();

                                    })
                                .appendTo($header)
                                .html(allSelected);

                        var updateParameters = function() {
                            $(this).toggleClass('k-state-selected');

                            var allSelected = "none";
                            $.each(children, function(i, c) {
                                if (!$(children[i]).hasClass('k-state-selected')) {
                                    allSelected = "all";
                                    return false;
                                }
                            });
                            $selectAllButton.html(allSelected);

                            updateParameter(listView.find('.k-state-selected'));
                        };

                        listView.on(
                            "click",
                            ".listviewitem",
                            updateParameters);


                    } else { // Single value
                        var lv = listView.data("kendoListView");
                        var lvChildren = lv.element.children();
                        listView.oldValues = value;
                        $.each(availableValues, function (i, av) {
                            if (value == av.value) {
                                lv.select(lvChildren[i]);
                                return false;
                            }
                            return true;
                        });
                    }
                }
            },

            {
                /** Multi-value with NO AvailableValues value editor */
                match: function (parameter) {
                    return Boolean(parameter.multivalue);
                },

                html: function () {
                    return '<div><textArea class="k-textbox" style="width: 100%; resize: none;"></textArea></div>';
                },

                init: function ($placeholder, parameter, valueChangeCallback, $error, $header) {
                    var text = "";
                    var value = parameter.value;

                    if (value instanceof Array) {
                        var lastIndex = value.length - 1;
                        $.each(value, function (i, v) {
                            text += v;
                            if (i < lastIndex) {
                                text += "\r\n";
                            }
                        });
                    }

                    var $textArea =
                        $placeholder.find(".k-textbox");

                    $textArea.val(text);

                    var validateValue = null;

                    switch (parameter.type) {
                        case trv.ParameterTypes.Integer:
                            validateValue = validator.isInteger;
                            break;
                        case trv.ParameterTypes.Float:
                            validateValue = validator.isFloat;
                            break;
                    }

                    $textArea.on("change", function () {
                        var isValid = (validateValue) ? validateValue(this.value) : true;

                        if ($error) {
                            (isValid ? $.fn.hide : $.fn.show)
                                .call($error);
                        }

                        valueChangeCallback(parameter, this.value.split("\n"));
                    });

                    if (null != validateValue) {
                        $textArea.on("keypress", function (e) { return validator.onKeyPressValidate(e, validateValue); });
                        $textArea.on("paste", function (e) { return validator.onPasteValidate(e, validateValue); });
                    }
                }
            },

            {
                /** DateTime value editor */
                match: function (parameter) {
                    return parameter.type === trv.ParameterTypes.DateTime;
                },

                html: function () {
                    return '<input data-role="dateTimePicker" data-bind="value: value" style="width: 100%;" />';
                },

                init: function ($placeholder, parameter, valueChangeCallback, $error, $header) {

                    function validateValue() {
                        var isValid = (this.value || parameter.allowNull);

                        (isValid ? $.fn.hide : $.fn.show).call($error);
                    }

                    function valueChanged() {
                        if ($error)
                            $error.hide();

                        if (valueChangeCallback) {
                            valueChangeCallback(parameter, this.value());
                        }

                    };

                    var datePicker = $placeholder
                        .find("input[data-role='dateTimePicker']")
                        .kendoDatePicker({
                            format: "dd MMMM yyyy",
                            change: valueChanged
                        });

                    datePicker.on("keyup", function (e) {
                        return validateValue(this.value);
                    });

                    datePicker
                        .data("kendoDatePicker")
                        .value(new Date(parameter.value));
                }
            },

            {
                /** Integer value editor */
                match: function (parameter) {
                    return parameter.type === trv.ParameterTypes.Integer;
                },

                html: function () {
                    return '<input type="number" class="k-textbox" data-bind="value: value" style="width: 100%;" />';
                },

                init: function ($placeholder, parameter, valueChangeCallback, $error, $title) {

                    kendo.bind($placeholder, parameter);

                    var $input = $placeholder.find("input[type='number']");

                    function validateValue(value) {

                        var isValid = validator.isInteger(value);

                        if (isValid && $error !== undefined)
                            $error.hide();

                        return isValid;
                    }

                    $input.on("change", function (e) {
                        if (valueChangeCallback &&
                            validateValue(this.value)) {
                            valueChangeCallback(parameter, this.value);
                        }
                    });

                    $input.on("keypress", function (e) { return validator.onKeyPressValidate(e, validateValue); });
                    $input.on("paste", function (e) { return validator.onKeyPressValidate(e, validateValue); });
                    $input.on("keyup", function (e) { if (!this.value && !parameter.allowNull) $error.show(); });
                },
            },

            {
                /** Double value editor */
                match: function (parameter) {
                    return parameter.type === trv.ParameterTypes.Float;
                },

                html: function () {
                    return '<input type="number" class="k-textbox" data-bind="value: value" style="width: 100%;" />';
                },

                init: function ($placeholder, parameter, valueChangeCallback, $error, $title) {

                    kendo.bind($placeholder, parameter);

                    function validateValue(value) {
                        var isValid = validator.isFloat(value);

                        if ($error !== undefined && isValid)
                            $error.hide();

                        return isValid;
                    }

                    var $input = $placeholder.find("input[type='number']");

                    $input.on("change", function (e) {
                        if (valueChangeCallback
                            && validateValue(this.value)) {
                            valueChangeCallback(parameter, parseFloat(this.value));
                        }
                    });

                    $input.on("keypress", function (e) { return validator.onKeyPressValidate(e, validateValue) });
                    $input.on("paste", function (e) { return validator.onPasteValidate(e, validateValue) });
                    $input.on("keyup", function (e) { if (!this.value && !parameter.allowNull) $error.show(); });
                }
            },

            {
                /** Boolean value editor */
                match: function (parameter) {
                    return parameter.type === trv.ParameterTypes.Boolean;
                },

                html: function () {
                    return '<input type="checkbox" data-bind="checked: value" />';
                },

                init: function ($placeholder, parameter, valueChangeCallback, $error, $header) {

                    var $input = $placeholder.find("input[type='checkbox']");

                    kendo.bind($placeholder, parameter);

                    $input.on("change", function (e) {

                        if ($error != undefined)
                            $error.hide();

                        if (valueChangeCallback) {
                            valueChangeCallback(parameter, this.checked);
                        }
                    });
                }
            },

            {
                /** String value editor */
                match: function (parameter) {
                    return parameter.type === trv.ParameterTypes.String;
                },

                html: function () {
                    return '<input type="text" class="k-textbox" data-bind="value: value" style="width: 100%;" />';;
                },

                init: function ($placeholder, parameter, valueChangeCallback, $error, $title) {

                    var $input = $placeholder.find("input[type='text']");

                    function validateValue(value) {
                        var isValid = (parameter.allowBlank || value !== "");

                        if ($error !== undefined && isValid)
                            $error.hide();

                        return isValid;
                    }

                    kendo.bind($placeholder, parameter);

                    $input.on("change", function (e) {

                        var isValidValue = validateValue(this.value);

                        if (valueChangeCallback && isValidValue) {
                            valueChangeCallback(parameter, this.value);
                        }
                    });
                }
            },

            {
                /** Any other */
                html: function () {
                    return '<div data-bind="text: text"></div>';
                },

                init: function ($placeholder, parameter, valueChangeCallback, $error, $header) {
                    kendo.bind($placeholder, parameter);
                },

                match: function (parameter) {
                    return true;
                }
            }

        ]
    };

    var ParametersArea = function (placeholder, options) {
        options = $.extend({}, defaultOptions, options);

        var controller = options.controller;

        if (!controller) {
            throw "No controller (telerikReporting.reportViewerController) has been specified.";
        }

        var recentParameterValues, parameters, internalReportSourceChange;

        var $placeholder = $(placeholder);

        var $wrapper = $("<div class='trv-parameters-area'></div>"),
            $error = $("<div class='trv-parameters-area-error trv-error-pane'></div>"),
            $errorMessage = $("<div class='trv-error-message k-tooltip-validation k-widget'></div>"),
            $content = $("<div class='trv-parameters-area-content'></div>"),
            $footer = $("<div class='trv-parameters-area-footer'></div>")
        ;

        $error.append($errorMessage);

        $wrapper
            .append($content)
            .append($footer)
            .append($error)
            .appendTo(placeholder);

        var $previewButton =
            $("<button class='k-button trv-parameters-area-preview-button'></button>")
                .html("Preview")
                .appendTo($footer)
                .on("click", function () {
                    controller.applyParameters();
                    /*
                    if ($validator.validate()) {
                        controller.applyParameters();
                    }
                    else {
                        alert("Invalid parameter values!");
                    }
                    */
                });

        var parameterContainerTemplate = "<div class='trv-parameter-container k-content'>" +
            "<div class='trv-parameter-header k-header'>" +
                "<span class='trv-parameter-title'></span>" +
            "</div>" +
            "<div class='trv-parameter-error k-tooltip-validation k-widget'>" +
                "<span class='k-icon k-warning'></span>" +
                "<span class='trv-parameter-error-message'></span>" +
            "</div>" +
            "<div class='trv-parameter-value'></div>" +
        "</div>";

        function createParameterUI(parameter) {
            var $container = $(parameterContainerTemplate),
                $header = $container.find(".trv-parameter-header"),
                $valueEditor = $container.find(".trv-parameter-value"),
                editorFactory = selectParameterEditorFactory(parameter)
            ;

            $container
                .find(".trv-parameter-title")
                .html(parameter.text);

            $container
                .find(".trv-parameter-error-message")
                .html(parameter.Error);

            var $error = $container.find(".trv-parameter-error");

            (parameter.Error ? $.fn.show : $.fn.hide).call($error);

            $valueEditor.html(editorFactory.html());

            editorFactory.init($valueEditor, parameter, onParameterChanged, $error, $header);

            return $container;
        }

        function selectParameterEditorFactory(parameter) {
            var factory;
            $.each(options.templates, function () {
                if (this.match(parameter)) {
                    factory = this;
                    return false;
                }
            });
            return factory;
        }

        function showError(error) {
            $errorMessage.html(error);
            (error ? $.fn.show : $.fn.hide).call($error);
        }

        function showPreviewButton(parameters) {

            $wrapper.removeClass("preview");
            $wrapper.addClass("nopreview");

            $.each(parameters, function () {
                if (this.isVisible && !this.autoRefresh) {
                    $wrapper.addClass("preview");
                    $wrapper.removeClass("nopreview");
                    return false;
                }

                return true;
            });
        }
        
        function fill(newParameters) {

            recentParameterValues = {};
            parameters = newParameters;

            $content.empty();
            
            var $parameterContainer;

            $.each(parameters || [], function () {

                var hasValue = Boolean(this.value),
                    hasError = Boolean(this.Error) || !hasValue;
                
                if (hasValue) {
                    recentParameterValues[this.name] = this.value;
                }
                else {
                    this.Error = "Please input a valid value";
                }

                if (this.isVisible) {
                    if ($parameterContainer = createParameterUI(this)) {
                        $content.append($parameterContainer);
                    }
                }
            });
        }
        
        function applyParameters() {
            try {

                internalReportSourceChange = true;

                var rs = controller.reportSource();

                rs.parameters = recentParameterValues;

                controller.reportSource(rs);
                controller.refreshReport();

            } finally {
                internalReportSourceChange = false;
            }
        }
        
        function allParametersValidForAutoRefresh() {
            var triggerAutoUpdate = true;
            
            for (var i = parameters.length - 1; triggerAutoUpdate && i >= 0; i--) {
                var p = parameters[i];
                
                triggerAutoUpdate =
                    (p.name in recentParameterValues) &&
                    (Boolean(p.autoRefresh) || !p.isVisible);
            }
            
            return triggerAutoUpdate;
        }
        
        function tryRefreshReport() {
            if (allParametersValidForAutoRefresh()) {
                applyParameters();
            }
        }

        function onParameterChanged(parameter, newValue) {

            delete parameter["Error"];

            parameter.value = newValue;
            recentParameterValues[parameter.name] = newValue;

            if (parameter.childParameters) {
                $.each(parameter.childParameters, function (i, name) {
                    delete recentParameterValues[name];
                });
            }

            if (parameter.childParameters) {
                loadParameters(recentParameterValues, false);
            }
            else {
                tryRefreshReport();
            }
        }
        
        function loadParameters(parameterValues, shouldRefreshReport) {
            $.when(controller.loadParameters(parameterValues))
                    .done(function (parameters) {
                        // parameters ready
                        fill(parameters);
                        showPreviewButton(parameters);
                        showError('');
                        
                        if (shouldRefreshReport) {
                            tryRefreshReport();
                        }
                    })
                    .fail(function (xhr, status, error) {
                        // load parameters error
                        fill([]);
                        showPreviewButton([]);
                        showError(error);
                    });
        }

        controller
            .reportSourceChanged(function () {
                
                if (!internalReportSourceChange) {
                    fill([]);
                    showPreviewButton([]);
                    showError();

                    loadParameters(null, true);
                }
                
            })

            .getParameterAreaVisible(function (event, args) {
                args.visible = !$placeholder.hasClass("hidden");
            })

            .setParameterAreaVisible(function (event, args) {
                (args.visible ? $.fn.removeClass : $.fn.addClass).call($placeholder, "hidden");
            })
        ;
    };

    var pluginName = "telerik_ReportViewer_ParametersArea";

    $.fn[pluginName] = function (options) {
        return this.each(function () {
            if (!$.data(this, pluginName)) {
                $.data(this, pluginName, new ParametersArea(this, options));
            }
        });
    };

}(window.telerikReportViewer = window.telerikReportViewer || {}, jQuery, window, document));