/*
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");

// testsequence contains the original testsequence that was edited. It is used to check
// for changes in the data.
var testsequence;

// Button Assignment
function assignButtonsTestSequence() {
//List
    $("#buttonNewTestSequence").off('click').on('click', newTestSequence);
    $("#buttonNewTestSequenceDisabled").off('click').on('click', showSigninHint);
    $("#buttonFirstTestSequence").off('click').on('click', newTestSequence);
    $("#buttonNewLabel").off('click').on('click', function (event) { /* nothing yet */
    });
    $(".testSequenceLine").off('click').on('click', showTestSequence);
//Show
    $("#buttonBack").off('click').on('click', backButtonTestSequenceList);
    $("#buttonDeleteTestSequence").off('click').on('click', deleteTestSequence);
    $("#buttonEdit").off('click').on('click', editTestSequence);
    $("#buttonHistory").off('click').on('click', showTestSequenceHistory);
//$("#buttonRevertConfirmed")  .off('click').on('click', function (event) { revertTestSequence(event); });
//History
    $("#buttonBackToSequence").off('click').on('click', backButtonTestSequence);
    $(".versionLine").off('click').on('click', showTestSequenceVersion);
//New
    $("#buttonAbort").off('click').on('click', backToTestSequenceList);
    $("#testSequenceEdit").on("submit", saveTestSequence);
    $("#buttonSaveNew").off('click').on('click', saveTestSequence);
//Edit
    $("#buttonSave").off('click').on('click', updateTestSequence);
    $("#buttonAbortEdit").off('click').on('click', backToTestSequence);
//Execute
    $("#buttonExecute").off('click').on('click', execute);


    /**
     * Set focus on input field when duplicating
     */
    $('#duplicateModal').on('shown.bs.modal', () => $('#inputDuplicateTestSequenceName').focus());
    
    /**
     * Enable enter button to confirm duplicating
     */
    $(document).ready(function () {

        //TODO: Move to general document ready function
        if ($.validator) {
            function uniqueTestCaseNameMethod(value, element, param) {
                return !sequences.includes(value.trim().replace(/\s\s+/g, ' ')) || !param || this.optional(element);
            }

            if (!$.validator.methods.unique) {
                $.validator.addMethod("unique", uniqueTestCaseNameMethod, "A test sequence 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 = $("#duplicateTestSequenceForm");
            const editForm = $("#testSequenceEdit");

            if (editForm.val() === "") {
                const savebutton = $('#buttonSaveTestSequence');
                editForm.validate({
                    // requirements for formfields
                    rules: {
                        inputTestSequenceName: {
                            required: true,
                            unique: true,
                            minlength: 4
                        }
                    },
                    // error messages
                    messages: {
                        inputTestSequenceName: {
                            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");
                    }
                });
            }

            if (duplicateForm.val() === "") {
                const savebutton = $('#buttonDuplicateConfirmed');
                duplicateForm.validate({
                    // requirements for formfields
                    rules: {
                        inputDuplicateTestSequenceName: {
                            required: true,
                            unique: true,
                            minlength: 4
                        }
                    },
                    // error messages
                    messages: {
                        inputDuplicateTestSequenceName: {
                            required: "Please enter a name for the new test sequence.",
                            minlength: "The test sequence name must consist of at least 4 characters.",
                            unique: "A test sequence 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");
                    }
                });
            }
        }

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

//Adds Enter-key-handler for name textfield, to prevent random actions for enter key
function assignEventsToNameTextField() {
    $("#inputTestSequenceName").keydown(function (event) {
        if (event.keyCode === 13) {
            event.preventDefault();
            $("#inputTestSequenceDescription").focus();
        }
    });
}

/* AJAX-Functions */

/* loads the new test sequence form */
function newTestSequence(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 sequence */
function showTestSequence(event) {
    ajaxRequestFragment(event, getProjectTabURL().appendSegment(event.target.id).toString(), "", "GET");
}

/* loads the chosen test sequence version*/
function showTestSequenceVersion(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);
}

/* saves the test sequence */
function saveTestSequence(event) {
    var history = currentURL().removeLastSegments(1).toString() + "/";
    ajaxRequestFragmentWithHistory(event, "save", getDataNewTestSequence(), "POST", history);
}

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

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

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

/* revert test sequence to selected version */
function revertTestSequence(event) {
    // not yet implemented
}

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

// onlyTestSequenceNameChanged checks whether there was any changes to the input
// of the metadata of the edited test sequence.
// Returns true if no metadata was changed. Else returns false.
function onlyTestSequenceNameChanged() {
    var onlyTSNameChanged = true;
    if (testsequence.SequenceVersions[0].Description !== $('#inputTestSequenceDescription').val().trim()) {
        onlyTSNameChanged = false;
    }
    if (preconditionsChanged()) {
        onlyTSNameChanged = false;
    }
    // There were no cases previously and during editing some cases were added
    if ((!testsequence.SequenceVersions[0].Cases && $(".testCaseInTestSequence").length > 0)
        // or the sequence contained cases and the number of cases changed
        || (testsequence.SequenceVersions[0].Cases &&
            ($(".testCaseInTestSequence").length !== testsequence.SequenceVersions[0].Cases.length))) {

        onlyTSNameChanged = false;
    }

    // Check if the cases in the sequence have changed
    if (testsequence.SequenceVersions[0].Cases) {
        $(".testCaseInTestSequence").each(function (index) {
            if (!testsequence.SequenceVersions[0].Cases[index] || this.id !== testsequence.SequenceVersions[0].Cases[index].Name) {
                onlyTSNameChanged = false;
            }
        });
    }
    return onlyTSNameChanged;
}


/* saves the test sequence */
function updateTestSequence(event) {
    $(document).off("keydown.event")
    $('#modal-testsequence-save').on
    (
        'hidden.bs.modal',
        handleEdit(event, $('#minorUpdate').is(':checked'))
    ).modal('hide');
}

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

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

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

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

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

/* functionality of back button*/
function backButtonTestSequence(event) {
    backButton(event, backToTestSequence);
}

/* functionality of back button*/
function backButtonTestSequenceList(event) {
    backButton(event, backToTestSequenceList);
}

// handleEdit either updates the current version or saves the edited version as newest version, depending
// on the handling parameter
function handleEdit(e, isMinor) {

    /* stop form from submitting normally */
    if (e !== null && e !== undefined) {
        e.preventDefault();
    }

    var url = currentURL().removeLastSegments(1).toString();
    /* Send the data using post with element ids*/
    var posting = $.ajax({
        url: url + "/update?fragment=true",
        type: "PUT",
        data: getDataEdit(isMinor)
    });
    /* Alerts the results */
    posting.done(function (response) {
        var historyText = currentURL().takeFirstSegments(4).appendCodedSegment(posting.getResponseHeader("newName")).toString();
        $('#tabTestsequences').empty().append(response);
        history.pushState('data', '', historyText);
    }).fail(function (response) {
        $("#modalPlaceholder").empty().append(response.responseText);
        $('#errorModal').modal('show');
    });
}

// getDataNewTestSequence return an array with information of the new test sequence
function getDataNewTestSequence() {
    var testcases = gatherTestCasesInSequenceIDs();
    return {
        inputTestSequenceName: $('#inputTestSequenceName').val().trim().replace(/\s\s+/g, ' '),
        inputTestSequenceDescription: $('#inputTestSequenceDescription').val(),
        inputTestSequencePreconditions: JSON.stringify(getPreconditions()),
        inputTestSequenceTestCase: testcases,
        inputSUTVersion: $('#inputSUTVersion').val(),
        inputTestSequenceVersion: $('#inputTestSequenceVersion').val(),
        inputTestSequenceTime: $('#inputTestSequenceTime').val(),
        inputLabels: JSON.stringify(testAssignedLabelIds)
    }
}

// getTestSequenceLabelsInput returns a list with the selected labels
function getTestSequenceLabelsInput() {
    // testSequenceLabels contains the the labels of the test case
    var testSequenceLabels = [];
    //  Get index of selected labels in dropdown
    var labelsValues = $('#labels').find('option:selected').map(function (a, item) {
        return item.value;
    });

    // Add the selected labels to testSequenceLabels
    $.each(labelsValues, function (index, value) {
        testSequenceLabels.push(projectLabels[value])
    });

    return testSequenceLabels;
}

// getDataEdit return an array with information of the test sequence to edit
function getDataEdit(isMinor) {
    var testcases = gatherTestCasesInSequenceIDs();
    return {
        isMinor: isMinor,
        inputCommitMessage: $('#inputCommitMessage').val(),
        version: "{{ GetTestSequenceVersion.VersionNr }}",
        inputTestSequenceName: $('#inputTestSequenceName').val().trim().replace(/\s\s+/g, ' '),
        inputTestSequenceDescription: $('#inputTestSequenceDescription').val(),
        inputTestSequencePreconditions: JSON.stringify(getPreconditions()),
        inputTestSequenceTestCase: testcases,
        inputSUTVersion: $('#inputSUTVersion').val(),
        inputTestSequenceVersion: $('#inputTestSequenceVersion').val(),
        inputTestSequenceTime: $('#inputTestSequenceTime').val()
    }
}

// collects the test case IDs for the test sequence
function gatherTestCasesInSequenceIDs() {
    var testcases = "";
    $(".testCaseInTestSequence").each(function () {
        testcases = testcases + this.id + "/";
    });
    return testcases.slice(0, -1);
}

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

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

function assignPreconditionInputListener() {
    document.getElementById('preconditionInput').addEventListener('keypress', function (e) {
        var key = e.which || e.keyCode;
        if (key === 13) { // 13 is enter
            addPrecondition(e);
        }
    });

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

    $('.deletePrecondition').click(function (event) {
        deletePrecondition(event);
    });
}

function addPrecondition(event) {
    event.preventDefault();
    text = document.getElementById('preconditionInput').value.trim();

    if (text.length > 0) {
        var preconditions = document.getElementById('preconditionsList');
        var li = document.createElement("li");
        li.className = 'list-group-item preconditionItem';

        var span = document.createElement("span");
        span.appendChild(document.createTextNode(text));
        li.appendChild(span);

        var button = document.createElement("button");
        button.className = "close ml-2 list-line-item btn-sm deletePrecondition pull-right";
        var span = document.createElement("span");
        span.className = "d-none d-sm-inline";
        span.innerHTML = "x";
        button.appendChild(span);
        button.addEventListener("click", function(event) {
            deletePrecondition(event);
        });

        li.appendChild(button);
        li.id = $('#preconditionsList').length

        preconditions.appendChild(li)

    }
    document.getElementById('preconditionInput').value = "";
}

function deletePrecondition(event) {
    event.preventDefault();

    var li = event.target.parentNode;
    li.remove();
}

function getPreconditions() {
    var arr = $('.preconditionItem').find('span:first').map(function () {
        return $(this).text();
    }).get();
    return arr;
}

function preconditionsChanged() {
    return testsequence.SequenceVersions[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 = "buttonNewTestSequence";
            break;
        case "S":
        case "s":
            buttonString = "buttonSaveTestSequence";
            break;
        case "Escape":
            if (document.getElementById("buttonAbort") != null) {
                buttonString = "buttonAbort";
            } else {
                buttonString = "buttonAbortEdit";
            }
            break;
    }
    if (document.getElementById(buttonString) != null) {
        document.getElementById(buttonString).click();
    }
}

$('.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"));