/** @module surveysManager */

var surveysManager = (function (surveysManager, $) {

    /** Handle the Question form page.
     *
     * @param {string} choicesFormsetPrefix - Prefix to identify the choices formet elements.
     * @param {Object.<string, string>} i18n - Object with translated strings.
     * @param {Object.<string, Array[string]>} ALLOWED_VALUE_TYPES - Dict with allowed value types
     *   for each question type.
     * @param {Object.<string, boolean>} CHOICES_EXPECTED - Dict telling if choices are expected
     *   for each question type.
     * @param {Object.<string, Array[string]>} ALLOWED_GLOBAL_SATISFACTION - Dict telling if a
     *   (question type, value type) couple is allowed for global satisfaction
     */
    surveysManager.handleQuestionForm = function (choicesFormsetPrefix,
                                                  i18n,
                                                  ALLOWED_VALUE_TYPES,
                                                  CHOICES_EXPECTED,
                                                  ALLOWED_GLOBAL_SATISFACTION
                                                  ) {

        /**
         * jQuery node of the whole question form.
         * @type {jQuery}
         */
        var $form = $('#survey-question-form');

        /**
         * jQuery node of the node holding the choices list.
         * @type {jQuery}
         */
        var $choicesHolder = $('#survey-question-form-choices');

        /**
         * Object holding the jQuery node for each input related to the question itself.
         * @type {Object.<string, jQuery>}
         */
        var mainFields = {};

        // Fill `mainFields`, ignoring hidden fields and submit.
        $form.find('#survey-question-form-main :input:not([type=hidden]):not([type=submit])').each(function () {
            var $input = $(this);
            mainFields[$input.attr('name')] = $input;
        });

        /** Tell if the given valueType if valid for the given questionType.
         *
         * It's a helper function to avoid business logic in the rest of the code.
         *
         * @param {string} valueType - The value type to check for.
         * @param {string} questionType - The question type to check for.
         *
         * @returns {boolean} - `true` if value type allowed, `false` otherwise`.
         */
        function isValueTypeAllowedForQuestionType(valueType, questionType) {
            return (ALLOWED_VALUE_TYPES[questionType].indexOf(valueType) >= 0);
        }

        /** Tell if the given value type indicates that values are expected or not.
         *
         * It's a helper function to avoid business logic in the rest of the code.
         *
         * @param {string} valueType - The value type to check for.
         *
         * @returns {boolean} - `true` if values expected, `false` otherwise`.
         */
        function areValuesExpected(valueType) {
            return valueType != '';
        }

        /** Tell if the given question type expects a list of choices or not.
         *
         * It's a helper function to avoid business logic in the rest of the code.
         *
         * @param {string} questionType - The question type to check for.
         *
         * @returns {boolean} - `true` if choices are expected, `false` otherwise`.
         */
        function areChoicesExpected(questionType) {
            return CHOICES_EXPECTED[questionType];
        }

        /** Tells if the "global satisfaction" flag is available for the given valueType and questionType.
         *
         * It's a helper function to avoid business logic in the rest of the code.
         *
         * @param {string} valueType - The value type to check for.
         * @param {string} questionType - The question type to check for.
         *
         * @returns {boolean} - `true` if the "global satisfaction" flag is allowed, `false` otherwise`.
         */
        function isGlobalSatisfactionAllowedForTypes(valueType, questionType) {
            return (ALLOWED_GLOBAL_SATISFACTION[questionType].indexOf(valueType) >= 0);
        }

        /** Returns the value of the currently selected question type.
         *
         * @returns {string} - The one letter value of the question type.
         */
        function getQuestionType() {
            return mainFields['question_type'].val();
        }


        /** Returns the value of the currently selected value type.
         *
         * @returns {string} - The one letter value of the value type.
         */
        function getValueType() {
            return mainFields['value_type'].val();
        }

        /** Helper that toggles the visibility of the `TR` dom node containing the given node.
         *
         * @param {jQuery} $node - The jQuery node for which we want it's containing `TR` visibility
         *   to be changed.
         * @param {boolean} toggle - `true` if the line should be shown, `false` otherwhise.
         */
        function toggleFieldLine($node, toggle) {
            $node.closest('tr').toggle(toggle);
        }

        /** React to a change on the question type select.
         *
         * - updates the list of available value types, hiding it if no value expected
         * - toggles the whole list of choices if the choices are now expected/not expected
         * - calls `onValueTypeChange` to react on the new available/selected value type
         *
         * @listens question_type:change
         */
        function onQuestionTypeChange() {
            var questionType = getQuestionType();
            updateValueTypes(questionType);
            toggleChoices(questionType);
            onValueTypeChange();
        }

        /** React to a change on the value type select.
         *
         * - toggles the "global satisfaction" depending on the (question type, value type) couple
         * - toggles the value fields of choices depending on the value type
         *
         * @listens value_type:change
         */
        function onValueTypeChange() {
            var valueType = getValueType();
            toggleGlobalSatisfaction(getQuestionType(), valueType);
            toggleChoicesValues(valueType);
        }

        /** Updates the list of allowed value types depending on the question type.
         *
         * Elements that are not allowed are hidden. If the previously selected item is not
         * allowed, the first allowed one will be selected.
         * If there is only one allowed element, the whole select box is hidden.
         *
         * @param {string} questionType - The question type to check for.
         */
        function updateValueTypes(questionType) {
            var $input = mainFields['value_type'];
            var currentVal = $input.val();

            // Will hold visible options to:
            // - select the first one if the previously selected is now hidden
            // - count the number of visible ones and hide the whole select if only one
            var visible_options = [];

            // Loop on each option to toggle its visibility.
            $input.find('option').each(function () {
                var $option = $(this);
                var val = $option.val();

                var visible = isValueTypeAllowedForQuestionType($option.val(), questionType);
                $option.toggle(visible);

                if (visible) {
                    visible_options.push($option);
                } else if (val == currentVal) {
                    // Selected element not visible anymore, ask to selected another one.
                    currentVal = null;
                }
            });

            // Select the first element if there is noone selected.
            if (currentVal == null) {
                visible_options[0].prop('selected', true);
            }

            // Toggle the line visibility if at max one, or more that one elements.
            toggleFieldLine($input, visible_options.length > 1);
        }

        /** Toggles the visibility of "global satisfaction" flag depending on question and value types..
         *
         * It will also uncheck the checkbox if the "global satisfaction" flag is not allowed.
         *
         * @param {string} questionType - The question type to check for.
         * @param {string} valueType - The value type to check for.
         */
        function toggleGlobalSatisfaction(questionType, valueType) {
            var $input = mainFields['is_for_global_satisfaction'];
            var allowed = isGlobalSatisfactionAllowedForTypes(valueType, questionType);
            if (!allowed) {
                $input.prop('checked', false);
            }
            toggleFieldLine($input, allowed);
        }

        /** Toggles the visibility of the whole choices list depending on the question type.
         *
         * @param {string} questionType - The question type to check for.
         */
        function toggleChoices(questionType) {
            $choicesHolder.toggle(areChoicesExpected(questionType));
        }

        /** Toggles the visibility of the "value" field of all choices depending on the value type.
         *
         * @param {string} valueType - The value type to check for.
         */
        function toggleChoicesValues(valueType) {
            $choicesHolder.find('.inline-choice').each(function () {
                toggleFieldLine($(this).find('input[name*=-value_text]'),
                    areValuesExpected(valueType));
            });
        }

        /** simulate a click on the delete link of all choices if the selected question
         *  type doesn't expect choices
         *
         * @param {callback} function - The function to call when we are done.
         *
         */
        function deleteUnexpectedChoicesOnSubmit(callback) {

            // Choices are expected, so nothing to do.
            if (areChoicesExpected(getQuestionType())) {
                callback();
                return;
            }

            // Simulate click on all delete links.
            var links = $('.inline-deletelink');
            var nb_links = links.length;
            if (!nb_links) {
                // No link, we can stop here and call the callback.
                callback();
                return;
            }

            // Stop the default form submission, we'll trigger it manually once all the
            // delete buttons will be clicked.
            ev.preventDefault();
            // Counter of click buttons. Once the number will be the same as `nb_links`, the
            // forms will be submited.
            var nb_done = 0;
            // Add a handler on each delete link that will be called after all previously set
            // handlers. So we are sure that all need operations are done before really
            // submitting the form.
            links.click(function () {
                nb_done++;
                if (nb_done == nb_links) {
                    // When all click operations are done, we are done and can call the
                    // callback
                    callback();
                }
            });
            // Click on all delete links. It will calls all existing handlers, finishing by
            // ours, one link at a time.
            links.click();
        }

        /** Empty "value" fields if no values are expected.
         *
         * @param {callback} function - The function to call when we are done.
         *
         */
        function clearUnexpectedValuesOnSubmit(callback) {

            // Values are expected, so nothing to do.
            if (areValuesExpected(getValueType())) {
                callback();
                return;
            }

            // Clear all value input.
            $choicesHolder.find('.inline-choice input[name*=-value_text]').each(function () {
                $(this).val('');
            });

            // We're done, we can call the callback
            callback();
        }

        /** React to the form submission.
         *
         * @param {event} ev - The event that triggered this call.
         *
         * If will:
         *   - simulate a click on the delete link of all choices if the selected question
         *     type doesn't expect choices.
         *   - enmpty "value" fields if no values are expected.
         *
         * @listens form:submit
         */
        function onFormSubmit(ev) {
            // The real submit will occur when all validation will be done.
            ev.preventDefault();

            // If choices are not expected (depending on the question type), we remove all.
            deleteUnexpectedChoicesOnSubmit(function() {
                // If values are not expected (depending on the value type), we clear all.
                clearUnexpectedValuesOnSubmit(function() {
                    // Everything is done, we can really submit the form, but remove the
                    // current submit handler to avoid recursion!
                    $form.off('submit', onFormSubmit);
                    $form.submit();
                })
            })
        }

        // Init our event handlers
        mainFields['question_type'].change(onQuestionTypeChange);
        mainFields['value_type'].change(onValueTypeChange);
        $form.submit(onFormSubmit);

        // Init the inline manager from `inlines.js`.
        $(".inline-choice").stackedFormset({
            prefix: choicesFormsetPrefix,
            softDeletion: true,  // Handles the deletion of existing choices, and dynamically
                                 // created ones the same way.
            addCssClass: 'add-row btn btn-default', // To make the "Add another choices" link
                                                    // looks like a button.
            addText: i18n.addText, // Text of the "Add another choice" button.
            deleteText: i18n.deleteText  // Text of the "Remove this choice" button.
        });

        // Initialize the page with the current values.
        onQuestionTypeChange();
    }

    return surveysManager;

}(surveysManager || {}, $));
