/*
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() {
    $('#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>");
    userCell.text(result.UserName);

    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, "", "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");
}

//reacts to filter settings (displays/hides rows)
function updateFilter() {
    var showPassed = document.getElementById("filterSuccessfulTest").checked;
    var showPartiallySuccessful = document.getElementById("filterPartiallySuccessful").checked;
    var showFailed = document.getElementById("filterFailedTest").checked;
    var showNotAssessed = document.getElementById("filterNotAssessedTest").checked;

    for(var i = 0; i < protocolData.length; i++) {
        var protocol = protocolData[i];
        var 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");
}

// requests data for a protocol view
function fillTestCaseResult() {
    var oldURL = currentURL();
    var testType = oldURL.segments[4];
    var testName = oldURL.segments[5];
    url = getProjectURL().appendSegment("protocols").appendSegment(testType).appendSegment(testName);
    selectedProtocolNr = oldURL.segments[6];
    getProtocolData(singleDataReceive);
    url = getProjectURL().appendSegment("testcases").appendSegment(testName).appendSegment("json");
    getProtocolData(testCaseDataReceive);
}

// processes (parses) a single protocol
function singleDataReceive(object) {
    if (object.responseText !== "") {
        protocolData = JSON.parse(object.responseText);
        dataComplete = dataComplete + 1;
        checkIfDataReceiveComplete();
    } else {
        //Error receiving data from server
    }
}

// processes (parses) data of a single test case
function testCaseDataReceive(object) {
    if (object.responseText !== "") {
        testCaseData = JSON.parse(object.responseText);
        dataComplete = dataComplete + 2;
        checkIfDataReceiveComplete();
    } else {
        //Error receiving data from server
    }
}

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

// checks if all needed data (test case and protocol data) is received
function checkIfDataReceiveComplete() {
    if(dataComplete === 3) {
        testCaseVersion = getProtocolFromArray(protocolData,selectedProtocolNr).TestVersion.TestVersion;
        fillTestCaseResultTemplate(getProtocolFromArray(protocolData,selectedProtocolNr), testCaseData);
    } else {
        //Bad Request (not existing protocol)
    }
}


// fills the received data into the protocol template
function fillTestCaseResultTemplate(result, data) {
    // Button actions
    $("#buttonBack")             .click(backToTestProtocolList);
    $("#protocolName").text("Protocol of " + result.TestVersion.Test);

    // Result icon
    $("#contentTestCaseResult").append(generateResultIcon(result.Result));

    // Result comment
    if(result.Comment !== "") {
        $("#contentTestCaseNotesContainer").removeClass("d-none");
        $("#contentTestCaseNotes").text(result.Comment);
    }


    var testCaseVersionData = data.TestCaseVersions[data.TestCaseVersions.length - testCaseVersion];

    //Description and Conditions
    $("#contentTestCaseDescription").text(testCaseVersionData.Description);
    populatePreconditions(result.PreconditionResults);


    // Step result accordion
    var stepList = $("#testStepsResultAccordion");

    for(i = 0; i < result.StepProtocols.length; i++ ) {
        var listElement = $("<li class=\"list-group-item\"></li>");
        var elementHeader = $("<a></a>");
        elementHeader.append(generateResultIcon(result.StepProtocols[i].Result)) ;
        if(testCaseVersionData.Steps[i] !== undefined) {
            elementHeader.append(" "+testCaseVersionData.Steps[i].Action);
        }
        var elementToogle = $("<a data-toggle=\"collapse\" aria-expanded=\"false\" " +
            "data-parent=\"#testStepsResultAccordion\" class=\"float-right\"></a>");
        elementToogle.attr("href","#testStepsResultAccordion"+i);
        elementToogle.attr("aria-controls","#testStepsResultAccordion"+i);
        elementToogle.append("<i class=\"fa fa-chevron-down d-print-none\" aria-hidden=\"true\"></i>");
        listElement.append(elementHeader);
        listElement.append(elementToogle);

        var elementBody = $("<div class=\"collapse\" role=\"tabpanel\">");
        elementBody.attr("id","testStepsResultAccordion"+i);

        var elementBodyTable = $("<table></table>");
        var expectedRow = $("<tr></tr>");
        expectedRow.append("<td>Expected: </td>");
        if(testCaseVersionData.Steps[i] !== undefined) {
            expectedRow.append("<td>" + testCaseVersionData.Steps[i].ExpectedResult +"</td>");
        }
        elementBodyTable.append(expectedRow);
        var observedRow = $("<tr></tr>");
        observedRow.append("<td>Observed: </td>");
        observedRow.append("<td>" +result.StepProtocols[i].ObservedBehavior+"</td>");
        elementBodyTable.append(observedRow);
        if(result.StepProtocols[i].Comment !== "") {
            var notesRow = $("<tr></tr>");
            notesRow.append("<td>Notes: </td>");
            notesRow.append("<td>" +result.StepProtocols[i].Comment+"</td>");
            elementBodyTable.append(notesRow);
        }
        elementBody.append(elementBodyTable);

        listElement.append(elementBody);
        stepList.append(listElement);
    }

    //Infos of sidebar
    var executionDate = $("<time></time>");
    executionDate.addClass("timeago");
    executionDate.attr("datetime",result.ExecutionDate);
    var time = new Date(result.ExecutionDate).toLocaleString();
    executionDate.text(time);
    $("#contentTestCaseExecutionDate").append(executionDate);
    $("#contentTestCaseTester").text(result.UserName);
    $("#contentTestCaseSUTVariant").text(result.SUTVariant);
    $("#contentTestCaseSUTVersion").text(result.SUTVersion);
    var version = $("<span></span>");
    version.addClass("cursor-clickable");
    version.click(function(event) {
        var testURL = getProjectURL().appendSegment("testcases").appendSegment(result.TestVersion.Test).toString();
        ajaxRequestFragmentWithHistory(event, testURL, {version: result.TestVersion.TestVersion}, "GET",
            testURL + "?version=" + result.TestVersion.TestVersion);
        updateTabs("testcases");
    });
    version.text(result.TestVersion.TestVersion);
    $("#contentTestVersion").html(version);
}

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);
}

/* 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;
}

// requests data for a test sequence protocol
function fillTestSequenceResult() {
    var oldURL = currentURL();
    var testType = oldURL.segments[4];
    var testName = oldURL.segments[5];
    url = getProjectURL().appendSegment("protocols").appendSegment(testType).appendSegment(testName);
    selectedProtocolNr = oldURL.segments[6];
    getProtocolData(testSequenceResultReceive);
    url = getProjectURL().appendSegment("testsequences").appendSegment(testName).appendSegment("json");
    getProtocolData(testSequenceDataReceive);
}
// processes (parses) received protocol data
function testSequenceResultReceive(object) {
    if (object.responseText !== "") {
        protocolData = JSON.parse(object.responseText);
        dataComplete = dataComplete + 1;
        checkIfSequenceDataReceiveComplete();
    } else {
        //Error receiving data from server
    }
}

// processes (parses) received test sequence data
function testSequenceDataReceive(object) {
    if (object.responseText !== "") {
        testSequenceData = JSON.parse(object.responseText);
        dataComplete = dataComplete + 2;
        checkIfSequenceDataReceiveComplete();
    } else {
        //Error receiving data from server
    }
}

// checks if all needed data is received
function checkIfSequenceDataReceiveComplete() {
    if(dataComplete === 3) {
        testSequenceVersion = getProtocolFromArray(protocolData,selectedProtocolNr).TestVersion.TestVersion;
        fillTestSequenceResultTemplate(getProtocolFromArray(protocolData,selectedProtocolNr), testSequenceData);
    } else {
        //Bad Request (not existing protocol)
    }
}

// fills received data into the template
function fillTestSequenceResultTemplate(result, data) {
    $("#buttonBack").click(backToTestProtocolList);
    $("#protocolName").text("Protocol of " + data.Name);
    $("#contentTestSequenceDescription").text(data.SequenceVersions[data.SequenceVersions.length-result.TestVersion.TestVersion].Description);
    populatePreconditions(result.PreconditionResults);
    $("#contentTestSequenceResult").empty().append(generateResultIcon(getProtocolFromArray(protocolData,selectedProtocolNr).Result));

    table.empty();
    if (result.CaseExecutionProtocols != null) {
        for(var i = 0; i < result.CaseExecutionProtocols.length; i++) {
            url = getProjectURL().appendSegment("protocols").appendSegment("testcases").appendSegment(result.CaseExecutionProtocols[i].Test);
            selectedProtocolNr = result.CaseExecutionProtocols[i].ProtocolNr;
            getProtocolData(testCaseDataInTestsequenceReceive);
        }
    }


    $("#contentTestSequenceSUTVariant").text(result.SUTVariant);
    $("#contentTestSequenceSUTVersion").text(result.SUTVersion);
    $("#contentTestSequenceTester").text(result.UserName);
    var version = $("<a></a>");
    version.click(function(event) {
        var testURL = getProjectURL().appendSegment("testsequences").appendSegment(result.TestVersion.Test).toString();
        ajaxRequestFragment(event, testURL+ "?version=" + result.TestVersion.TestVersion, "", "GET");
        updateTabs("testsequences");
    });
    version.text(result.TestVersion.TestVersion);
    version.attr("href", "");
    $("#contentTestVersion").html(version);


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

    //$("#contentTestCaseTester").text("Description of " + result.user);
    $("#contentTestCaseSUTVariant").text(result.SUTVariant);
    $("#contentTestCaseSUTVersion").text(result.SUTVersion);
}

//processes (parses) data of a test case in a test sequence
function testCaseDataInTestsequenceReceive(object) {
    if (object.responseText !== "") {
        testCaseData = JSON.parse(object.responseText);
        addInTestSequenceRow(testCaseData[0]);
        jQuery("time.timeago").timeago();
    } else {
        //Error receiving data from server
    }
}

//adds a row with data of a test sequence in the test sequence protocol table
function addInTestSequenceRow(result) {
    var row = $("<tr></tr>");
    row.attr("id",result.ProtocolNr);
    row.attr("style","cursor: pointer;");
    row.attr("test",result.TestVersion.Test);
    row.attr("protocol",result.ProtocolNr);
    row.click(showTestCaseProtocol);

    var resultCell = $("<td></td>");

    resultCell.append(generateResultIcon(result.Result));

    var nameCell = $("<td></td>");
    url = getProjectURL().appendSegment("testcases").appendSegment(result.TestVersion.Test).appendSegment("json");
    getProtocolData(function(object) {
        if (object.responseText !== "") {
            var testCase = JSON.parse(object.responseText);
            nameCell.text(testCase.Name);
        } else {
            //Error receiving data from server
        }
    });
    var executionDate = $("<time></time>");
    executionDate.addClass("timeago");
    executionDate.attr("datetime",result.ExecutionDate);
    var time = new Date(result.ExecutionDate).toLocaleString();
    executionDate.text(time);
    $("#contentTestSequenceExecutionDate").empty().append(executionDate);



    //var timeCell = $("<td></td>");
    //var time = new Date(result.ExecutionDate).toLocaleString();
    //timeCell.text(time);

    row.append(resultCell);
    row.append(nameCell);

    table.append(row);
}

/**
 * Prints protocol to PDF.
 *
 * @returns {boolean} - Always false
 */
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);
}