/*
This file is part of SystemTestPortal.
Copyright (C) 2017  Institute of Software Technology, University of Stuttgart

SystemTestPortal is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

SystemTestPortal is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with SystemTestPortal.  If not, see <http://www.gnu.org/licenses/>.
*/

$.getScript("/static/js/util/common.js");
$.getScript("/static/js/util/ajax.js");
$.getScript("/static/js/project/tests.js");

// newID is the id of the edited test case from the response header
// It is needed to load the correct sut variants and versions after the name
// and therefore the id of a test case was edited
var newID;

// testcase is the test case that is currently being edited, shown, etc...
var testcase;


// Button Assignment
function assignButtonsTestCase() {
//List
    $("#buttonNewTestCase").off('click').on('click', newTestCase);
    $("#buttonNewTestCaseDisabled").off('click').on('click', showSigninHint);
    $("#buttonFirstTestCase").off('click').on('click', newTestCase);
    $(".testCaseLine").off('click').on('click', showTestCase);
//Show
    $("#buttonBack").off('click').on('click', backButtonTestCaseList);
    $("#buttonDeleteTestCase").off('click').on('click', deleteTestCase);
    $("#buttonEdit").off('click').on('click', editTestCase);
    $("#buttonHistory").off('click').on('click', showTestCaseHistory);
//History
    $("#buttonBackToCase").off('click').on('click', backButtonTestCase);
    $(".versionLine").off('click').on('click', showTestCaseVersion);
//New
    $("#buttonAbort").off('click').on('click', backToTestCaseList);
    $("#testCaseEdit").on("submit", saveTestCase);
    $("#buttonConfirmSaveWithoutSteps").off('click').on('click', confirmSaveCaseWithoutSteps);
//Edit
    $("#buttonSave").off('click').on('click', updateTestCase);
    $("#buttonAbortEdit").off('click').on('click', backToTestCase);
//Execute
    $("#buttonExecute").off('click').on('click', execute);

    $("#buttonAddTestStep").off('click').on('click', newTestStep);

    $(".buttonDeleteTestStep").off('click').on('click', function (event) {
        buttonDeleteTestStepConfirm(event);
    });
    $(".buttonDeleteTestStepConfirm").off('click').on('click', function (event) {
        deleteTestStep(event);
    });
    $(".buttonEditTestStep").off('click').on('click', function (event) {
        editTestStep(event);
    });
    $("#buttonFinishTestStepEditing").off('click').on('click', function (event) {
        finishEditTestStep(event);
    });

    $('#modal-teststep-edit').on('shown.bs.modal', function () {
        $('#inputTestStepActionEditField').focus();
    });

    $("#attachImage").off('click').on('click', function (event) {
        attachImage(event);
    });

}

/** Sets up drag and drop on the page. */
function reloadDragAndDropFeature() {
    $('.sortable').sortable({
        placeholderClass: 'list-group-item',
        handle: '.test-case-reorder',
        containment: "#test-steps-container",
        forcePlaceholderSize: true,
        forceHelperSize: true,
        cancel: ' ',
    });
}


//Adds enter-key-handler for textfields, to prevent random actions
function assignEventsToTextFields() {
    $("#inputTestCaseName").keydown(function (event) {
        if (event.keyCode === 13) {
            event.preventDefault();
            $("#inputTestCaseDescription").focus();
        }
    });

    $("#inputHours").keydown(function (event) {
        if (event.keyCode === 13) {
            event.preventDefault();
            $("#inputHours").blur();
        }
    });

    $("#inputMinutes").keydown(function (event) {
        if (event.keyCode === 13) {
            event.preventDefault();
            $("#inputMinutes").blur();
        }
    })
}

/* AJAX-Functions */

/* loads the new test case form */
function newTestCase(event) {
    ajaxRequestFragment(event, "new", "", "GET");
}

/*show sign in hint if not signed in and button is pressed*/
function showSigninHint(event) {
    event.preventDefault();
    $("#signin-link").tooltip('show');
}

/* loads the chosen test case */
function showTestCase(event) {
    ajaxRequestFragment(event, getProjectTabURL().appendSegment(event.target.id).toString(), "", "GET");
}

/* loads the chosen test case version*/
function showTestCaseVersion(event) {
    var url = getTestURL().toString();
    // Get version number. When clicking on an element that is in the .versionLine, check the parent of
    // this element until the parent is the versionLine with the id
    var ind = event.target.id;
    while (ind === "") {
        event.target = event.target.parentNode;
        ind = event.target.id;
    }
    ajaxRequestFragmentWithHistory(event, url, {version: ind}, "GET", url + "?version=" + ind);
}

/* loads the chosen test case version*/
function showTestCaseVersionFromTestSequence(event) {
    var url = getProjectURL().toString();
    // Get version number. When clicking on an element that is in the .versionLine, check the parent of
    // this element until the parent is the versionLine with the id
    var ind = event.target.id;
    while (ind === "") {
        event.target = event.target.parentNode;
        ind = event.target.id;
    }
    ajaxRequestFragment(event, url + "/testcases/" + ind, "", "GET");
}

/* saves the test case */
function saveTestCase(event) {
    event.preventDefault();

    // Check if there are test steps. If there is no test step,
    // ask the user if he really wants to save a case without steps.
    var tcData = getTestCaseData();
    // Do not save if the name is empty
    if ($('#inputTestCaseName').val().length <= 0) {
        return
    }
    // Check if there are steps
    if (tcData.inputSteps.length <= 0) {
        $('#modal-confirm-case-save-without-steps').modal('show');
    } else {
        var history = currentURL().removeLastSegments(1).toString() + "/";
        ajaxRequestFragmentWithHistory(event, "save", JSON.stringify(tcData), "POST", history);
    }
}

// Saves a case without any steps after the user confirmed it
function confirmSaveCaseWithoutSteps(event) {
    var history = currentURL().removeLastSegments(1).toString() + "/";

    $('#modal-confirm-case-save-without-steps').on('hidden.bs.modal', function () {
        ajaxRequestFragmentWithHistory(event, "save", JSON.stringify(getTestCaseData()), "POST", history);
    }).modal('hide');

}

/* loads the start page of the test case execution */
function execute(event) {
    // for back button functionality
    pressedBack = true;

    ajaxRequestFragment(event, currentURL().appendSegment("execute").toString(), "", "GET");
}

// This function is called when clicking on the save button in the edit test case
// screen. It checks if there are any changes to the data of a test case.
// If only the name changed, the case is saved directly. If the metadata was changed
// the modal with the commit message is opened.
function checkForChanges() {
    // If only name of the test case changed, dont open commit modal but save directly
    if (onlyTestCaseNameChanged()) {
        handleEdit(null, false);
    } else {
        $("#modal-testCase-save").modal('show');
    }
}

// onlyTestCaseNameChanged checks if only the name field was changed or if
// any other data was changed. Returns true if only the name field was changed.
// This function is called when clicking on the save button in the edit test case
// screen.
function onlyTestCaseNameChanged() {
    var onlyTcNameChanged = true;
    if (testcase.TestCaseVersions[0].Description !== $('#inputTestCaseDescription').val().trim()) {
        onlyTcNameChanged = false;
    }
    if (preconditionsChanged()) {
        onlyTcNameChanged = false;
    }
    if (sutVersionsChanged()) {
        onlyTcNameChanged = false;
    }
    if (durationChanged()) {
        onlyTcNameChanged = false;
    }
    if (testStepsChanged()) {
        onlyTcNameChanged = false;
    }
    return onlyTcNameChanged;
}

// sutVersionsChanged compares the versions of the original test case with the
// selected versions and variants when clicking on the save button in the edit screen
// of a test case. If the versions or variants have changed, return true, else return
// false.
function sutVersionsChanged() {
    // Selected versions are the currently selected versions
    var selectedVersions = removeEmptyVersions(selectedVariants);
    // testcaseVersions are the original versions from the test case
    var testcaseVersions = testCaseVariantData;
    if (Object.keys(selectedVersions).length !== Object.keys(testcaseVersions).length) {
        return true;
    }
    // Iterate over variants and check if they have changed
    for (var versionKey in testcaseVersions) {

        if (!testcaseVersions[versionKey] && selectedVersions[versionKey] ||
            testcaseVersions[versionKey] && !selectedVersions[versionKey]) {
            return true;
        }

        if (testcaseVersions[versionKey].Name !== selectedVersions[versionKey].Name) {
            return true;
        }

        // Iterate over versions and check if they changed
        for (var variantIndex in testcaseVersions[versionKey].Variants) {

            if (!testcaseVersions[versionKey].Variants[variantIndex] && selectedVersions[versionKey].Variants[variantIndex] ||
                testcaseVersions[versionKey].Variants[variantIndex] && !selectedVersions[versionKey].Variants[variantIndex]) {
                return true;
            }

            if (testcaseVersions[versionKey].Variants[variantIndex].Name !== selectedVersions[versionKey].Variants[variantIndex].Name) {
                return true;
            }
        }
    }
    return false;
}


// durationChanged checks whether the current input for the duration differs
// from the original duration of the test case being edited
function durationChanged() {
    // this gets the duration in nanoseconds
    var testCaseDuration = testcase.TestCaseVersions[0].Duration.Duration;
    // convert nanoseconds to seconds (remove 9 zeros)
    testCaseDuration = testCaseDuration / (1000000000);
    // convert the current duration input to seconds
    var hours = getHours();
    var minutes = getMinutes();
    var inputDuration = hours * 60 * 60 + minutes * 60;
    // compare the two durations
    return testCaseDuration !== inputDuration;
}

// testStepsChanged checks whether the original test steps of the
// edited test case have changed compared to the current test steps
function testStepsChanged() {
    var testCaseSteps = testcase.TestCaseVersions[0].Steps;
    var inputTestSteps = getStepData();

    // If there were no steps and there are no steps or
    // if there were some steps and the number of steps changed,
    // then return true
    if ((!testCaseSteps && inputTestSteps.length > 0) ||
        (testCaseSteps && (testCaseSteps.length !== inputTestSteps.length))) {
        return true;
    }

    // Check if the action or result of any step has changed
    for (var tcStepIndex in testCaseSteps) {
        if (testCaseSteps[tcStepIndex].Action !== inputTestSteps[tcStepIndex].actual) {
            return true;
        }
        if (testCaseSteps[tcStepIndex].ExpectedResult !== inputTestSteps[tcStepIndex].expected) {
            return true;
        }
    }

    return false;
}

/* Updates the test case */
function updateTestCase(event) {
    $('#modal-testCase-save').on('hidden.bs.modal',
        handleEdit(event, $('#minorUpdate').is(':checked'))
    ).modal('hide');
}

/* deletes a test case */
function deleteTestCase(event) {
    var history = currentURL().removeLastSegments(1).toString() + "/";
    $('#deleteTestCase').on('hidden.bs.modal', function () {
        ajaxRequestFragmentWithHistory(event, currentURL().toString(), null, "DELETE", history);
    }).modal('hide');
}

/* edits a test case */
function editTestCase(event) {
    // for back button functionality
    pressedBack = true;

    var ver = $('#inputTestCaseVersion').find("option:selected").val();
    ajaxRequestFragment(event, currentURL().appendSegment("edit").toString(), {version: ver}, "GET");
}

/* show test case history */
function showTestCaseHistory(event) {
    ajaxRequestFragment(event, currentURL().appendSegment("history").toString(), "", "GET");
}

/* steps back to the test case  */
function backToTestCase(event) {
    // for back button functionality
    pressedBack = true;

    var requestURL = getTestURL().toString();
    ajaxRequestFragment(event, requestURL, "", "GET");
}

/* steps back to the test case list */
function backToTestCaseList(event) {
    // for back button functionality
    pressedBack = true;

    var requestURL = getProjectURL().appendSegment("testcases").toString() + "/";
    ajaxRequestFragment(event, requestURL, "", "GET");
}

/* functionality of back button*/
function backButtonTestCase(event) {
    backButton(event, backToTestCase);
}

/* functionality of back button*/
function backButtonTestCaseList(event) {
    backButton(event, backToTestCaseList);
}

/* helper */
function getTestResult() {
    var radioButtons = document.getElementsByName("testResults");
    var selectedButton;

    for (var i = 0; i < radioButtons.length; i++) {
        if (radioButtons[i].checked)
            selectedButton = radioButtons[i].value;
    }
    return selectedButton
}

// getTestCaseData returns an array with the test case information
function getTestCaseData() {
    if (typeof testAssignedLabelIds === undefined) {
        testAssignedLabelIds = [];
    }
    return {
        inputTestCaseName: $('#inputTestCaseName').val().trim().replace(/\s\s+/g, ' '),
        inputTestCaseDescription: $('#inputTestCaseDescription').val(),
        inputTestCasePreconditions: getPreconditions(),
        inputTestCaseSUTVersions: selectedVariants,
        inputHours: getHours(),
        inputMinutes: getMinutes(),
        inputSteps: getStepData(),
        inputLabels: testAssignedLabelIds
    }
}

// getDataEdit return an array with information of the test case to edit
function getTestCaseDataEdit(isMinor) {
    return {
        isMinor: isMinor,
        inputCommitMessage: $('#inputCommitMessage').val(),
        data: getTestCaseData()
    }
}

// removeEmptyVariants removes all variants from an array of variants that contain no versions
function removeEmptyVersions(sutVersions) {
    $.each(sutVersions, function (index, version) {
        if (Object.keys(version.Variants).length < 1) {
            delete sutVersions[version.Name]
        }
    });
    return sutVersions
}

// handleEdit either updates the current version or saves the edited version
// as newest version, depending on the handling parameter
function handleEdit(event, isMinor) {
    if (event !== null && event !== undefined) {
        event.preventDefault();
    }

    var url = currentURL().removeLastSegments(1);

    var posting = $.ajax({
        url: url.appendSegment("update").toString() + "?fragment=true",
        type: "PUT",
        data: JSON.stringify(getTestCaseDataEdit(isMinor))
    });
    posting.done(function (response) {
        //Save the new id of the test case after editing
        newID = decodeURIComponent(posting.getResponseHeader("newName"));

        var historyText = getProjectTabURL().appendSegment(newID).toString();
        $('#tabTestCases').empty().append(response);
        history.pushState('data', '', historyText);
    }).fail(function (response) {
        $("#modalPlaceholder").empty().append(response.responseText);
        $('#errorModal').modal('show');
    });
}

// getHours returns 0 if no hours duration was given
function getHours() {
    const hours = parseInt($('#inputHours').val());
    if (isNaN(hours)) {
        return 0;
    }
    return hours;
}

// getMinutes returns 0 if no minutes duration was given
function getMinutes() {
    const mins = parseInt($('#inputMinutes').val());
    if (isNaN(mins)) {
        return 0;
    }
    return mins;
}

// 2562047 is the max of int64 as nanoseconds converted to minutes
var HOUR_HARD_LIMIT = 2562047;

// checkMinutes sets the minutes between 0 and 59
function checkMins(form) {
    minutes = Math.min(Math.max(0, form.value), 59);
    $("#inputMinutes").val(minutes);
}

// checkHour sets the input between 0 and the biggest number that can be shown
// by a test case
function checkHours(form) {
    hours = Math.min(Math.max(0, form.value), HOUR_HARD_LIMIT);
    $("#inputHours").val(hours);
}

var hours = getHours();
var minutes = getMinutes();

$("#hour-plus").click(function () {
    if (hours < HOUR_HARD_LIMIT) {
        hours++;
        $("#inputHours").val(hours);
    }
});
$("#hour-minus").click(function () {
    hours = Math.max(0, hours - 1);
    $("#inputHours").val(hours);
});
$("#minute-plus").click(function () {
    minutes = (minutes + 1) % 60;
    if (minutes === 0 && hours < HOUR_HARD_LIMIT) {
        hours++;
        $("#inputHours").val(hours);
        $("#inputMinutes").val(minutes);
    } else if (hours < HOUR_HARD_LIMIT) {
        $("#inputMinutes").val(minutes);
    }
});
$("#minute-minus").click(function () {
    if (minutes <= 0 && hours > 0) {
        hours--;
        minutes = 59;
        $("#inputHours").val(hours);
    } else if (minutes <= 0) {
        minutes = 0;
    } else {
        minutes--;
    }
    $("#inputMinutes").val(minutes);
});

// buttonDeleteTestStepConfirm manages the change of the delete-button,
// to confirm the delete-intent of the user
function buttonDeleteTestStepConfirm(event) {
    $(event.currentTarget).next().removeClass("d-none");
    $(event.currentTarget).addClass("d-none");
}

// deleteTestStep removes the test step-list element from the GUI
function deleteTestStep(event) {
    $(event.currentTarget).parent().parent().parent().parent().remove();
}

// editTestStep handles the click-event of an existing test-steps edit-button,
// calls the test-step-edit-modal with the data from the test step in the
// input-fields
function editTestStep(event) {
    const id = $(event.target).closest("li").prop("id").toString().replace("testStep", "");

    const actText = $(`#markdown-task-${id}`).text();
    const textAreaA = $('#inputTestStepActionEditField');
    textAreaA.val(actText);

    const expText = $(`#markdown-exre-${id}`).text();
    const textAreaE = $('#inputTestStepExpectedResultEditField');
    textAreaE.val(expText);

    const idArea = $('#testStepIdField');
    idArea.val(id.toString());

    $("#add-1-edit-2").text("2");
    $('#modal-teststep-edit').modal('show');
}

// finishEditTestStep handles the submit of the test-step-edit-modal,
// in case of being called from an existing test-steps edit-button
function finishEditTestStep(event) {
    event.preventDefault();

    const id = $('#testStepIdField').val();

    const actText = $('#inputTestStepActionEditField').val();
    const expText = $('#inputTestStepExpectedResultEditField').val();

    if (actText.trim()) {
        if ($("#add-1-edit-2").text() === "1") {
            finishAddingNewTestStep(event, id);
        } else {

            parseMarkdown(actText).done(html => {
                $(`#markdown-task-${id}`).text(actText);
                $(`#ActionField-${id}`).html(html);
            });

            parseMarkdown(expText).done(html => {
                $(`#markdown-exre-${id}`).text(expText);
                $(`#ExpectedResultField-${id}`).html(html);
            });

            $('#modal-teststep-edit').modal('hide');
        }
    }

}

// newTestStep handles the click-event of the add-test-step-button,
// calls the test-step-edit-modal with empty input fields
function newTestStep() {
    const textAreaA = $('#inputTestStepActionEditField');
    textAreaA.val('');
    const textAreaE = $('#inputTestStepExpectedResultEditField');
    textAreaE.val('');
    const idArea = $('#testStepIdField');
    idArea.val(Date.now());
    $("#add-1-edit-2").text("1");

    $('#modal-teststep-edit').modal('show');
}

// finishAddingNewTestStep processes the the submitting of the test-step-
// edit-modal when the modal was requested via the add-test-step-button
function finishAddingNewTestStep(event, id) {

    const inputExpect = $('#inputTestStepExpectedResultEditField').val();
    const inputAction = $('#inputTestStepActionEditField').val();
    let textExpect, textAction;

    $.when(
        parseMarkdown(inputExpect.toString()).done(html => textAction = html),
        parseMarkdown(inputAction.toString()).done(html => textExpect = html),
    ).then(
        // Successful
        () => {
            const entry =
                `<li class="list-group-item pr-3" id="testStep${id}">
        <div class="d-flex">
            <div id="markdown-task-${id}" class="d-none markdown-task">${inputAction}</div>
            <div id="markdown-exre-${id}" class="d-none markdown-exre">${inputExpect}</div>
        
            <div>
                <button type="button" class="test-case-reorder ui-sortable-handle">
                    <i class="fa fa-bars"></i>
                </button>
            </div>
            
            <div style="flex: 1;">
                <span id="ActionField-${id}"  data-toggle="collapse" data-parent="#testStepsAccordion"
                      href="#testStepsAccordion${id}"
                      aria-expanded="true" aria-controls="testStepsAccordion${id}">${textExpect}</span>

                <div id="testStepsAccordion${id}" class="collapse" role="tabpanel">
                    <span id="ExpectedResultField-${id}" class="text-muted">${textAction}</span>
                    <button type="button" onclick="buttonDeleteTestStepConfirm(event)"
                            class="btn btn-danger ml-2 list-line-item btn-sm buttonDeleteTestStep">
                        <i class="fa fa-trash-o" aria-hidden="true"></i>
                        <span class="d-none d-sm-inline">Delete</span>
                    </button>
                    <button type="button" onclick="deleteTestStep(event)"
                            class="btn btn-danger ml-2 d-none list-line-item btn-sm buttonDeleteTestStepConfirm"
                            data-toggle="modal"
                            data-target="#deleteModal">
                        <i class="fa fa-check" aria-hidden="true"></i>
                        <span class="d-none d-sm-inline">Confirm delete</span>
                    </button>
                    <button type="button" onclick="editTestStep(event)"
                            class="btn btn-primary ml-auto list-line-item btn-sm buttonEditTestStep">
                        <i class="fa fa-pencil-square-o" aria-hidden="true"></i>
                        <span class="d-none d-sm-inline">Edit</span>
                    </button>
                </div>
            </div>
        </div>
    </li>`;

            $("#testStepsAccordion").append(entry);
            $('#modal-teststep-edit').modal('hide');
        },

        // On fail
        () => {
            console.log("error creating new test step because of ajax markdown request")
        }
    );


}

// getStepData returns an array of arrays containing the ID, action and
// expected result data of the test step GUI elements
function getStepData() {
    const steps = [];
    let temp = {};
    $("#testStepsAccordion > li").each((index, element) => {

        temp.ID = index;
        temp.actual = $(element).find(".markdown-task").text();
        temp.expected = $(element).find(".markdown-exre").text();
        steps.push(temp);
        temp = {};

    });
    return steps;
}

function preconditionsChanged() {
    return testcase.TestCaseVersions[0].Preconditions !== getPreconditions();
}

/**
 * Keyboard support
 */
$(document)
    .off("keydown.event")
    .on("keydown.event", keyboardSupport);

/**
 * keybindings
 * @param event
 */
function keyboardSupport(event) {
    //checks if focus is in textfields, areas, etc.
    switch (event.target.tagName) {
        case "INPUT":
        case "SELECT":
        case "TEXTAREA":
            return;
    }

    //Adds Keybindings
    let buttonString = "";
    switch (event.key) {
        case "Enter":
            buttonString = "buttonExecute";
            break;
        case "Delete":
            buttonString = "buttonDelete";
            break;
        case "E":
        case "e":
            buttonString = "buttonEdit";
            break;
        case "Backspace":
            buttonString = "buttonBack";
            break;
        case "D":
        case "d":
            buttonString = "buttonDuplicate";
            break;
        case "A":
        case "a":
            buttonString = "buttonAssign";
            break;
        case "H":
        case "h":
            buttonString = "buttonHistory";
            break;
        case "P":
        case "p":
            buttonString = "tabButtonPrint";
            break;
        case "+":
            buttonString = "buttonNewTestCase";
            break;
        case "S":
        case "s":
            buttonString = "buttonSaveTestCase";
            break;
        case "Escape":
            if (document.getElementById("buttonAbort") != null) {
                buttonString = "buttonAbort";
            } else {
                buttonString = "buttonAbortEdit";
            }
            break;
    }
    if (document.getElementById(buttonString) != null) {
        document.getElementById(buttonString).click();
    }
}

/**
 * Key Listeners on modal!
 */
$('.modal')
    .off("hide.bs.modal.keyboardsupport")
    .off("show.bs.modal.keyboardsupport")

    /* Adds key listeners when modals closes */
    .on('hide.bs.modal.keyboardsupport', () => $(document).on("keydown.event", keyboardSupport))

    /* Removes key listeners when modals open */
    .on('show.bs.modal.keyboardsupport', () => $(document).off("keydown.event"));

/**
 * Set focus on input field when duplicating
 */
$('#duplicateModal').on('shown.bs.modal', () => $('#inputDuplicateTestCaseName').focus());

/**
 * Enable enter button to confirm duplicating
 */

$(function () {

    //If the validator is used, the unique method is added or overwritten if already existent
    if ($.validator) {
        function uniqueTestCaseNameMethod(value, element, param) {
            if( cases != null){
                return !cases.includes(value.trim().replace(/\s\s+/g, ' ')) || !param || this.optional(element);
            } else {
                return true;
            }
        }

        if (!$.validator.methods.unique) {
            $.validator.addMethod("unique", uniqueTestCaseNameMethod, "A test case with this name already exists.");
        } else {
            $.validator.methods.unique = uniqueTestCaseNameMethod;
        }

        function errorPlacementFunction(error, element) {
            // Add the `help-block` class to the error element

            error.addClass("form-control-feedback");

            // Add `has-feedback` class to the parent div.form-group
            // in order to add icons to inputs
            element.parents(".col-sm-5").addClass("has-feedback");

            if (element.prop("type") === "checkbox") {
                error.insertAfter(element.parent("label"));
            } else {
                error.insertAfter(element);
            }

            // Add the span element, if doesn't exists, and apply the icon classes to it.
            if (!element.next("span")[0]) {
                $("<span class='glyphicon glyphicon-remove form-control-feedback'></span>").insertAfter(element);
            }

        }


        const duplicateForm = $("#duplicateTestCaseForm");
        const editForm = $("#testCaseEdit");
        if (duplicateForm.val() === "") {
            const savebutton = $('#buttonDuplicateConfirmed');
            duplicateForm.validate({
                // requirements for formfields
                rules: {
                    inputDuplicateTestCaseName: {
                        required: true,
                        unique: true,
                        minlength: 4
                    }
                },
                // error messages
                messages: {
                    inputDuplicateTestCaseName: {
                        required: "Please enter a name for the new testcase.",
                        minlength: "The test case name must consist of at least 4 characters.",
                        unique: "A test case with this name already exists."
                    },
                },
                // error message placement
                errorElement: "div",
                errorPlacement: errorPlacementFunction,
                success: function (label, element) {
                    // Add the span element, if doesn't exists, and apply the icon classes to it.
                    $(element).parents(".col-sm-10").addClass(" text-success").removeClass("text-danger");
                    $(element).addClass("form-control-success").removeClass("text-danger");
                    savebutton.removeAttr("disabled");

                },
                highlight: function (element, errorClass, validClass) {
                    $(element).parents(".col-sm-10").addClass(" text-danger").removeClass("text-success");
                    $(element).addClass("form-control-danger").removeClass("text-danger");
                    savebutton.attr("disabled", "");
                },
                unhighlight: function (element, errorClass, validClass) {
                    $(element).parents(".col-sm-10").addClass("text-success").removeClass("text-error");
                    $(element).addClass("form-control-success").removeClass("text-danger");
                }
            });
        }
        if (editForm.val() === "") {
            const savebutton = $('#buttonSaveTestCase');
            editForm.validate({
                // requirements for formfields
                rules: {
                    inputTestCaseName: {
                        required: true,
                        unique: true,
                        minlength: 4
                    }
                },
                // error messages
                messages: {
                    inputTestCaseName: {
                        required: "Please enter a name for the new test case.",
                        minlength: "The test case name must consist of at least 4 characters.",
                        unique: "A test case with this name already exists."
                    },
                },
                // error message placement
                errorElement: "div",
                errorPlacement: errorPlacementFunction,
                success: function (label, element) {
                    // Add the span element, if doesn't exists, and apply the icon classes to it.
                    $(element).parents(".form-group").addClass(" text-success").removeClass("text-danger");
                    $(element).addClass("form-control-success").removeClass("text-danger");
                    savebutton.removeAttr("disabled");
                },
                highlight: function (element, errorClass, validClass) {
                    $(element).parents(".form-group").addClass(" text-danger").removeClass("text-success");
                    $(element).addClass("form-control-danger").removeClass("text-danger");
                    savebutton.attr("disabled", "");
                },
                unhighlight: function (element, errorClass, validClass) {
                    $(element).parents(".form-group").addClass("text-success").removeClass("text-error");
                    $(element).addClass("form-control-success").removeClass("text-danger");
                }
            });
        }
    }

    $('#inputDuplicateTestCaseName')
        .unbind('keypress')
        .keypress(e => {
            if (e.keyCode === 13)
                $('#buttonDuplicateConfirmed').click();
        });
});


/**
 * Fills the test steps with numbers
 *
 * @param steps test steps of the test case
 */
function setTestStepNumber(steps) {
    let i;
    for (i = 0; i < steps.length; i++){
        $("#stepNumber" + i).text(i + 1 + ".");
    }
}
