/**
 * https://dequeuniversity.com/library/aria/date-picker
 */
class DatePickerAria {

    static DATE_FORMAT = "dd/mm/yy";
    static SELECTOR_DATA = "[data-datepicker-aria]";
    //static DAY_NAME_SHORT = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
    //static MONTHS = ['january', 'february','march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december']
    static MONTH_PREV_TEXT = "Mese precedente";
    static MONTH_NEXT_TEXT = "Mese successivo";
    static CLOSE_TEXT = "Chiudi";
    //static BUTTON_TEXT = "Calendar View";
    static BTN_TRIGGER_TEXT = "Apri calendario";

    datePicker;
    datePickerElement;
    datePickerTrigger;
    prev;
    next;
    months;

    constructor(datePickerElement) {
        this.datePickerElement = datePickerElement;
        this.datePicker = $(datePickerElement).datepicker({
            dateFormat: DatePickerAria.DATE_FORMAT,
            showOn: 'button',
            //buttonImage: '/assets/images/calendar.png', // File (and file path) for the calendar image
            buttonImageOnly: false,
            //buttonText: DatePickerAria.BUTTON_TEXT,
            //dayNamesShort: DatePickerAria.DAY_NAME_SHORT,
            showButtonPanel: true,
            closeText: DatePickerAria.CLOSE_TEXT,
            onClose: () => this.removeAria()
        });

        if (jQuery.datepicker && jQuery.datepicker.regional && jQuery.datepicker.regional.it) {
            this.months = jQuery.datepicker.regional.it.monthNames;
        }

        // Add aria-describedby to the button referring to the label
        this.datePickerTrigger = $(datePickerElement).next('.ui-datepicker-trigger');
        this.datePickerTrigger
            .attr('aria-label', DatePickerAria.BTN_TRIGGER_TEXT) // aria-label
            .addClass('inail-button-calendar') // classe custom
            .html('<em class="fa-regular fa-calendar" aria-hidden="true"></em>'); // icona

        // Se l'input è disabilitato → disabilito anche il trigger
        if ($(datePickerElement).is(':disabled')) {
            this.datePickerTrigger.prop('disabled', true);
        }

        this.dayTripper();
    }


    dayTripper() {
        $(this.datePickerTrigger).click(() => {
            setTimeout(() => {

                var today = $('.ui-datepicker-today a')[0];

                if (!today) {
                    today = $('.ui-state-active')[0] ||
                        $('.ui-state-default')[0];
                }


                // Hide the entire page (except the date picker)
                // from screen readers to prevent document navigation
                // (by headings, etc.) while the popup is open
                //$("main").attr('id', 'dp-container');
                // $("#dp-container").attr('aria-hidden','true');
                //$("#skipnav").attr('aria-hidden', 'true');

                // Hide the "today" button because it doesn't do what
                // you think it supposed to do
                $(".ui-datepicker-current").hide();

                today.focus();
                this.datePickHandler();
                $(document).on('click', '#ui-datepicker-div .ui-datepicker-close', () => {
                    this.closeCalendar();
                });
            }, 0);
        });
    }

    datePickHandler() {
        var activeDate;
        var container = document.getElementById('ui-datepicker-div');
        var input = this.datePicker[0];

        if (!container || !input) {
            return;
        }

        // $(container).find('table').first().attr('role', 'grid');

        container.setAttribute('role', 'application');
        container.setAttribute('aria-label', 'Calendar view date-picker');

        // the top controls:
        this.prev = $('.ui-datepicker-prev', container)[0],
            this.next = $('.ui-datepicker-next', container)[0];


        // This is the line that needs to be fixed for use on pages with base URL set in head
        this.next.href = 'javascript:void(0)';
        this.prev.href = 'javascript:void(0)';

        this.next.setAttribute('role', 'button');
        this.next.removeAttribute('title');
        this.prev.setAttribute('role', 'button');
        this.prev.removeAttribute('title');

        this.appendOffscreenMonthText(this.next);
        this.appendOffscreenMonthText(this.prev);

        // delegation won't work here for whatever reason, so we are
        // forced to attach individual click listeners to the prev /
        // next month buttons each time they are added to the DOM
        $(this.next).on('click', () => this.handleNextClicks());
        $(this.prev).on('click', () => this.handlePrevClicks());

        this.monthDayYearText();

        $(container).on('keydown', (keyVent) => {
            var which = keyVent.which;
            var target = keyVent.target;
            var dateCurrent = this.getCurrentDate(container);

            if (!dateCurrent) {
                dateCurrent = $('a.ui-state-default')[0];
                this.setHighlightState(dateCurrent, container);
            }

            if (27 === which) {
                keyVent.stopPropagation();
                return this.closeCalendar();
            } else if (which === 9 && keyVent.shiftKey) { // SHIFT + TAB
                keyVent.preventDefault();
                if ($(target).hasClass('ui-datepicker-close')) { // close button
                    $('.ui-datepicker-prev')[0].focus();
                } else if ($(target).hasClass('ui-state-default')) { // a date link
                    $('.ui-datepicker-close')[0].focus();
                } else if ($(target).hasClass('ui-datepicker-prev')) { // the prev link
                    $('.ui-datepicker-next')[0].focus();
                } else if ($(target).hasClass('ui-datepicker-next')) { // the next link
                    activeDate = $('.ui-state-highlight') ||
                        $('.ui-state-active')[0];
                    if (activeDate) {
                        activeDate.focus();
                    }
                }
            } else if (which === 9) { // TAB
                keyVent.preventDefault();
                if ($(target).hasClass('ui-datepicker-close')) { // close button
                    activeDate = $('.ui-state-highlight') ||
                        $('.ui-state-active')[0];
                    if (activeDate) {
                        activeDate.focus();
                    }
                } else if ($(target).hasClass('ui-state-default')) {
                    $('.ui-datepicker-next')[0].focus();
                } else if ($(target).hasClass('ui-datepicker-next')) {
                    $('.ui-datepicker-prev')[0].focus();
                } else if ($(target).hasClass('ui-datepicker-prev')) {
                    $('.ui-datepicker-close')[0].focus();
                }
            } else if (which === 37) { // LEFT arrow key
                // if we're on a date link...
                if (!$(target).hasClass('ui-datepicker-close') && $(target).hasClass('ui-state-default')) {
                    keyVent.preventDefault();
                    this.previousDay(target);
                }
            } else if (which === 39) { // RIGHT arrow key
                // if we're on a date link...
                if (!$(target).hasClass('ui-datepicker-close') && $(target).hasClass('ui-state-default')) {
                    keyVent.preventDefault();
                    this.nextDay(target);
                }
            } else if (which === 38) { // UP arrow key
                if (!$(target).hasClass('ui-datepicker-close') && $(target).hasClass('ui-state-default')) {
                    keyVent.preventDefault();
                    this.upHandler(target, container, this.prev);
                }
            } else if (which === 40) { // DOWN arrow key
                if (!$(target).hasClass('ui-datepicker-close') && $(target).hasClass('ui-state-default')) {
                    keyVent.preventDefault();
                    this.downHandler(target, container, this.next);
                }
            } else if (which === 13) { // ENTER
                if ($(target).hasClass('ui-state-default')) {
                    setTimeout(() => {
                        this.closeCalendar();
                    }, 100);
                } else if ($(target).hasClass('ui-datepicker-prev')) {
                    this.handlePrevClicks();
                } else if ($(target).hasClass('ui-datepicker-next')) {
                    this.handleNextClicks();
                }
            } else if (32 === which) {
                if ($(target).hasClass('ui-datepicker-prev') || $(target).hasClass('ui-datepicker-next')) {
                    target.click();
                }
            } else if (33 === which) { // PAGE UP
                this.moveOneMonth(target, 'prev');
            } else if (34 === which) { // PAGE DOWN
                this.moveOneMonth(target, 'next');
            } else if (36 === which) { // HOME
                var firstOfMonth = $(target).closest('tbody').find('.ui-state-default')[0];
                if (firstOfMonth) {
                    firstOfMonth.focus();
                    this.setHighlightState(firstOfMonth, $('#ui-datepicker-div')[0]);
                }
            } else if (35 === which) { // END
                var $daysOfMonth = $(target).closest('tbody').find('.ui-state-default');
                var lastDay = $daysOfMonth[$daysOfMonth.length - 1];
                if (lastDay) {
                    lastDay.focus();
                    this.setHighlightState(lastDay, $('#ui-datepicker-div')[0]);
                }
            }
            $(".ui-datepicker-current").hide();
        });
    }

    closeCalendar() {
        var container = $('#ui-datepicker-div');
        $(container).off('keydown');
        var input = $(this.datePicker)[0];
        $(input).datepicker('hide');

        input.focus();
    }

    removeAria() {
        // make the rest of the page accessible again:
        $("#dp-container").removeAttr('aria-hidden');
        $("#skipnav").removeAttr('aria-hidden');
    }

    ///////////////////////////////
    //////////////////////////// //
    ///////////////////////// // //
    // UTILITY-LIKE THINGS // // //
    ///////////////////////// // //
    //////////////////////////// //
    ///////////////////////////////
    isOdd(num) {
        return num % 2;
    }

    moveOneMonth(currentDate, dir) {
        var button = (dir === 'next')
            ? $('.ui-datepicker-next')[0]
            : $('.ui-datepicker-prev')[0];

        if (!button) {
            return;
        }

        var ENABLED_SELECTOR = '#ui-datepicker-div tbody td:not(.ui-state-disabled)';
        var $currentCells = $(ENABLED_SELECTOR);
        var currentIdx = $.inArray(currentDate.parentNode, $currentCells);

        button.click();
        setTimeout(() => {
            this.updateHeaderElements();

            var $newCells = $(ENABLED_SELECTOR);
            var newTd = $newCells[currentIdx];
            var newAnchor = newTd && $(newTd).find('a')[0];

            while (!newAnchor) {
                currentIdx--;
                newTd = $newCells[currentIdx];
                newAnchor = newTd && $(newTd).find('a')[0];
            }

            this.setHighlightState(newAnchor, $('#ui-datepicker-div')[0]);
            newAnchor.focus();

        }, 0);

    }

    handleNextClicks() {
        setTimeout(() => {
            this.updateHeaderElements();
            this.prepHighlightState();
            $('.ui-datepicker-next').focus();
            $(".ui-datepicker-current").hide();
        }, 0);
    }

    handlePrevClicks() {
        setTimeout(() => {
            this.updateHeaderElements();
            this.prepHighlightState();
            $('.ui-datepicker-prev').focus();
            $(".ui-datepicker-current").hide();
        }, 0);
    }

    previousDay(dateLink) {
        var container = document.getElementById('ui-datepicker-div');
        if (!dateLink) {
            return;
        }
        var td = $(dateLink).closest('td');
        if (!td) {
            return;
        }

        var prevTd = $(td).prev(),
            prevDateLink = $('a.ui-state-default', prevTd)[0];

        if (prevTd && prevDateLink) {
            this.setHighlightState(prevDateLink, container);
            prevDateLink.focus();
        } else {
            this.handlePrevious(dateLink);
        }
    }


    handlePrevious(target) {
        var container = document.getElementById('ui-datepicker-div');
        if (!target) {
            return;
        }
        var currentRow = $(target).closest('tr');
        if (!currentRow) {
            return;
        }
        var previousRow = $(currentRow).prev();

        if (!previousRow || previousRow.length === 0) {
            // there is not previous row, so we go to previous month...
            this.previousMonth();
        } else {
            var prevRowDates = $('td a.ui-state-default', previousRow);
            var prevRowDate = prevRowDates[prevRowDates.length - 1];

            if (prevRowDate) {
                setTimeout(() => {
                    this.setHighlightState(prevRowDate, container);
                    prevRowDate.focus();
                }, 0);
            }
        }
    }

    previousMonth() {
        var prevLink = $('.ui-datepicker-prev')[0];
        var container = document.getElementById('ui-datepicker-div');
        prevLink.click();
        // focus last day of new month
        setTimeout(() => {
            var trs = $('tr', container),
                lastRowTdLinks = $('td a.ui-state-default', trs[trs.length - 1]),
                lastDate = lastRowTdLinks[lastRowTdLinks.length - 1];

            // updating the cached header elements
            this.updateHeaderElements();

            this.setHighlightState(lastDate, container);
            lastDate.focus();

        }, 0);
    }

    ///////////////// NEXT /////////////////
    /**
     * Handles right arrow key navigation
     * @param  {HTMLElement} dateLink The target of the keyboard event
     */
    nextDay(dateLink) {
        var container = document.getElementById('ui-datepicker-div');
        if (!dateLink) {
            return;
        }
        var td = $(dateLink).closest('td');
        if (!td) {
            return;
        }
        var nextTd = $(td).next(),
            nextDateLink = $('a.ui-state-default', nextTd)[0];

        if (nextTd && nextDateLink) {
            this.setHighlightState(nextDateLink, container);
            nextDateLink.focus(); // the next day (same row)
        } else {
            this.handleNext(dateLink);
        }
    }

    handleNext(target) {
        var container = document.getElementById('ui-datepicker-div');
        if (!target) {
            return;
        }
        var currentRow = $(target).closest('tr'),
            nextRow = $(currentRow).next();

        if (!nextRow || nextRow.length === 0) {
            this.nextMonth();
        } else {
            var nextRowFirstDate = $('a.ui-state-default', nextRow)[0];
            if (nextRowFirstDate) {
                this.setHighlightState(nextRowFirstDate, container);
                nextRowFirstDate.focus();
            }
        }
    }

    nextMonth() {
        var nextMon = $('.ui-datepicker-next')[0];
        var container = document.getElementById('ui-datepicker-div');
        nextMon.click();
        // focus the first day of the new month
        setTimeout(() => {
            // updating the cached header elements
            this.updateHeaderElements();

            var firstDate = $('a.ui-state-default', container)[0];
            this.setHighlightState(firstDate, container);
            firstDate.focus();
        }, 0);
    }

    /////////// UP ///////////
    /**
     * Handle the up arrow navigation through dates
     * @param  {HTMLElement} target   The target of the keyboard event (day)
     * @param  {HTMLElement} cont     The calendar container
     * @param  {HTMLElement} prevLink Link to navigate to previous month
     */
    upHandler(target, cont, prevLink) {
        prevLink = $('.ui-datepicker-prev')[0];
        var rowContext = $(target).closest('tr');
        if (!rowContext) {
            return;
        }
        var rowTds = $('td', rowContext),
            rowLinks = $('a.ui-state-default', rowContext),
            targetIndex = $.inArray(target, rowLinks),
            prevRow = $(rowContext).prev(),
            prevRowTds = $('td', prevRow),
            parallel = prevRowTds[targetIndex],
            linkCheck = $('a.ui-state-default', parallel)[0];

        if (prevRow && parallel && linkCheck) {
            // there is a previous row, a td at the same index
            // of the target AND theres a link in that td
            this.setHighlightState(linkCheck, cont);
            linkCheck.focus();
        } else {
            // we're either on the first row of a month, or we're on the
            // second and there is not a date link directly above the target
            prevLink.click();
            setTimeout(() => {
                // updating the cached header elements
                this.updateHeaderElements();
                var newRows = $('tr', cont),
                    lastRow = newRows[newRows.length - 1],
                    lastRowTds = $('td', lastRow),
                    tdParallelIndex = $.inArray(target.parentNode, rowTds),
                    newParallel = lastRowTds[tdParallelIndex],
                    newCheck = $('a.ui-state-default', newParallel)[0];

                if (lastRow && newParallel && newCheck) {
                    this.setHighlightState(newCheck, cont);
                    newCheck.focus();
                } else {
                    // theres no date link on the last week (row) of the new month
                    // meaning its an empty cell, so we'll try the 2nd to last week
                    var secondLastRow = newRows[newRows.length - 2],
                        secondTds = $('td', secondLastRow),
                        targetTd = secondTds[tdParallelIndex],
                        linkCheck = $('a.ui-state-default', targetTd)[0];

                    if (linkCheck) {
                        this.setHighlightState(linkCheck, cont);
                        linkCheck.focus();
                    }

                }
            }, 0);
        }
    }

    //////////////// DOWN ////////////////
    /**
     * Handles down arrow navigation through dates in calendar
     * @param  {HTMLElement} target   The target of the keyboard event (day)
     * @param  {HTMLElement} cont     The calendar container
     * @param  {HTMLElement} nextLink Link to navigate to next month
     */
    downHandler(target, cont, nextLink) {
        nextLink = $('.ui-datepicker-next')[0];
        var targetRow = $(target).closest('tr');
        if (!targetRow) {
            return;
        }
        var targetCells = $('td', targetRow),
            cellIndex = $.inArray(target.parentNode, targetCells), // the td (parent of target) index
            nextRow = $(targetRow).next(),
            nextRowCells = $('td', nextRow),
            nextWeekTd = nextRowCells[cellIndex],
            nextWeekCheck = $('a.ui-state-default', nextWeekTd)[0];

        if (nextRow && nextWeekTd && nextWeekCheck) {
            // theres a next row, a TD at the same index of `target`,
            // and theres an anchor within that td
            this.setHighlightState(nextWeekCheck, cont);
            nextWeekCheck.focus();
        } else {
            nextLink.click();

            setTimeout(() => {
                // updating the cached header elements
                this.updateHeaderElements();

                var nextMonthTrs = $('tbody tr', cont),
                    firstTds = $('td', nextMonthTrs[0]),
                    firstParallel = firstTds[cellIndex],
                    firstCheck = $('a.ui-state-default', firstParallel)[0];

                if (firstParallel && firstCheck) {
                    this.setHighlightState(firstCheck, cont);
                    firstCheck.focus();
                } else {
                    // lets try the second row b/c we didnt find a
                    // date link in the first row at the target's index
                    var secondRow = nextMonthTrs[1],
                        secondTds = $('td', secondRow),
                        secondRowTd = secondTds[cellIndex],
                        secondCheck = $('a.ui-state-default', secondRowTd)[0];

                    if (secondRow && secondCheck) {
                        this.setHighlightState(secondCheck, cont);
                        secondCheck.focus();
                    }
                }
            }, 0);
        }
    }


    onCalendarHide() {
        this.closeCalendar();
    }

    // add an aria-label to the date link indicating the currently focused date
    // (formatted identically to the required format: mm/dd/yyyy)
    monthDayYearText() {
        var cleanUps = $('.amaze-date');

        $(cleanUps).each((clean) => {
            // each(cleanUps, function (clean) {
            clean.parentNode.removeChild(clean);
        });

        var datePickDiv = document.getElementById('ui-datepicker-div');
        // in case we find no datepick div
        if (!datePickDiv) {
            return;
        }

        var dates = $('a.ui-state-default', datePickDiv);
        $(dates).attr('role', 'button').on('keydown', (e) => {
            if (e.which === 32) {
                e.preventDefault();
                e.target.click();
                setTimeout(() => {
                    this.closeCalendar();
                }, 100);
            }
        });
        $(dates).each((index, date) => {
            var currentRow = $(date).closest('tr'),
                currentTds = $('td', currentRow),
                currentIndex = $.inArray(date.parentNode, currentTds),
                headThs = $('thead tr th', datePickDiv),
                dayIndex = headThs[currentIndex],
                daySpan = $('span', dayIndex)[0],
                monthName = $('.ui-datepicker-month', datePickDiv)[0].innerHTML,
                year = $('.ui-datepicker-year', datePickDiv)[0].innerHTML,
                number = date.innerHTML;

            if (!daySpan || !monthName || !number || !year) {
                return;
            }

            // AT Reads: {month} {date} {year} {day}
            // "December 18 2014 Thursday"
            var dateText = date.innerHTML + ' ' + monthName + ' ' + year + ' ' + daySpan.title;
            // AT Reads: {date(number)} {name of day} {name of month} {year(number)}
            // var dateText = date.innerHTML + ' ' + daySpan.title + ' ' + monthName + ' ' + year;
            // add an aria-label to the date link reading out the currently focused date
            date.setAttribute('aria-label', dateText);
        });
    }



    // update the cached header elements because we're in a new month or year
    updateHeaderElements() {
        var context = document.getElementById('ui-datepicker-div');
        if (!context) {
            return;
        }

        //  $(context).find('table').first().attr('role', 'grid');

        this.prev = $('.ui-datepicker-prev', context)[0];
        this.next = $('.ui-datepicker-next', context)[0];

        //make them click/focus - able
        this.next.href = 'javascript:void(0)';
        this.prev.href = 'javascript:void(0)';

        this.next.setAttribute('role', 'button');
        this.prev.setAttribute('role', 'button');
        this.appendOffscreenMonthText(this.next);
        this.appendOffscreenMonthText(this.prev);

        $(this.next).on('click', () => this.handleNextClicks());
        $(this.prev).on('click', () => this.handlePrevClicks());

        // add month day year text
        this.monthDayYearText();
    }


    prepHighlightState() {
        var highlight;
        var cage = document.getElementById('ui-datepicker-div');
        highlight = $('.ui-state-highlight', cage)[0] ||
            $('.ui-state-default', cage)[0];
        if (highlight && cage) {
            this.setHighlightState(highlight, cage);
        }
    }

    // Set the highlighted class to date elements, when focus is received
    setHighlightState(newHighlight, container) {
        var prevHighlight = this.getCurrentDate(container);
        // remove the highlight state from previously
        // highlighted date and add it to our newly active date
        $(prevHighlight).removeClass('ui-state-highlight');
        $(prevHighlight).removeAttr('aria-current');
        $(newHighlight).attr('aria-current', 'date');
        $(newHighlight).addClass('ui-state-highlight');
    }


    // grabs the current date based on the highlight class
    getCurrentDate(container) {
        var currentDate = $('.ui-state-highlight', container)[0];
        return currentDate;
    }

    /**
     * Appends logical next/prev month text to the buttons
     * - ex: Next Month, January 2015
     *       Previous Month, November 2014
     */
    appendOffscreenMonthText(button) {
        var buttonText;
        var isNext = $(button).hasClass('ui-datepicker-next');
        var months = this.months;

        var currentMonth = $('.ui-datepicker-title .ui-datepicker-month').text().toLowerCase();
        var monthIndex = this.findIndex(currentMonth.toLowerCase(), months);//$.inArray(currentMonth.toLowerCase(), months);
        var currentYear = $('.ui-datepicker-title .ui-datepicker-year').text().toLowerCase();
        var adjacentIndex = (isNext) ? monthIndex + 1 : monthIndex - 1;

        if (isNext && monthIndex === months.length - 1) {
            currentYear = parseInt(currentYear, 10) + 1;
            adjacentIndex = 0;
        } else if (!isNext && monthIndex === 0) {
            currentYear = parseInt(currentYear, 10) - 1;
            adjacentIndex = months.length - 1;
        }

        buttonText = (isNext)
            ? DatePickerAria.MONTH_NEXT_TEXT + ', ' + this.firstToCap(months[adjacentIndex]) + ' ' + currentYear
            : DatePickerAria.MONTH_PREV_TEXT + ', ' + this.firstToCap(months[adjacentIndex]) + ' ' + currentYear;

        $(button).find('.ui-icon').html(buttonText);

    }

    // Returns the string with the first letter capitalized
    firstToCap(s) {
        return s.charAt(0).toUpperCase() + s.slice(1);
    }

    findIndex(search, array) {
        return array.findIndex(item => search.toLowerCase() === item.toLowerCase());
    }
}

function initDatePickerAria() {
    document.querySelectorAll(DatePickerAria.SELECTOR_DATA).forEach((element) => {
        new DatePickerAria(element);
    });
}

document.addEventListener('DOMContentLoaded', initDatePickerAria);

// opzionale: lo esponi globalmente
window.initDatePickerAria = initDatePickerAria;