// Retrieve elements from the DOM.
var showConfig = document.getElementById("show-config");
var configForm = document.getElementById("config-form");
var regenerateButton = document.getElementById("config-regenerate");

// Global variables.
var rawConfig = {};
var emptyValueDefault = "🖊️";

/**
 * Fetch the existing config file.
 */
fetch("/config")
    .then(response => response.json())
    .then(data => {
        rawConfig = data;
        configForm.style.display = "block";
        processChildren(configForm, data);

        var submitButton = document.createElement("button");
        submitButton.type = "submit";
        submitButton.innerHTML = "update";
        configForm.appendChild(submitButton);

        // The config form's submit handler.
        configForm.addEventListener("submit", (event) => {
            event.preventDefault();
            console.log(rawConfig);
            const response = fetch("/config", {
                method: "POST",
                credentials: "same-origin",
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(rawConfig)
            }).then(response => response.json())
            .then((data) => console.log(data));
        });
});

/**
 * The click handler for the Regenerate button.
 */
regenerateButton.addEventListener("click", (event) => {
    event.preventDefault();
    regenerateButton.style.cursor = "progress";
    regenerateButton.disabled = true;
    fetch("/regenerate")
        .then(response => response.json())
        .then(data => {
            regenerateButton.style.cursor = "pointer";
            regenerateButton.disabled = false;
            console.log(data);
        });
})

/**
 * Adds config elements to the DOM representing the sub-components 
 * of one of the fields in the raw config file.
 * @param {the parent element} element 
 * @param {the data to be rendered for this element and its children} data 
 */
function processChildren(element, data) {
    for (let key in data) {
        var child = document.createElement("div");
        child.id = key;
        child.className = "config-element";
        child.appendChild(document.createTextNode(key + ": "));
        if (data[key] === Object(data[key]) && !Array.isArray(data[key])) {
            child.className+=" config-title";
            processChildren(child, data[key]);
        } else {
            child.appendChild(createValueNode(data, key));
        }
        element.appendChild(child);
    }
}

/**
 * Takes an element, and replaces it with an editable 
 * element with the same data in place.
 * @param {the original element to be replaced} original 
 * @param {the source data to be rendered for the new element} data 
 * @param {the key for this input in the source data} key 
 */
function makeElementEditable(original, data, key) {
    original.addEventListener("click", () => {
        var inputNewText = document.createElement("input");
        inputNewText.type = "text";
        inputNewText.className = "config-element-edit";
        inputNewText.value = (original.textContent == emptyValueDefault) ? "" : original.textContent;
        fixInputOnFocusOut(inputNewText, data, key);
        original.parentNode.replaceChild(inputNewText, original);
        inputNewText.focus();
    });
}

/**
 * Creates a node corresponding to the value of a config element.
 * @param {the source data} data 
 * @param {the key corresponding to this node's data} key 
 * @returns A new element which corresponds to the value in some field.
 */
function createValueNode(data, key) {
    var valueElement = document.createElement("span");
    valueElement.className = "config-element-value";
    valueElement.textContent = !data[key] ? emptyValueDefault : data[key];
    makeElementEditable(valueElement, data, key);
    return valueElement;
}

/**
 * Replaces an existing input element with an element with the same data, which is not an input. 
 * If the input data for this element was changed, update the corresponding data in the raw config.
 * @param {the original element to be replaced} original 
 * @param {the source data} data 
 * @param {the key corresponding to this node's data} key 
 */
function fixInputOnFocusOut(original, data, key) {
    original.addEventListener("blur", () => {
        data[key] = (original.value != emptyValueDefault) ? original.value : "";
        original.parentNode.replaceChild(createValueNode(data, key), original);
    })
}
