/*
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/project/project-tabs.js");
$.getScript("/static/js/util/ajax.js");
$.getScript("/static/js/project/testprotocols.js");

//Initialize variables with id's
var dropdown = "#versions-dropdown-menu";
var dashboardVariantHeader = "#variantNames";
var dashboardContent = "#dashboard-content";
var dashboardToggle = "#dashboardToggle";

/**
 * Initialize an on change listener and runs refreshDashboard with the correct boolean dependent on the toggle state.
 *
 * @param id the id of the element that should be listened
 */
function onChangeInit(id) {
    $(id).on("change", function () {

        destroyDataTable();

        if ($(dashboardToggle).prop('checked')) {
            refreshDashboard(casesDashboardElements, versions, variants, $(dropdown)[0].selectedIndex, true);
        } else {
            refreshDashboard(sequencesDashboardElements, versions, variants, $(dropdown)[0].selectedIndex, false);
        }

        initDataTable()
    });

}


$(function () {
    /** Loads the dashboard after changing variants in the dropdown. */
    onChangeInit(dashboardToggle);

    /** Loads the dashboard with cases or sequences, dependent on the state of the toggle. */
    onChangeInit(dropdown);
});


/**
 * Fills the dropdown with all variants of the project
 * @param variants an array with all variants of the project
 */
function fillDropdown(variants) {
    variants.forEach(function (element) {

        let el = document.createElement("option");
        el.textContent = element.Name;
        $(dropdown).append(el);
    });
}

/**
 * Refreshes the dashboard with new results after switching between variants in the dropdown.
 *
 * @param dashboardElements elements of the Dashboard, can be cases or sequence dashboard elements.
 * @param versions all Versions of the project
 * @param variants all variants of the project
 * @param selectedIndex the selectedIndex of the variant dropdown
 * @param toggleState the state of the toggle between cases and sequences
 */
function refreshDashboard(dashboardElements, versions, variants, selectedIndex, toggleState) {
    changeDashboardVersionHeader(versions, toggleState);
    fillDashboardWithResults(variants, dashboardElements, selectedIndex, toggleState);
    $('[data-toggle="tooltip"]').tooltip();
}

/**
 * Loads a new Dashboard and selects the first entry of the variant dropdown.
 *
 * @param dashboardElements elements of the Dashboard, can be cases or sequence dashboard elements.
 * @param versions all Versions of the project
 * @param variants all variants of the project
 * @param toggleState the state of the toggle between cases and sequences
 */
function newSiteDashboard(dashboardElements, versions, variants, toggleState) {
    changeDashboardVersionHeader(versions, toggleState);
    fillDashboardWithResults(variants, dashboardElements, 0, toggleState);
}

/**
 * Changes the Header of the Dashboard, dependent on the dashboard toggle.
 *
 * @param versions all Versions of the project
 * @param toggleState the state of the toggle between cases and sequences
 */
function changeDashboardVersionHeader(versions, toggleState) {
    if (toggleState) {
        $(dashboardVariantHeader).html("<th>" + stringTestCases + "</th>");
    } else {
        $(dashboardVariantHeader).html("<th>" + stringTestSequences + "</th>");
    }

    versions.forEach(function (element) {
        let el = document.createElement("th");
        el.setAttribute("class", "text-center");
        el.textContent = element.Name;
        $(dashboardVariantHeader).append(el);
    });
}

/**
 * Fills the dashboard with cases/sequences and the correct results.
 *
 * @param variants all variants of the project
 * @param dashboardElements elements of the Dashboard, can be cases or sequence dashboard elements.
 * @param selectedIndex the selectedIndex of the variant dropdown
 * @param toggleState the state of the toggle between cases and sequences
 */
function fillDashboardWithResults(variants, dashboardElements, selectedIndex, toggleState) {
    const variant = variants[selectedIndex];
    let results = null;
    dashboardElements.forEach(function (element) {
        if (variant.Name === element.Variant.Name) {
            results = element.Results;
        }
    });

    $(dashboardContent).html("");

    createTable(results, toggleState);
}

/**
 * Creates a table with cases or sequences as lines.
 *
 * @param results results of the dashboard element, dependent on the selected variant
 * @param toggleState the state of the toggle between cases and sequences
 */
function createTable(results, toggleState) {
    results.forEach(function (result) {
        let protocols = result.Protocols;
        let dashboardLine = document.createElement("tr");
        //test objects are cases or sequences
        let testObject = document.createElement("td");
        let testObjectName;
        if (toggleState) {
            testObjectName = result.TestCase.Name.toString();
        } else {
            testObjectName = result.TestSequence.Name.toString();
        }

        testObject.setAttribute("class", "align-middle testCaseCell");
        testObject.setAttribute("id", testObjectName);
        testObject.textContent = testObjectName;
        if (toggleState) {
            testObject.onclick = onTestCaseClick;
        } else {
            testObject.onclick = onTestSequenceClick;
        }
        dashboardLine.appendChild(testObject);

        fillTableWithIcons(result, testObjectName, protocols, dashboardLine, toggleState);

        $(dashboardContent).append(dashboardLine);
    });
}

/**
 * Fills each line with the correct results
 *
 * @param result the result of the test case/sequence
 * @param testObjectName name of the test case/sequence
 * @param protocols
 * @param dashboardLine current line to fill
 */
function fillTableWithIcons(result, testObjectName, protocols, dashboardLine, toggleState) {
    let protocolIndex = 0;
    result.Results.forEach(function (result) {
        let el = document.createElement("td");
        let resultIcon;

        el.setAttribute("class", "text-center resultCaseCell");
        el.setAttribute("style", "font-size:1.5em;");
        el.setAttribute("id", testObjectName + protocolIndex.toString());
        //Adds click handler for different results
        if (result === 4) {
            resultIcon = initResultIcon(result, null, toggleState);
            onTestCaseNotApplicableHandler(el, testObjectName);
            // if the test case is not applicable no protocol is sent
        } else if (protocols[protocolIndex].ProtocolNr === 0) { // required comparison to distinguish between "not assessed" and "not executed"
            resultIcon = initResultIcon(result, null, toggleState);
            onTestResultClickHandler(el, testObjectName, null, toggleState);
            protocolIndex++; // not assessed sends empty protocol
        } else {
            resultIcon = initResultIcon(result, protocols[protocolIndex], toggleState);
            onTestResultClickHandler(el, testObjectName, protocols[protocolIndex], toggleState);
            protocolIndex++;
        }

        el.appendChild(resultIcon);

        dashboardLine.appendChild(el);
    });
}

/**
 * Initalize the result Icon, dependent on the result.
 *
 * @param result result of an test case/sequence
 * @param protocol
 * @param toggleState the state of the toggle between cases and sequences
 * @returns {HTMLElement} the result Icon as HTML
 */
function initResultIcon(result, protocol, toggleState) {
    let resultIcon = document.createElement("i");
    resultIcon.setAttribute("aria-hidden", "true");
    resultIcon.setAttribute("data-toggle", "tooltip");
    resultIcon.setAttribute("data-placement", "bottom");

    if (result === 0) {
        resultIcon.setAttribute("class", "fa fa-times-circle text-secondary");
        if (toggleState) {
            resultIcon.setAttribute("title", stringTestCaseNotYetExecuted);
        } else {
            resultIcon.setAttribute("title", stringTestSequenceNotYetExecuted);
        }
        if (protocol !== null) {
            resultIcon.setAttribute("class", "fa fa-question-circle text-secondary");
            if (toggleState) {
                initTooltip(resultIcon, stringNotAssessed, protocol, true);
            } else {
                initTooltip(resultIcon, stringNotAssessed, protocol, false);
            }
        }
        return resultIcon;
    }

    if (result === 1) {
        resultIcon.setAttribute("class", "fa fa-check-circle text-success");
        resultIcon.setAttribute("title", stringPassed);
        if (toggleState) {
            initTooltip(resultIcon, stringPassed, protocol, true);
        } else {
            initTooltip(resultIcon, stringPassed, protocol, false);
        }
        return resultIcon;
    }

    if (result === 2) {
        resultIcon.setAttribute("class", "fa fa-exclamation-circle text-warning");
        resultIcon.setAttribute("title", stringPartiallySuccessful);
        if (toggleState) {
            initTooltip(resultIcon, stringPartiallySuccessful, protocol, true);
        } else {
            initTooltip(resultIcon, stringPartiallySuccessful, protocol, false);
        }
        return resultIcon;
    }

    if (result === 3) {
        resultIcon.setAttribute("class", "fa fa-exclamation-circle text-danger");
        resultIcon.setAttribute("title", stringFailed);
        if (toggleState) {
            initTooltip(resultIcon, stringFailed, protocol, true);
        } else {
            initTooltip(resultIcon, stringFailed, protocol, false);
        }
        return resultIcon;
    }

    if (result === 4) {
        resultIcon.setAttribute("class", "fa fa-minus text-secondary");
        if (toggleState) {
            resultIcon.setAttribute("title", stringTestCaseIsNotApplicable);
        } else {
            resultIcon.setAttribute("title", stringTestSequenceIsNotApplicable);
        }
        return resultIcon;
    }
}

/**
 * OnClick listener for testcase cells
 *
 * @param event
 */
function onTestCaseClick(event) {
    $("#tabButtonDashboard").removeClass("active");
    $("#menuButtonDashboard").removeClass("active");

    //Testcase page
    $("#tabButtonTestCases").addClass("active");
    $("#menuButtonTestCases").addClass("active");
    let url = getProjectTabURL().toString().replace("dashboard", "testcases");
    url = url + "/" + event.target.id.toString();
    ajaxRequestFragment(event, url.toString(), "", "GET");
}

/**
 * Onclick handler for result cells
 *
 * @param element
 * @param testName
 * @param protocol
 * @param isCase
 */
function onTestResultClickHandler(element, testName, protocol, isCase) {
    element.addEventListener("click", function (event) {
        if (protocol != null) {
            $('body>.tooltip').remove();

            //Adds right tab highlighting
            $("#tabButtonDashboard").removeClass("active");
            $("#menuButtonDashboard").removeClass("active");
            $("#tabButtonProtocols").addClass("active");
            $("#menuButtonProtocols").addClass("active");

            //links to the test case protocol
            let url = getProjectTabURL().toString().replace("dashboard", "protocols");
            if (isCase) {
                url = url + "/testcases/" + testName;
            } else {
                url = url + "/testsequences/" + testName;
            }
            url = url + "/" + protocol.ProtocolNr.toString();
            ajaxRequestFragment(event, url.toString(), "", "GET");
        } else {
            //if no protocol exists, gives feedback to user
            $('#errorModalTitle').html("No Execution");
            $('#errorModalBody').html("The test " + testName + " has not yet been executed");
            $('#modal-generic-error').modal('show');
        }
    });
}

/**
 * Shows alert for user if test case is not applicable for the version
 *
 * @param element
 * @param testName
 */
function onTestCaseNotApplicableHandler(element, testName) {
    element.addEventListener("click", function (event) {
        $('#errorModalTitle').html("Not Applicable");
        $('#errorModalBody').html("The test " + testName + " is not applicable for this version");
        $('#modal-generic-error').modal('show');
    });
}

/**
 * Shows a tooltip for resultIcons
 *
 * @param resultIcon
 * @param result
 * @param protocol
 * @param toggleState
 */
function initTooltip(resultIcon, result, protocol, toggleState) {
    const time = new Date(protocol.ExecutionDate).toLocaleString();
    let comment;
    if (toggleState) {
        if (protocol.Comment === "") {
            comment = stringNoComment;
        } else if (protocol.Comment.length > 50) {
            comment = protocol.Comment.substring(0, 49) + "...";
        } else {
            comment = protocol.Comment;
        }
        resultIcon.setAttribute("title", stringResult + ": " + result + "\n" + stringTester + ": " + protocol.UserName.toString() + "\n" + stringComment + ": " + comment + "\n" + stringDate + ": " + time);
    } else {
        resultIcon.setAttribute("title", stringResult + ": " + result + "\n" + stringTester + ": " + protocol.UserName.toString() + "\n" + stringDate + ": " + time);
    }
}

/**
 * Onclick listener for testsequence cells
 * @param event
 */
function onTestSequenceClick(event) {
    $("#tabButtonDashboard").removeClass("active");
    $("#menuButtonDashboard").removeClass("active");

    //Testsequence page
    $("#tabButtonTestSequences").addClass("active");
    $("#menuButtonTestSequences").addClass("active");
    let url = getProjectTabURL().toString().replace("dashboard", "testsequences");
    url = url + "/" + event.target.id.toString();
    ajaxRequestFragment(event, url.toString(), "", "GET");
}

/**
 * Set up the table (sorting by column etc) ...
 */
function initDataTable() {

    const searchField = $("#dashboard-search").val("");

    const dataTable = $("#dashboardTable").DataTable({
        "sDom": '<"top">t<"bottom"><"clear">',

        "paging": false,

        "language": {"zeroRecords": `There is nothing to display currently ...`}
    });

    searchField
        .off("keyup")
        .on("keyup", function () {
            dataTable.search(this.value).draw()
        });

}

/**
 * Destroys the DataTable. This function is needed to destroy the DataTable before external changes.
 * After external changes are complete, "initDataTable" must be called.
 */
function destroyDataTable() {
    if ($.fn.DataTable.isDataTable("#dashboardTable")) {
        $('#dashboardTable').DataTable().clear().destroy();
    }
}