/**
 * mdCalendar.js
 */

document.addEventListener("DOMContentLoaded", function() {

    /**
     * @var {array} translations Array of all translations.
     */
    let translations = {
        "en" : {
            "dow0"  : "Sunday",
            "dow1"  : "Monday",
            "dow2"  : "Tuesday",
            "dow3"  : "Wednesday",
            "dow4"  : "Thursday",
            "dow5"  : "Friday",
            "dow6"  : "Saturday",
            "mon0"  : "January",
            "mon1"  : "February",
            "mon2"  : "March",
            "mon3"  : "April",
            "mon4"  : "May",
            "mon5"  : "June",
            "mon6"  : "July",
            "mon7"  : "August",
            "mon8"  : "September",
            "mon9"  : "October",
            "mon10" : "November",
            "mon11" : "December",
            "Today" : "Today",
            "Title" : "Title",
            "Start" : "Start",
            "End"   : "End",
            "Location" : "Location"
        },
        "de" : {
            "dow0"  : "Sonntag",
            "dow1"  : "Montag",
            "dow2"  : "Dienstag",
            "dow3"  : "Mittwoch",
            "dow4"  : "Donnerstag",
            "dow5"  : "Freitag",
            "dow6"  : "Samstag",
            "mon0"  : "Januar",
            "mon1"  : "Februar",
            "mon2"  : "März",
            "mon3"  : "April",
            "mon4"  : "Mai",
            "mon5"  : "Juni",
            "mon6"  : "Juli",
            "mon7"  : "August",
            "mon8"  : "September",
            "mon9"  : "Oktober",
            "mon10" : "November",
            "mon11" : "Dezember",
            "Today" : "Jetzt",
            "Title" : "Titel",
            "Start" : "Beginn",
            "End"   : "Ende",
            "Location" : "Ort"
        },
        "hu" : {
            "dow0": "Vas\u00e1rnap",
            "dow1": "H\u00e9tf\u0151",
            "dow2": "Kedd",
            "dow3": "Szerda",
            "dow4": "Cs\u00fct\u00f6rt\u00f6k",
            "dow5": "P\u00e9ntek",
            "dow6": "Szombat",
            "mon0": "Janu\u00e1r",
            "mon1": "Febru\u00e1r",
            "mon2": "M\u00e1rcius",
            "mon3": "\u00c1prilis",
            "mon4": "M\u00e1jus",
            "mon5": "J\u00fanius",
            "mon6": "J\u00falius",
            "mon7": "Augusztus",
            "mon8": "Szeptember",
            "mon9": "Okt\u00f3ber",
            "mon10": "November",
            "mon11": "December",
            "Today": "Ma",
            "Title": "C\u00edm",
            "Start": "Nyit",
            "End": "Bez\u00e1r",
            "Location": "Helysz\u00edn"
        }
    }

    /**
     * @var {boolean} Toggle debugging.
     */
    let debugging = false;

    /**
     * Function queryPage queries a web page and runs the specified function over the output.
     *
     * @param {string}   url   URL to query.
     * @param {function} func  Callback function to run on the request after loading.
     * @param {boolean}  debug Enable / disable debug mode.
     *
     * @return {boolean}
     */
    function queryPage(url, func, debug = false) {

        let request     = new XMLHttpRequest();
        request.open('GET', url);
        request.setRequestHeader("Cache-Control", "no-cache");
        request.responseType = 'htm';
        request.send();
        request.onload = function() {

            func(request, debug);
        };

    }

    /**
     * Removes all children of an element.
     *
     * @param {string} id ID of the element to tear down.
     *
     * @return {void}
     */
    function emptyElement(element) {
        while (element.firstChild) {
            emptyElement(element.firstChild);
        }
        element.parentNode.removeChild(element);
        if (debugging === true) {
            console.log("Removed element:");
            console.log(element);
        }
    }

    function tearDownById(id) {
        let target = document.getElementById(id);
        if (target !== null) emptyElement(target);
    }

    /**
     * Returns a requested translation from an array in the currently used language.
     *
     * @param {mixed[]} list      Translation variable.
     * @param {string}  specifier Specifies which translation to get.
     *
     * @return {string}
     */
    function getTranslation(list, specifier) {

        let preferedLang = document.getElementsByTagName("html")[0].getAttribute("lang");

        if (list[preferedLang] !== undefined && list[preferedLang][specifier] !== null) return list[preferedLang][specifier];
        return list["en"][specifier];

    }

    /**
     * Function to create calendar.
     */
    function createCalendarTable(target, events) {

        let startOfWeek = 1; // The week starts on Monday.
        let endOfWeek   = 0; // The week ends on Sunday.

        let calLocale = document.getElementsByTagName("html")[0].getAttribute("lang");
        if (calLocale === undefined || calLocale === null) calLocale = "en";

        function removeToolTips() {
            let toolTips = document.getElementsByClassName("mdCToolTip");
            for (let i = 0, max = toolTips.length; i < max; i++) {
                emptyElement(toolTips[i]);
            }
        }

        /**
         * Gets all days to display per month.
         * Adapted version of function described in https://stackoverflow.com/a/13146828.
         *
         * @param {int} The month number, 0 based
         * @param {int} The year, not zero based, required to account for leap years
         *
         * @return {Date[]} List with date objects for each day of the month
         */
        function getDaysInMonth(month, year) {

            var date = new Date(year, month, 1);
            var days = [];

            // If the weekday of the first of the month does not equal 1 (Monday),
            // get days until the last monday before the month.
            if (date.getDay() !== startOfWeek) {
                while (date.getDay() !== startOfWeek) {
                    date.setDate(date.getDate() - 1);
                    days.unshift(new Date(date));
                }
            }

            date = new Date(year, month, 1);
            // Get days of the month
            while (date.getMonth() === month) {
                days.push(new Date(date));
                date.setDate(date.getDate() + 1);
            }

            // If the weekday of the first of the month does not equal 1 (Monday),
            // get days until the last monday before the month.
            while (date.getDay() !== startOfWeek) {
                days.push(new Date(date));
                date.setDate(date.getDate() + 1);
            }
            return days;
        }

        events = (function() {

            let colorSchemeLength = 29;
            for (var i = 0, max = events.length; i < max; i++) {
                let hash = 0;
                for (var j = 0, maxj = events[i].name.length; j < maxj; j++) {
                    hash = events[i].name.charCodeAt(j) + hash;
                }
                hash = hash % (colorSchemeLength) - 1;

                events[i].color = hash;
            }
            return events;

        })();

        let d = new Date();

        let year;
        if (target.getAttribute("data-year") !== null) year = parseInt(target.getAttribute("data-year"));
        else year = d.getFullYear();

        let month;
        if (target.getAttribute("data-month") !== null) month = parseInt(target.getAttribute("data-month") - 1);
        else month = d.getMonth();

        // Create outer div
        let mdCalDiv = document.createElement("div");
        mdCalDiv.classList.add("mdCalendar");

        /**
         * Creater header line of the calendar.
         */
        (function() {

            let mdCalTitleLine = document.createElement("header");
            mdCalTitleLine.classList.add("mdCTitleLine");

            let mdCalTitle = document.createElement("h3");
            mdCalTitle.textContent = getTranslation(translations, "mon" + month.toString());
            if (year != d.getFullYear()) mdCalTitle.textContent = mdCalTitle.textContent + " (" + year + ")";
            mdCalTitleLine.appendChild(mdCalTitle);

            let mdCalNav = document.createElement("div");
            mdCalNav.classList.add("mdCNav");
            mdCalTitleLine.appendChild(mdCalNav);

            let mdCalNavPrev  = document.createElement("a");
            mdCalNavPrev.href = target.getAttribute("data-prev");
            mdCalNavPrev.rel  = "prev";
            mdCalNavPrev.classList.add("mdCNavPrev");
            mdCalNav.appendChild(mdCalNavPrev);

            let mdCalNavNext  = document.createElement("a");
            mdCalNavNext.href = target.getAttribute("data-next");
            mdCalNavNext.rel  = "next";
            mdCalNavNext.classList.add("mdCNavNext");
            mdCalNav.appendChild(mdCalNavNext);

            let mdCalNavToday  = document.createElement("a");
            mdCalNavToday.href = target.getAttribute("data-today");
            mdCalNavToday.textContent = getTranslation(translations, "Today");
            mdCalNav.appendChild(mdCalNavToday);

            mdCalTitleLine.appendChild(mdCalNav);
            mdCalDiv.appendChild(mdCalTitleLine);

        })();

        /**
         * Create table.
         */
        let table = document.createElement("table");
        table.classList.add("mdCTable");

        let thead = document.createElement("thead");
        let theadTr = document.createElement("tr");

        for (let i = 1, max = 7; i < max; i++) {
            let th = document.createElement("th");
            th.textContent = getTranslation(translations, "dow" + i.toString());
            theadTr.appendChild(th);
        }
        let th = document.createElement("th");
        th.textContent = getTranslation(translations, "dow0");
        theadTr.appendChild(th);

        thead.appendChild(theadTr);
        table.appendChild(thead);

        let tbody = document.createElement("tbody");

        let days = getDaysInMonth(month, year);
        let tr;

        let daysTDs = [];

        function createSingleEventOverlay(e, parentElement, data) {

            let toolTip = document.createElement("table");
            toolTip.classList.add("mdCToolTip");

            let tableData = [];
            tableData.push(["Title", data["name"]]);
            if (data["start"] !== undefined) tableData.push(["Start", data["start"]]);
            if (data["end"] !== undefined)   tableData.push(["End", data["end"]]);
            if (data["place"] !== undefined) tableData.push(["Location", data["place"]]);

            for (let i = 0, max= tableData.length; i < max; i++) {
                let nameRow = document.createElement("tr");
                let nameTh = document.createElement("th");
                nameTh.textContent = getTranslation(translations, tableData[i][0]);
                nameRow.appendChild(nameTh);
                let nameTd = document.createElement("td");
                nameTd.textContent = tableData[i][1];
                nameRow.appendChild(nameTd);
                toolTip.appendChild(nameRow);
            }

            parentElement.appendChild(toolTip);

        }

        for (let i = 0, max= days.length; i < max; i++) {

            // Begin a new table row every Monday.
            if (days[i].getDay() === startOfWeek) {
                tr = document.createElement("tr");
                tbody.appendChild(tr);
            }

            // Create new TD per day and add appropriate classes.
            let td = document.createElement("td");
            if (days[i].getMonth() !== month) td.classList.add("mdCOtherMonth");
            if (days[i].getYear() == d.getYear() && days[i].getMonth() == d.getMonth() && days[i].getDate() == d.getDate()) td.classList.add("mdCToday");

            tdTitle = document.createElement("time");
            tdTitle.textContent = days[i].getDate();

            td.appendChild(tdTitle);
            tr.appendChild(td);

            // Append dates.
            let dayTime = days[i].getTime();
            let dayTimeMinusDay = days[i].getTime() + 24 * 3600 * 1000;
            let dayTimePlusDay = days[i].getTime() - 0 * 3600 * 1000;
            for (let j = 0, maxj = events.length; j < maxj; j++) {
                if (Date.parse(events[j].start) > dayTimeMinusDay || Date.parse(events[j].end) < dayTimePlusDay) continue;

                let eventSignifier = document.createElement("a");
                eventSignifier.classList.add("color" + events[j].color.toString());
                if (events[j].link) eventSignifier.href = events[j].link;

                let eventSignifierText = document.createElement("span");
                eventSignifierText.textContent = events[j].name;
                eventSignifier.appendChild(eventSignifierText);

                eventSignifier.addEventListener('mouseover', function(e) {
                    createSingleEventOverlay(e, eventSignifier, events[j]);
                });
                eventSignifier.addEventListener('mouseout', function(e) {
                    removeToolTips();
                });

                td.appendChild(eventSignifier);

            }

            // Add posibility for overlay.
            tdTitle.addEventListener('click', function(e) {

                tearDownById("mdCOverlay");

                let overlay = document.createElement("div");
                overlay.id = "mdCOverlay";

                let eventTitleBar = document.createElement("div");
                eventTitleBar.classList.add("mdCOverlayTitleBar");
                overlay.appendChild(eventTitleBar);

                let title = document.createElement("span");
                title.classList.add("mdCOverlayTitle");
                title.textContent = days[i].toLocaleDateString(calLocale);
                eventTitleBar.appendChild(title);

                let closer = document.createElement("span");
                closer.classList.add("mdCOverlayClose");
                closer.addEventListener('click', function(e) {
                    if (debugging === true) console.log("Clicked close button: Tearing down daily agenda.");
                    tearDownById("mdCOverlay");
                });
                eventTitleBar.appendChild(closer);

                let ul = document.createElement("ul");

                // Append events.
                for (let j = 0, maxj = events.length; j < maxj; j++) {

                    let start = new Date(events[j].start);
                    let end   = new Date(events[j].end);

                    if (start.getTime() > dayTime || end.getTime() < dayTimePlusDay) continue;

                    let eventLi = document.createElement("li");

                    let eventSignifier = document.createElement("a");
                    eventSignifier.textContent = events[j].name;
                    if (events[j].link) eventSignifier.href = events[j].link;
                    eventLi.appendChild(eventSignifier);

                    let eventP = document.createElement("p");
                    eventP.textContent = start.toLocaleDateString(calLocale) + " - " + end.toLocaleDateString(calLocale);
                    if (events[j].place !== undefined) eventP.textContent = eventP.textContent + ", " + events[j].place;
                    eventLi.appendChild(eventP);

                    let eventDesc = document.createElement("p");
                    eventDesc.textContent = events[j].description;
                    eventLi.appendChild(eventDesc);

                    ul.appendChild(eventLi);

                }

                overlay.appendChild(ul);
                document.getElementsByTagName("body")[0].appendChild(overlay);

            });

            if (td.childElementCount > 6) td.classList.add("mdCManyElements");
            daysTDs[days[i].getFullYear() + "-" + days[i].getMonth() + "-" + days[i].getDate()] = td;

        }

        table.appendChild(tbody);
        mdCalDiv.appendChild(table);

        target.appendChild(mdCalDiv);

        // Enable closing overlay by pressing escape.
        document.addEventListener('keydown', function(e) {
            if (e.keyCode !== 27) return;
            if (debugging === true) console.log("Pressed escape: Tearing down daily agenda.");
            tearDownById("mdCOverlay");
        });

        return daysTDs;
    }

    (function() {

        let calendars = document.getElementsByClassName("mdCalendar");
        for (let i = 0, max = calendars.length; i < max; i++) {

            queryPage(
                encodeURI(calendars[i].getAttribute("data-src")),
                function (request) {
                    if (debugging === true) console.log("Loaded\n" + request.response);
                    let elements = JSON.parse(request.response);
                    let tCalendar = createCalendarTable(calendars[i], elements);
                });
        }
    }
    )();

});
