/*
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 dataTable;
var selectCases = $("#inputSelectTestCase");
var selectSequences = $("#inputSelectTestSequence");

function assignListeners() {

    $("#buttonBack").off('click').on("click", (event) =>
        backButton(event, function () {
            ajaxRequestFragment(event, getProjectURL().appendSegment("protocols").toString() + "/", "", "GET")
        }));

    $('#buttonPdf').off('click').on("click", printPdf);
    $('#buttonMd').off('click').on('click', printMd);
}

/**
 * Requests the pdf for the protocol and downloads it.
 */
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;
}

/**
 * Requests the markdown-text for the protocol and shows it in a modal.
 */
function printMd() {
    const requestURL = currentURL();
    requestURL.appendSegment("md");

    const protocolNr = requestURL.segments[6];
    const req = new XMLHttpRequest();
    req.open("POST", requestURL, true);
    req.responseType = "text";
    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 text = JSON.parse(req.response);
            $("#protocolMdText").val(text);
            $("#exportMdModal").modal('show');  
        }
    };

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

var Protocol = class Protocol {

    constructor(protocolJsonSnippet, requestSequence) {
        this.result = protocolJsonSnippet.Result;
        this.testName = protocolJsonSnippet.TestVersion.Test;
        this.version = protocolJsonSnippet.SUTVersion;
        this.variant = protocolJsonSnippet.SUTVariant;
        this.tester = protocolJsonSnippet.UserName;
        this.date = protocolJsonSnippet.ExecutionDate;

        this.protocolNr = protocolJsonSnippet.ProtocolNr;
        this.isSequence = requestSequence;
    }

    /**
     * Returns the icon associated with this protocol's result.
     * @returns {string} - An HTML string
     */
    get icon() {
        let iconClass, iconTitle;
        switch (this.result) {
            case 1:
                iconClass = "fa-check-circle text-success";
                iconTitle = "Passed";
                break;
            case 2:
                iconClass = "fa-exclamation-circle text-warning";
                iconTitle = "Partially Successful";
                break;
            case 3:
                iconClass = "fa-exclamation-circle text-danger";
                iconTitle = "Failed";
                break;
            default:
                //Not Assessed
                iconClass = "fa-question-circle text-secondary";
                iconTitle = "Not Assessed";
                break;
        }
        return `<i class="fa ${iconClass}" aria-hidden="true" data-toggle="tooltip" data-placement="bottom" title="${iconTitle}"></i>`
    }

    /**
     * Returns the time ago html snippet for the time.
     * @returns {string}
     */
    get timeAgo() {
        return `<time class="timeago text-muted" datetime="${this.date}"></time>`
    }

    /**
     * Returns this protocol in the required DataTable format. Currently only the first 6 elements are displayed.
     * @returns {*[]}
     */
    toDataTableItem() {
        return [this.icon, this.testName, this.version, this.variant, this.tester, this.timeAgo,

            // Non visible data
            this.protocolNr, this.isSequence]
    }

    /**
     * Returns true if this protocol matches the given criteria, false else.
     * @param displaySuccessful
     * @param displayPartially
     * @param displayFailed
     * @param displayNotAssessed
     * @returns {boolean}
     */
    isMatchingFilter(displaySuccessful, displayPartially, displayFailed, displayNotAssessed) {

        // noinspection UnnecessaryLocalVariableJS // will be useful for more filters
        const criteriaAssessment = (displaySuccessful && this.result === 1)
            || (displayPartially && this.result === 2)
            || (displayFailed && this.result === 3)
            || (displayNotAssessed && (this.result > 3 || this.result < 1));

        return criteriaAssessment
    }

};

var ProtocolRequest = class ProtocolRequest {

    /**
     * Will request protocols by the given name and criteria.
     * @param name {string} The unique case/sequence identifier
     * @param requestSequence {boolean} - True if sequence is requested, false else.
     * @param callback {function}
     */
    static requestProtocolsByName(name, requestSequence, callback) {

        const type = requestSequence ? "testsequences" : "testcases";
        const requestAll = name === "none";
        const url = requestAll
            ? getProjectURL().appendSegment("protocols").appendSegment(type)
            : getProjectURL().appendSegment("protocols").appendSegment(type).appendSegment(name);

        ProtocolRequest.__requestProtocols(url.toString(), requestSequence, callback)
    }

    static __requestProtocols(url, requestSequence, callback) {
        $.ajax({url: url, type: "GET"}).done(response => {

            if (response === "null") {
                callback([]);
                return;
            }

            const currentTestCasesParsed = [];
            for (const protocol of JSON.parse(response))
                currentTestCasesParsed.push(new Protocol(protocol, requestSequence));

            callback(currentTestCasesParsed)
        });
    }
};

var ProtocolDataTable = class ProtocolDataTable {

    /** Invoked when user plays with the filter settings on the right side. */
    static onUserUpdatedFilter() {
        dataTable.__displayFilteredProtocols()
    }

    /** Invoked when user selects a different test case from the select drop down menu. */
    static onUserDropDownSelectedTestCase(newTestCase) {
        dataTable.protocolsByName(newTestCase, false);

        selectCases.removeClass("d-none");
        selectSequences.addClass("d-none");
    }

    /** Invoked when user selects a different test sequence from the select drop down menu. */
    static onUserDropDownSelectedTestSequences(newTestSequence) {
        dataTable.protocolsByName(newTestSequence, true);

        selectCases.addClass("d-none");
        selectSequences.removeClass("d-none");
    }

    /**
     * Invoked when user clicks on a table row.
     * @param test - The unique test identifier
     * @param protocol - The protocol number
     * @param isSequence - True if sequence, false else
     */
    static onUserClickedRow(test, protocol, isSequence) {
        const url = isSequence === true
            ? getProjectURL().appendSegment("protocols").appendSegment("testsequences").appendSegment(test).appendSegment(protocol).toString()
            : getProjectURL().appendSegment("protocols").appendSegment("testcases").appendSegment(test).appendSegment(protocol).toString();

        ajaxRequestFragment(null, url, {protocolNr: protocol}, "GET");

    }

    constructor() {
        this.table = $('#DataTable')
            .on("draw.dt", () => $("time.timeago").timeago())
            .DataTable({
                "sDom": '<"top">rt<"bottom"p><"clear">',

                "pageLength": 15,
                drawCallback: function () {
                    const pagination = $(this).closest('.dataTables_wrapper').find('.dataTables_paginate');
                    pagination.toggle(this.api().page.info().pages > 1);
                },

                "order": [5, 'desc'],
                columnDefs: [
                    {"width": "5%", "targets": 0},
                    {"type": "dom-text", "targets": [0, 5]}
                ],
                "autoWidth": false,

                "language": {"zeroRecords": `No protocols found matching filter criteria ...`},

                'createdRow': (tr, cellData) => {
                    $(tr).on("click", () => ProtocolDataTable.onUserClickedRow(cellData[1], cellData[6], cellData[7]))
                        .addClass("mouse-hover-pointer");
                }
            });

        this.filterSuccessful = $("#filterSuccessfulTest");
        this.filterPartially = $("#filterPartiallySuccessfulTest");
        this.filterFailed = $("#filterFailedTest");
        this.filterNotAssessed = $("#filterNotAssessedTest");

        this.currentProtocols = null;
    }

    /**
     * Will change the table cells matching the given criteria.
     * @param requestTestCaseName {string} - Name of the new test case
     * @param requestSequence {boolean} - True if sequence is requested, false else
     */
    protocolsByName(requestTestCaseName, requestSequence) {

        ProtocolRequest.requestProtocolsByName(requestTestCaseName, requestSequence, protocols => {
            this.currentProtocols = protocols;
            this.__displayFilteredProtocols();
        })
    }

    /**
     * Will filter the given protocol matching the filter criteria on the right side.
     * @param protocol - The protocol to filter
     * @returns {boolean} - True if protocol matches filter
     * @private
     */
    __filter(protocol) {
        return protocol.isMatchingFilter(
            this.filterSuccessful.is(':checked'),
            this.filterPartially.is(':checked'),
            this.filterFailed.is(':checked'),
            this.filterNotAssessed.is(':checked')
        )
    }

    /**
     * Will filter all protocols matching the filter criteria on the right side.
     * @private
     */
    __displayFilteredProtocols() {
        this.table.clear();

        if (this.currentProtocols !== null)
            this.currentProtocols
                .filter(p => this.__filter(p))
                .forEach(p => this.table.row.add(p.toDataTableItem()));

        this.table.draw();
    }

};


/**
 * 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 "E":
        case "e":
            buttonString = "buttonPdf";
            break;
        case "Backspace":
            buttonString = "buttonBack";
            break;
    }
    if (document.getElementById(buttonString)) {
        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"));

$(() => {

    dataTable = new ProtocolDataTable();
    dataTable.protocolsByName("none", false)

});
