<!DOCTYPE html>
<html class="adaptive">

<head>
    <title>EMHASS: Energy Management Optimization for Home Assistant</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" type="text/css" href="{{ basename }}/static/style.css">
    <link rel="icon" href="{{ basename }}/static/img/emhass_logo_short.svg">
</head>

<body style="margin: auto; align-items:center; text-align:center;">
    <div>
        <img src="{{ basename }}/static/img/emhass_icon.png" alt="">
        <h2>EMHASS: Energy Management Optimization for Home Assistant</h2>
    </div>
    <div class="center">
        <div class="form">
            <!-- action button elements section -->
            <div class="loading-div">
                <h4>Use the buttons below to manually launch different optimization tasks</h4>
                <div id=loader></div> <!-- status dynamic element -->
            </div>
            <button type="button" id="perfect-optim" class="button button1">Perfect Optimization</button>
            <button type="button" id="dayahead-optim" class="button button2">Day-ahead Optimization</button>
            <button type="button" id="advanced-ml" class="button button3">Advanced ML Tasks</button>
            <!--ml toggled div button -->
            <h4>Use the button below to publish the optimized variables at the current timestamp</h4>
            <button type="button" id="publish-data" class="button button1">Publish Optimization Results</button>
            <div id="mlDiv" style="display: none;"> <!-- dynamic hidden toggled div for ml  -->
                <h4>Use the buttons below to fit, predict and tune a machine learning model for testing</h4>
                <button type="button" id="forecast-model-fit" class="button button1">ML forecast model fit</button>
                <button type="button" id="forecast-model-predict" class="button button2">ML forecast model
                    predict</button>
                <button type="button" id="forecast-model-tune" class="button button3">ML forecast model tune</button>
            </div>
            <!-- -->
            <!--dynamic input elements section -->
            <h4>Input Runtime Parameters</h4>
            <div class="input-button-container">
                <div class="input-buttons">
                    <button type="button" id="input-plus">+</button>
                    <button type="button" id="input-minus">-</button>
                    <select name="Select" id="input-select">
                        <option value="List" selected>List</option>
                        <option value="Box">Box</option>
                    </select>
                    <button type="button" id="input-clear">Clear</button>
                </div>
            </div>
            <div id="input-container"> <!-- (Box/List) dynamic input elements will be added here -->
            </div>
            <!-- -->
        </div>
        <!-- alert box element (for json parse issue) -->
        <div style="display: none;" id="alert" class="alert">
            <div>
                <span onclick="this.parentElement.parentElement.style.display='none';">&times;</span>
                <p id="alert-text"></p>
            </div>
        </div>
        <!-- -->
        <br><br><br>
        <!-- dynamic table/diagram elements section -->
    </div>
    <div id="template"> <!-- table/diagram container element  -->
        {% for plot in injection_dict %} <!-- diagrams/tables elements will be added here -->
        <div class="table_div">
            {{injection_dict[plot]}}
        </div>
        {% endfor %}
        <div>
            <!-- -->
            <footer class="footer">
                <p style="margin-top:10px; text-align:center;">&copy; MIT License | Copyright (c) 2021-2023 David
                    HERNANDEZ</p>
            </footer>
</body>



<script>

    //on page reload get saved data
    window.onload = async function () {
        getSavedData()
    };

    //function pushing data via post, triggered by button action
    async function formAction(action) {
        var data = inputToJson()
        if (data !== 0) { //don't run if there is an error in the input (box/list) Json data 
            showChangeStatus("loading", {}) // show loading div for status
            const response = await fetch(`{{ basename }}/action/${action}`, { //fetch data from webserver.py
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify(data), //note that post can only send data via strings
            })
            if (response.status == 201) {
                showChangeStatus(response.status, {})
                saveStorage() //save to storage if successful
            } //if successful 
            else { showChangeStatus(response.status, await response.json()) } // else get Log data from response
        }
        else {
            showChangeStatus("remove") //replace loading, show tick or cross with none 
        }
    }

    //function in control of status icons of post above
    async function showChangeStatus(status, logJson) {
        var loading = document.getElementById("loader") //element showing statuses
        if (status === "remove") { //remove all
            loading.innerHTML = "";
            loading.classList.remove("loading");
        }
        else if (status === "loading") { //show loading logo
            loading.innerHTML = "";
            loading.classList.add("loading"); //append class with loading animation styling
        }
        else if (status === 201) { //if status is 201, then show a tick 
            loading.classList.remove("loading")
            loading.innerHTML = `<p class=tick>&#x2713;</p>`
            getTemplate() //get updated templates

        }
        else { //then show a cross 
            loading.classList.remove("loading")
            loading.innerHTML = `<p class=cross>&#x292C;</p>` //show cross icon to indicate an error
            if (logJson.length != 0) {
                document.getElementById("alert-text").textContent = "\r\n\u2022 " + logJson.join("\r\n\u2022 ") //show received log data in alert box
                document.getElementById("alert").style.display = "block";
                document.getElementById("alert").style.textAlign = "left";
            }
        }
    }

    //get rendered html template with containing new table data  
    async function getTemplate() { //fetch data from webserver.py
        let htmlTemplateData = ""
        response = await fetch(`{{ basename }}/template/table-template`, {
            method: "GET"
        })
        blob = await response.blob() //get data blob
        htmlTemplateData = await new Response(blob).text() //obtain html from blob
        templateDiv = document.getElementById("template") //get template container element to override 
        templateDiv.innerHTML = htmlTemplateData //override container inner html with new data
        var scripts = Array.from(templateDiv.getElementsByTagName('script')); //replace script tags manually
        for (const script of scripts) {
            var TempScript = document.createElement("script");
            TempScript.innerHTML = script.innerHTML
            script.parentElement.appendChild(TempScript)
        }
    }


    //test localStorage support
    function testStorage() {
        try {
            localStorage.setItem("test", { "test": "123" })
            localStorage.removeItem("test")
            return true
        }
        catch (error) {
            return false
        }
        return false
    }


    //function gets saved data (if any)
    function getSavedData() {

        dictInputs() //check selected current (List or Box) is correct
        if (testStorage()) { //if local storage exists and works
            let selectElement = document.getElementById('input-select') // select button element
            var input_container = document.getElementById('input-container'); // container div containing all dynamic input elements (Box/List)
            if (localStorage.getItem("input_container_content") && localStorage.getItem("input_container_content") !== "{}" ) { //If items already stored in local storage, then override default
                if (selectElement.value == "Box") { //if Box is selected, show saved json data into box 
                    document.getElementById("text-area").value = localStorage.getItem("input_container_content");
                }
                if (selectElement.value == "List") { //if List is selected, show saved json data into box 
                    storedJson = JSON.parse(localStorage.getItem("input_container_content"));
                    if (Object.keys(storedJson).length > 0) {
                        input_container.innerHTML = "";
                        i = 1;
                        for (const ikey in storedJson) {
                            input_container.appendChild(createInputListDiv(ikey, JSON.stringify(storedJson[ikey]))); //call function to present each key as an list div element (with saved values)
                        }
                    }
                }
            }
        }
    }

    //using localStorage, store json data from input-list(List)/text-area(from input-box) elements for saved state save on page refresh (will save state on successful post)
    function saveStorage() {
        var data = JSON.stringify(inputToJson())
        if (testStorage() && data != "{}") { //don't bother saving if empty and/or storage don't exist
            localStorage.setItem("input_container_content", data);
        }
    }

    //function gets values from input-list/text-area(from input-box) elements and return json dict object 
    function inputToJson() {
        var input_container = document.getElementById('input-container'); //container
        let inputListArr = document.getElementsByClassName('input-list'); //list
        let inputTextArea = document.getElementById('text-area'); //box 
        let input_container_child = null
        input_container_child = input_container.firstElementChild; //work out which element is first inside container div
        var jsonReturnData = {};

        if (input_container_child == null) { //if no elements in container then return empty
            return (jsonReturnData)
        };
        //if List return box json
        if (input_container_child.className == "input-list" && inputListArr.length > 0) { //if list is first and if list is greater then 0, otherwise give empty dict

            let jsonTempData = "{";
            for (let i = 0; i < inputListArr.length; i++) {
                let key = inputListArr[i].getElementsByClassName('input-key')[0].value;
                var value = inputListArr[i].getElementsByClassName('input-value')[0].value;
                //curate a string with list elements to parse into json later
                if (key !== "") { //key must not be empty
                    if (i !== 0) { jsonTempData = jsonTempData.concat(","); } //add comma before every parameter, exuding the first 
                    jsonTempData = jsonTempData.concat('"' + key + '":' + value);
                }
            }
            jsonTempData = jsonTempData.concat("}");
            try {
                jsonReturnData = JSON.parse(jsonTempData);
            }
            catch (error) { //if json error, show in alert box
                document.getElementById("alert-text").textContent = "\r\n" + error + "\r\n" + "JSON Error: String values may not be wrapped in quotes"
                document.getElementById("alert").style.display = "block";
                document.getElementById("alert").style.textAlign = "center";
                return (0)
            }
        }
        //if Box return box json
        if (input_container_child.className == "input-box" && inputTextArea.value != "") {  //if Box is first and text is not empty, otherwise give empty dict
            try {
                jsonReturnData = JSON.parse(inputTextArea.value);
            }
            catch (error) {  //if json error, show in alert box
                document.getElementById("alert-text").textContent = "\r\n" + error
                document.getElementById("alert").style.display = "block";
                return (0)
            }
        }
        return (jsonReturnData)
    }

    //function creates input list div element (and pass it values if given)
    function createInputListDiv(ikey, ivalue) {
        let div = document.createElement('div');
        div.className = 'input-list';
        div.innerHTML = `
                <input class="input-key" type="text" placeholder="pv_power_forecast" >
                <p>:</p>
                <input class="input-value" type="text" placeholder="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 141.22, 246.18, 513.5, 753.27, 1049.89, 1797.93, 1697.3, 3078.93, 1164.33, 1046.68, 1559.1, 2091.26, 1556.76, 1166.73, 1516.63, 1391.13, 1720.13, 820.75, 804.41, 251.63, 79.25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" >
            `;

        if (ikey && ivalue) { //if value and key is provided (from local storage) then add as elements values
            div.getElementsByClassName("input-key")[0].value = String(ikey);
            div.getElementsByClassName("input-value")[0].value = String(ivalue);
        }

        return (div);
    }

    //function assigned to control (add and remove) input (Box and List) elements 
    function dictInputs(action) {
        var input_container = document.getElementById('input-container') // container div containing all dynamic input elements
        let selectElement = document.getElementById('input-select')      // select button
        let input_container_child = null
        let input_container_child_name = null
        if (input_container.children.length > 0) {
            input_container_child = input_container.firstElementChild  // figure out what is the first element inside of container (ie: "text-area" (input-box) or "input-list" (list)) 
            input_container_child_name = input_container.firstElementChild.className
        };
        //if list is selected, remove text-area (from Box) element and replace (with input-list)
        if (selectElement.value == "List") {
            if (action == "input-plus" || input_container_child_name == "input-box") {  //if plus button pressed, or Box element exists
                if (input_container_child_name == "input-box") {
                    input_container_child.remove();
                }
                input_container.appendChild(createInputListDiv(false, false)); //call to createInputListDiv function to craft input-list element (with no values) and append inside container element
            };
            if (action == "input-minus") {  //minus button pressed, remove input-list element
                if (input_container.children.length > 0) {
                    let inputListArr = document.getElementsByClassName('input-list');
                    let obj = inputListArr.item(inputListArr.length - 1);
                    obj.innerHTML = '';
                    obj.remove();
                }
            };
        }
        //if box is selected, remove input-list elements and replace (with text-area)
        if (selectElement.value == "Box") {
            if (input_container_child_name == "input-list" || input_container_child === null) { // if input list exists or no Box element
                input_container.innerHTML = ""; //remove input-list list elements via erasing container innerHTML
                let div = document.createElement('div'); //add input-box element
                div.className = "input-box";
                div.innerHTML = `
                <textarea id="text-area" rows="30" placeholder="{}"></textarea>
                `;
                input_container.appendChild(div); //append inside of container element
            }
        };
    }


    //hide ml buttons behind Advanced ML tasks button
    function advancedButtons(id) {
        mldiv = document.getElementById("mlDiv")
        if (mldiv.style.display == "none") {
            mldiv.style.display = "block"
        }
        else {
            mldiv.style.display = "none"
        }
    }

    //clear stored input data from localStorage (if any), clear input elements
    async function ClearInputData(id) {
        if (testStorage() && localStorage.getItem("input_container_content") !== null) {
            localStorage.setItem("input_container_content", "{}")
        }
        ClearInputElements();

    }

    //clear input elements
    async function ClearInputElements() {
        let selectElement = document.getElementById('input-select')
        var input_container = document.getElementById('input-container'); 
        if (selectElement.value == "Box") { 
            document.getElementById("text-area").value = "{}";
        }
        if (selectElement.value == "List") { 
            input_container.innerHTML = "";
        }

    }



    //add listeners to buttons
    [
        "dayahead-optim",
        "forecast-model-fit",
        "forecast-model-predict",
        "forecast-model-tune",
        "perfect-optim",
        "publish-data",
    ].forEach((id) => document.getElementById(id).addEventListener('click', () => formAction(id)));
    [
        "advanced-ml",
    ].forEach((id) => document.getElementById(id).addEventListener('click', () => advancedButtons(id)));
    [
        "input-plus",
        "input-minus",
    ].forEach((id) => document.getElementById(id).addEventListener('click', () => dictInputs(id)));
    [
        "input-select"
    ].forEach((id) => document.getElementById(id).addEventListener('change', () => getSavedData(id)));
    [
        "input-clear"
    ].forEach((id) => document.getElementById(id).addEventListener('click', () => ClearInputData(id)));
</script>

</html>