/*****************************************************************************
 *
 * ajax.js - Functions for handling NagVis Ajax requests
 *
 * Copyright (c) 2004-2016 NagVis Project (Contact: info@nagvis.org)
 *
 * License:
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *****************************************************************************/

function call_ajax(url, args) {
    args = merge_args(
        {
            add_ajax_id: true,
            response_handler: null,
            error_handler: function (status_code, response, handler_data) {
                if (response === null) {
                    response = {
                        type: "error",
                        title: "Network error",
                        message:
                            "Failed to execute ajax call. Maybe a network issue or webserver is not available." +
                            "<div class=details>\n" +
                            "HTTP-Status-Code: " +
                            status_code +
                            "<br />\n" +
                            "Time: " +
                            iNow +
                            "<br />\n" +
                            "URL: <code>" +
                            url.replace(/</g, "&lt;") +
                            "</code><br />\n" +
                            "</div>"
                    };
                }
                frontendMessage(response, "serverError");
            },
            handler_data: null,
            method: "GET",
            post_data: null,
            sync: false,
            decode_json: true
        },
        args
    );

    const AJAX = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
    if (!AJAX) return null;

    if (args.add_ajax_id) {
        url += url.indexOf("?") !== -1 ? "&" : "?";
        url += "_ajaxid=" + iNow;
    }

    AJAX.open(args.method, url, !args.sync);

    if (args.method == "POST") {
        // Set post specific options. request might be a FormData object. In this case
        // the request is not using form-urlencoded data, instead it is automatically
        // set to multipart/form-data
        if (typeof args.post_data !== "object")
            AJAX.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    }

    if (!args.sync) {
        AJAX.onreadystatechange = function () {
            if (AJAX && AJAX.readyState == 4) {
                if (AJAX.status == 200) {
                    // in case this is no error remove all frontend messages of type
                    // serverError which might have been shown by the default
                    // args.error_handler.
                    frontendMessageRemove("serverError");

                    let response = AJAX.responseText;
                    if (args.decode_json) {
                        try {
                            response = JSON.parse(response);
                        } catch (e) {
                            args.error_handler(
                                AJAX.status,
                                {
                                    type: "error",
                                    title: "Error: Syntax Error",
                                    message:
                                        "Invalid JSON response<div class=details>\nTime: " +
                                        iNow +
                                        "<br />\nURL: " +
                                        url.replace(/</g, "&lt;") +
                                        "<br />\n" +
                                        "Response: <code>" +
                                        response +
                                        "</code></div>"
                                },
                                0,
                                "jsonError"
                            );
                            return "";
                        }

                        // The server might return a json object which is representing
                        // an error which happened on the server.
                        // We adapt the old behaviour of showing a frontend message for
                        // all errors here by using the default error handler. Callers
                        // which register their own error handlers need to deal with
                        // this kind of error data on their own.
                        if (isset(response.type) && isset(response.message)) {
                            args.error_handler(AJAX.status, response, args.handler_data);
                            return "";
                        }
                    }

                    if (args.response_handler) args.response_handler(response, args.handler_data);
                } else if (AJAX.status == 401) {
                    // This is reached when someone is not authenticated anymore
                    // but has some webservices running which are still fetching
                    // infos via AJAX. Reload the whole frameset or only the
                    // single page in that case.
                    document.location.reload();
                } else if (AJAX.status == 0) {
                    // this happens when a user aborts currently running ajax
                    // requests by reloading the page. simply ignore
                } else {
                    args.error_handler(AJAX.status, null, args.handler_data);
                }
            }
        };
    }

    AJAX.send(args.post_data);
    return AJAX;
}

/**
 * Parses all values from the given form to a string or form data object
 * which is then be used as request data in ajax queries
 */
function getFormParams(formId, skipHelperFields) {
    let data, formdata;
    if (window.FormData) {
        data = new FormData();
        formdata = true;
    } else {
        formdata = false;
        data = "";
    }

    const add_data = function (key, val) {
        if (formdata) data.append(key, val);
        else data += key + "=" + escapeUrlValues(val) + "&";
    };

    // Read form contents
    let oForm = document.getElementById(formId);
    if (typeof oForm === "undefined") return data;

    // Get relevant input elements
    let aFields = oForm.querySelectorAll("input,textarea");

    for (let i = 0, len = aFields.length; i < len; i++) {
        // Filter helper fields (if told to do so)
        if (skipHelperFields && aFields[i].name.charAt(0) === "_") continue;

        // Skip options which use the default value and where the value has
        // not been set before
        let oFieldDefault = document.getElementById("_" + aFields[i].name);
        if (aFields[i] && oFieldDefault && !document.getElementById("_conf_" + aFields[i].name)) {
            if (aFields[i].value === oFieldDefault.value) {
                continue;
            }
        }
        oFieldDefault = null;

        if (
            aFields[i].type == "hidden" ||
            aFields[i].type == "text" ||
            aFields[i].type == "textarea" ||
            aFields[i].type == "password" ||
            aFields[i].type == "submit"
        ) {
            add_data(aFields[i].name, aFields[i].value);
        } else if (aFields[i].type == "checkbox") {
            if (aFields[i].checked) {
                add_data(aFields[i].name, aFields[i].value);
            }
            // Unchecked checkboxes are intentionally not submitted, matching standard
            // HTML form behaviour. This prevents hitting PHP's max_input_vars limit
            // on forms with many checkboxes (e.g. role permissions with many maps).
        } else if (aFields[i].type == "radio") {
            if (aFields[i].checked) {
                add_data(aFields[i].name, aFields[i].value);
            }
        } else if (aFields[i].type == "file") {
            // Now handle the file upload elements (which lead to an error when not
            // using the form data mechanism)
            if (!formdata) {
                throw new Error(
                    "File upload not supported with your browser. " +
                        "This form can only be used when using a browser " +
                        "which suports javascript file uploads (FormData)."
                );
            }

            if (aFields[i].files.length > 0) {
                const file = aFields[i].files[0];
                data.append(aFields[i].name, file, file.name);
            }
        }
    }

    // Get relevant select elements
    aFields = oForm.getElementsByTagName("select");
    for (let i = 0, len = aFields.length; i < len; i++) {
        // Filter helper fields (NagVis WUI specific)
        if (skipHelperFields && aFields[i].name.charAt(0) === "_") continue;

        // Skip options which use the default value
        oFieldDefault = document.getElementById("_" + aFields[i].name);
        if (aFields[i] && oFieldDefault) {
            if (aFields[i].value === oFieldDefault.value) {
                continue;
            }
        }
        oFieldDefault = null;

        // Can't use the selectedIndex when using select fields with multiple
        if (!aFields[i].multiple || aFields[i].multiple !== true) {
            // Skip fields where nothing is selected
            if (aFields[i].selectedIndex != -1) {
                add_data(aFields[i].name, aFields[i].options[aFields[i].selectedIndex].value);
            }
        } else {
            for (let a = 0; a < aFields[i].options.length; a++) {
                // Only add selected options
                if (aFields[i].options[a].selected == true) {
                    add_data(aFields[i].name + "[]", aFields[i].options[a].value);
                }
            }
        }
    }

    aFields = null;
    oForm = null;

    return data;
}
