/*
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").click(newTestCase);
    $("#buttonNewTestCaseDisabled").click(showSigninHint);
    $("#buttonFirstTestCase").click(newTestCase);
    $("#buttonNewLabel").click(function (event) { /* nothing yet */
    });
    $(".testCaseLine").click(showTestCase);
    $(".filterBadge").click(filterTestCaseListWithBadge);
//Show
    $("#buttonBack").click(backToTestCaseList);
    $("#buttonDeleteTestCase").click(deleteTestCase);
    $("#buttonEdit").click(editTestCase);
    $("#buttonHistory").click(showTestCaseHistory);
//History
    $("#buttonBackToCase").click(backToTestCase);
    $(".versionLine").click(showTestCaseVersion);
//New
    $("#buttonAbort").click(backToTestCaseList);
    $("#testCaseEditForm").on("submit", saveTestCase);
    $("#buttonConfirmSaveWithoutSteps").click(confirmSaveCaseWithoutSteps);
//Edit
    $("#buttonSave").click(updateTestCase);
    $("#buttonAbortEdit").click(backToTestCase);
//Execute
    $("#buttonExecute").click(execute);

    $("#buttonAddTestStep").click(function (event) {
        newTestStep(event);
    });
    $(".buttonDeleteTestStep").click(function (event) {
        buttonDeleteTestStepConfirm(event);
    });
    $(".buttonDeleteTestStepConfirm").click(function (event) {
        deleteTestStep(event);
    });
    $(".buttonEditTestStep").click(function (event) {
        editTestStep(event);
    });
    $("#buttonFinishTestStepEditing").click(function (event) {
        finishEditTestStep(event);
    });

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


//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) {
    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) {
    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) {
    var requestURL = getTestURL().toString();
    ajaxRequestFragment(event, requestURL, "", "GET");
}

/* steps back to the test case list */
function backToTestCaseList(event) {
    var requestURL = getProjectURL().appendSegment("testcases").toString() + "/";
    ajaxRequestFragment(event, requestURL, "", "GET");
}

/* 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() {
    return {
        inputTestCaseName: $('#inputTestCaseName').val(),
        inputTestCaseDescription: $('#inputTestCaseDescription').val(),
        inputTestCasePreconditions: getPreconditions(),
        inputTestCaseSUTVersions: selectedVariants,
        inputTestCaseLabels: getTestCaseLabelsInput(),
        inputHours: getHours(),
        inputMinutes: getMinutes(),
        inputSteps: getStepData()
    }
}

// getTestCaseLabelsInput returns a list with the selected labels
function getTestCaseLabelsInput() {
    // testCaseLabels contains the the labels of the test case
    var testCaseLabels = [];
    //  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 testCaseLabels
    $.each(labelsValues, function(index, value) {
        testCaseLabels.push(projectLabels[value])
    });

    return testCaseLabels;
}

// 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"));
        console.log(newID);

        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() {
    var hours = parseInt($('#inputHours').val());
    if (isNaN(hours)) {
        return 0;
    }
    return hours;
}

// getMinutes returns 0 if no minutes duration was given
function getMinutes() {
    var 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().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) {
    var id = $(event.target).closest("li").prop("id");
    var actText = $('#ActionField', $("#" + id)).text();
    var textAreaA = $('#inputTestStepActionEditField');
    textAreaA.val(actText.toString().replace(/^\s+|\s+$/g, ''));

    var expText = $('#ExpectedResultField', $("#" + id)).text();
    var textAreaE = $('#inputTestStepExpectedResultEditField');
    textAreaE.val(expText.toString().replace(/^\s+|\s+$/g, ''));

    var idArea = $('#testStepIdField');
    idArea.val(id.toString());
    $('#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) {
    var id = $('#testStepIdField').val();

    var actText = $('#inputTestStepActionEditField').val();
    if(actText.trim()) {
        if (id.toString().match(/^\w*-1/g)) {
            finishAddingNewTestStep(event, id);
        } else {
            var actField = $('#ActionField', $("#" + id));
            actField.text(actText.toString());

            var expField = $('#ExpectedResultField', $("#" + id));
            var expText = $('#inputTestStepExpectedResultEditField').val();
            expField.text(expText.toString());

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

}

// newTestStep handles the click-event of the add-test-step-button,
// calls the test-step-edit-modal with empty input fields
function newTestStep(event) {
    var textAreaA = $('#inputTestStepActionEditField');
    textAreaA.val('');
    var textAreaE = $('#inputTestStepExpectedResultEditField');
    textAreaE.val('');
    var idArea = $('#testStepIdField');
    idArea.val("-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) {
    var ind = $("#testStepsAccordion").find("li").length;
    var l = $("#testStepsAccordion").find("li:last");
    var lastId;
    if (ind > 0) {
        lastId = parseInt($(l).attr("id").replace(/^[^\d]+/g, ''), 10);
        ind = lastId + 1;
    } else {
        ind = 0;
    }

    var entry = $("#emptyTestStepElement").find("li");
    entry = entry.clone(true, true);
    $(entry).find("*").each(function () {
        if (this.id === "testStepsAccordion00000") {
            this.id = this.id.toString().replace(/\d+/g, ind);
        }
    });
    entry.find("button").each(function () {
        if (this.classList.contains("buttonDeleteTestStep")) {
            $(this).on("click", null, function (event) {
                buttonDeleteTestStepConfirm(event)
            });
        }
        if (this.classList.contains("buttonDeleteTestStepConfirm")) {
            $(this).on("click", null, function (event) {
                deleteTestStep(event)
            });
        }
        if (this.classList.contains("buttonEditTestStep")) {
            $(this).on("click", null, function (event) {
                editTestStep(event)
            });
        }
    });

    entry[0].id = "testStep" + ind;
    if (ind > 0) {
        $("#testStep" + (ind - 1)).after(entry);
        $(entry).insertAfter($("#testStepsAccordion").lastChild);
    } else {
        $("#testStepsAccordion").append(entry[0]);
    }
    var text = $("#ActionField", entry);
    text.attr("href", "#testStepsAccordion" + ind);
    text.attr("aria-controls", "testStepsAccordion" + ind);

    var actField = $('#ActionField', entry);
    var actText = $('#inputTestStepActionEditField').val();
    actField.text(actText.toString());

    var expField = $('#ExpectedResultField', entry);
    var expText = $('#inputTestStepExpectedResultEditField').val();
    expField.text(expText.toString());

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

// getStepData returns an array of arrays containing the ID, action and
// expected result data of the test step GUI elements
function getStepData() {
    var steps = [];
    var temp = {};
    var stepsRaw = $("#testStepsAccordion").find("li");
    stepsRaw.each(function (index, element) {
        temp.ID = index;
        temp.actual = $("#ActionField", element).text().replace(/^\s+|\s+$/g, '');
        temp.expected = $("#ExpectedResultField", element).text().replace(/^\s+|\s+$/g, '');
        steps.push(temp);
        temp = {};
    });
    return steps;
}

// filters the list to show only elements with the selected badge
function filterTestCaseListWithBadge(event) {
    $(".tooltip").tooltip("hide");

    event.preventDefault();

    var filters = getFilterFromSession();
    filters = handleFilterOnFilters(event.target.id, filters);

    var requestURL = getProjectURL().appendSegment("testcases").toString() + "/";
    requestURL = requestURL + "?fragment=true&filter=";
    requestURL = requestURL + JSON.stringify(filters);

    var posting = $.get(requestURL);

    /* Alerts the results */
    posting.done(function (response) {
        $('#tabarea').empty().append(response);
    }).fail(function (response) {
        $("#modalPlaceholder").empty().append(response.responseText);
        $('#errorModal').modal('show');
    });

    return false;
}

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