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

var xmlhttp = new XMLHttpRequest();
var url;
var protocolData;
var testCaseData;
var testSequenceData;
var dataComplete = 0;
var testCaseVersion;
var testSequenceVersion;
var selectedType;
var selectedProtocolNr;
var table = $("#protocolTable");
var selectCases = $("#inputSelectTestCase");
var selectSequences = $("#inputSelectTestSequence");
var selectedCase = getCookie("selected");
var selectedSequence = selectSequences.val();

function assignListeners() {
    $("#buttonBack").click(backToTestProtocolList);
    $('#buttonPdf').on("click", printPdf);
}

// evaluate selected Type and forward event
function showProtocol(event) {
    if (selectedType === "testsequences") {
        showTestSequenceProtocol(event);
    } else {
        showTestCaseProtocol(event);
    }
}

// listener for radiobuttons
function typeSelection() {
    selectedType = $("input[name=protocolType]:checked").val();
    if (selectedType === "testsequences") {
        selectSequences.removeClass("d-none");
        selectCases.addClass("d-none");
        sequenceSelection();
    } else {
        selectCases.removeClass("d-none");
        selectSequences.addClass("d-none");
        caseSelection();
    }
}

// listener for test case drop down
function caseSelection() {
    selectedCase = selectCases.val();
    if (selectedCase === "none") {
        placeholderTable("Select a test case to display protocols.");
    } else {
        // fetches protocol data and creates table
        url = getProjectURL().appendSegment("protocols").appendSegment(selectedType).appendSegment(selectedCase);
        getProtocolData(listDataReceive);
        var urlSeg = getProjectTabURL();
        var updatedURL = urlSeg.toString() + "/?type=" + selectedType + "&selected=" + selectedCase;
        history.pushState('data', '', updatedURL);
    }
}

// listener for test sequence drop down
function sequenceSelection() {
    selectedSequence = selectSequences.val();
    if (selectedSequence === "none") {
        placeholderTable("Select a test sequence to display protocols.");
    } else {
        // fetches protocol data and creates table
        url = getProjectURL().appendSegment("protocols").appendSegment(selectedType).appendSegment(selectedSequence);
        getProtocolData(listDataReceive);
        var urlSeg = getProjectTabURL();
        var updatedURL = urlSeg + "/?type=" + selectedType + "&selected=" + selectedSequence;
        history.pushState('data', '', updatedURL);
    }
}

//fetches protocol data
function getProtocolData(fkt) {
    xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", url.toString(), true);
    xmlhttp.send();

    xmlhttp.onreadystatechange = function () {
        if (this.readyState === 4 && this.status === 200) {
            fkt(this);
        }
    }
}

// processes (parses) received json data
function listDataReceive(object) {
    if (object.responseText !== "") {
        protocolData = JSON.parse(object.responseText);
        fillTable();
    } else {
        if (selectedType === "testsequences") {
            placeholderTable("There are no test sequence protocols yet. Run some test sequences to get protocols.");
        } else {
            placeholderTable("There are no test case protocols yet. Run some test cases or sequences to get protocols.");
        }
    }
}

// empties table
function placeholderTable(message) {
    table.empty();
    table.append($("<tr></tr>")
        .append($("<td></td>").attr("colspan", 4).text(message)));

}

// fills table with data from protocolData
function fillTable() {
    table.empty();
    for (var i = 0; i < protocolData.length; i++) {
        addRow(protocolData[i]);
    }
    updateFilter();
    jQuery("time.timeago").timeago();
}

// adds a row to the table
function addRow(result) {
    var row = $("<tr></tr>");
    row.attr("id", result.TestVersion.TestVersion + "-" + result.ProtocolNr);
    row.attr("style", "cursor: pointer;");
    row.attr("class", "protocolLine");
    row.attr("test", result.TestVersion.Test);
    row.attr("protocol", result.ProtocolNr);
    row.click(showProtocol);

    var resultCell = $("<td></td>");
    resultCell.append(generateResultIcon(result.Result));

    var versionCell = $("<td></td>");
    versionCell.text(result.SUTVersion);

    var variantCell = $("<td></td>");
    variantCell.text(result.SUTVariant);

    var userCell = $("<td></td>");
    if (!result.IsAnonymous) {
        userCell.text(result.UserName);
    } else {
        userCell.text("anonymous");
    }

    var executionDateCell = $("<td></td>");
    var executionDate = $("<time></time>");
    executionDate.addClass("timeago");
    executionDate.attr("datetime", result.ExecutionDate);
    var time = new Date(result.ExecutionDate).toLocaleString();
    executionDate.text(time);
    executionDateCell.append(executionDate);

    row.append(resultCell);
    row.append(versionCell);
    row.append(variantCell);
    row.append(userCell);
    row.append(executionDateCell);

    row.append(userCell);

    row.append(executionDateCell);

    table.append(row);
}

/* loads the chosen test case protocol*/

// noinspection JSUnusedGlobalSymbols
function showTestCaseProtocol(event) {
    var loadurl = getProjectURL().appendSegment("protocols").appendSegment("testcases").appendSegment($(event.target.parentElement).attr("test")).appendSegment($(event.target.parentElement).attr("protocol")).toString();

    document.cookie = "selected=" + $(event.target.parentElement).attr("test") + ";";
    document.cookie = "type=testcases;";

    ajaxRequestFragment(event, loadurl, {protocolNr: $(event.target.parentElement).attr("protocol")}, "GET");
}

/* loads the chosen test sequence protocol*/
// noinspection JSUnusedGlobalSymbols
function showTestSequenceProtocol(event) {
    var loadurl = getProjectURL().appendSegment("protocols").appendSegment("testsequences").appendSegment($(event.target.parentElement).attr("test")).appendSegment($(event.target.parentElement).attr("protocol")).toString();
    document.cookie = "selected=" + $(event.target.parentElement).attr("test") + ";";
    document.cookie = "type=testsequences;";
    ajaxRequestFragment(event, loadurl, "", "GET");
}

/**
 * This function is called when the filter settings on the right are changed.
 * Also called when a case/sequence is chosen to update the table (applying the filter settings).
 */
function updateFilter() {
    const showPassed = document.getElementById("filterSuccessfulTest").checked;
    const showPartiallySuccessful = document.getElementById("filterPartiallySuccessfulTest").checked;
    const showFailed = document.getElementById("filterFailedTest").checked;
    const showNotAssessed = document.getElementById("filterNotAssessedTest").checked;

    for (let i = 0; i < protocolData.length; i++) {
        const protocol = protocolData[i];
        const protocolFieldID = protocol.TestVersion.TestVersion + "-" + protocol.ProtocolNr;

        switch (protocol.Result) {
            case 1:
                //pass
                if (showPassed)
                    filterVersionVariant(protocol);
                else
                    hideRow(protocolFieldID);
                break;
            case 2:
                //PartiallySuccessful
                if (showPartiallySuccessful)
                    filterVersionVariant(protocol);
                else
                    hideRow(protocolFieldID);
                break;
            case 3:
                //fail
                if (showFailed)
                    filterVersionVariant(protocol);
                else
                    hideRow(protocolFieldID);
                break;
            default:
                //Not Assessed
                if (showNotAssessed)
                    filterVersionVariant(protocol);
                else
                    hideRow(protocolFieldID);
                break;
        }
    }
}

// filter according to the selected version/variant
function filterVersionVariant(protocol) {
    var showVersion = $("#inputSelectTestVersion").val();
    var showVariant = $("#inputSelectTestVariant").val();

    if ((showVersion !== "all" && protocol.SUTVersion !== showVersion) ||
        (showVariant !== "all" && protocol.SUTVariant !== showVariant)) {
        hideRow(protocol.TestVersion.TestVersion + "-" + protocol.ProtocolNr);
    } else {
        showRow(protocol.TestVersion.TestVersion + "-" + protocol.ProtocolNr);
    }
}

// hides a row
function hideRow(protocolFieldID) {
    $("tr#" + protocolFieldID).addClass("d-none");
}

// unhides a row
function showRow(protocolFieldID) {
    $("tr#" + protocolFieldID).removeClass("d-none");
}

// array search helper
function getProtocolFromArray(array, protocolNr) {
    for (i = 0; i < array.length; i++) {
        if (array[i].ProtocolNr === protocolNr) {
            return array[i];
        }
    }
}

function populatePreconditions(preconditionData) {
    if (preconditionData != null && !preconditionData.length === 0) {
        for (var i = 0; i < preconditionData.length; i++) {
            element = preconditionData[i];
            createPreconditionElement(element.Precondition.Content, element.Result)
        }
    } else {
        var ul = document.getElementById("contentPreconditions");

        li = document.createElement('li');
        li.className = "list-group-item preconditionItem";

        span = document.createElement('span');
        span.innerHTML = "No Conditions";
        li.appendChild(span);
        ul.appendChild(li);
    }
}

function createPreconditionElement(value, key) {
    var ul = document.getElementById("contentPreconditions");

    li = document.createElement('li');
    li.className = "list-group-item preconditionItem";

    spanContent = document.createElement('span');
    spanContent.innerHTML = value;
    li.appendChild(spanContent);

    spanResult = document.createElement('span');
    spanResult.className = "italic";
    spanResult.innerHTML = " (" + key + ")";
    li.appendChild(spanResult);

    ul.appendChild(li);
}

// openTestVersion opens the test version that belongs to this protocol
// without reloading the whole page
function openTestVersion(event, testURL, testVersionNr) {
    var testURL = testURL.toString(); // encode link
    ajaxRequestFragmentWithHistory(event, testURL, "", "GET", testURL + "?version=" + testVersionNr);
    updateTabs(testURL.split("/")[3]);
}

/* steps back to the test case list */
function backToTestProtocolList(event) {
    var requestURL = getProjectURL().appendSegment("protocols").toString() + "/";
    requestURL += "?type=" + getCookie("type") + "&selected=" + getCookie("selected");
    ajaxRequestFragment(event, requestURL, "", "GET");
}

// cookie helper
function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) === ' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name) === 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}

//icon helper
function generateResultIcon(resultState) {
    var icon = $("<i></i>");
    icon.attr("class", "fa");
    icon.attr("aria-hidden", "true");
    icon.attr("data-toggle", "tooltip");
    icon.attr("data-placement", "bottom");

    switch (resultState) {
        case 1:
            //pass
            icon.addClass("fa-check-circle text-success");
            icon.attr("title", "Passed");
            break;
        case 2:
            //PartiallySuccessful
            icon.addClass("fa-info-circle text-warning");
            icon.attr("title", "Partially Successful");
            break;
        case 3:
            //fail
            icon.addClass("fa-times-circle text-danger");
            icon.attr("title", "Failed");
            break;
        default:
            //Not Assessed
            icon.addClass("fa-question-circle text-secondary");
            icon.attr("title", "Not Assessed");
            break;
    }
    return icon;
}

function printPdf() {
    const requestURL = currentURL();
    requestURL.appendSegment("pdf");

    const protocolNr = requestURL.segments[6];

    const req = new XMLHttpRequest();
    req.open("POST", requestURL, true);
    req.responseType = "blob";
    req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    req.send('testVersion=' + $('#contentTestVersion').text() + '&protocolNr=' + protocolNr);
    req.onreadystatechange = function handler() {

        if (req.status === 200 && this.readyState === this.DONE) {
            const blob = req.response;
            const link = document.createElement('a');

            link.href = window.URL.createObjectURL(blob);
            link.download = getFileNameByContentDisposition(req.getResponseHeader('Content-Disposition'));

            document.body.appendChild(link);
            link.click();

            setTimeout(function () {
                document.body.removeChild(link);
                window.URL.revokeObjectURL(link);
            }, 100);
        }
    };

    return false;
}

/**
 * https://stackoverflow.com/a/50136075
 * Gets the file name out of the content disposition header
 * @param contentDisposition - Response header of the request
 * @returns {string} - The file name
 */
function getFileNameByContentDisposition(contentDisposition){
    const regex = /filename[^;=\n]*=(UTF-8(['"]*))?(.*)/;
    const matches = regex.exec(contentDisposition);
    let filename;

    if (matches !== false && matches[3]) {
        filename = matches[3].replace(/['"]/g, '');
    }

    return decodeURI(filename);
}