//noinspection JSUnusedGlobalSymbols
/* global DateTimePicker */
/* global feather */
const TempusDominusBootstrap4 = ($ => { // eslint-disable-line no-unused-vars
    // ReSharper disable once InconsistentNaming
    const JQUERY_NO_CONFLICT = $.fn[DateTimePicker.NAME],
        verticalModes = ['top', 'bottom', 'auto'],
        horizontalModes = ['left', 'right', 'auto'],
        toolbarPlacements = ['default', 'top', 'bottom'],
        getSelectorFromElement = function ($element) {
            let selector = $element.data('target'),
                $selector;

            if (!selector) {
                selector = $element.attr('href') || '';
                selector = /^#[a-z]/i.test(selector) ? selector : null;
            }
            $selector = $(selector);
            if ($selector.length === 0) {
                return $element;
            }

            if (!$selector.data(DateTimePicker.DATA_KEY)) {
                $.extend({}, $selector.data(), $(this).data());
            }

            return $selector;
        };

    // ReSharper disable once InconsistentNaming
    class TempusDominusBootstrap4 extends DateTimePicker {
        constructor(element, options) {
            super(element, options);
            this._init();
        }

        _init() {
            if (this._element.hasClass('input-group')) {
                const datepickerButton = this._element.find('.datepickerbutton');
                if (datepickerButton.length === 0) {
                    this.component = this._element.find('[data-toggle="datetimepicker"]');
                } else {
                    this.component = datepickerButton;
                }
            }
        }

        _iconTag(iconName) {
            if (typeof feather !== 'undefined' && this._useFeatherIcons() && feather.icons[iconName]) {
                return $('<span>').html(feather.icons[iconName].toSvg());
            }
            else {
               return $('<span>').addClass(iconName);
            }
        }

        _getDatePickerTemplate() {
            const headTemplate = $('<thead>').append($('<tr>').append($('<th>').addClass('prev').attr('data-action', 'previous').append(this._iconTag(this._options.icons.previous))).append($('<th>').addClass('picker-switch').attr('data-action', 'pickerSwitch').attr('colspan', `${this._options.calendarWeeks ? '6' : '5'}`)).append($('<th>').addClass('next').attr('data-action', 'next').append(this._iconTag(this._options.icons.next)))),
                contTemplate = $('<tbody>').append($('<tr>').append($('<td>').attr('colspan', `${this._options.calendarWeeks ? '8' : '7'}`)));

            return [$('<div>').addClass('datepicker-days').append($('<table>').addClass('table table-sm').append(headTemplate).append($('<tbody>'))), $('<div>').addClass('datepicker-months').append($('<table>').addClass('table-condensed').append(headTemplate.clone()).append(contTemplate.clone())), $('<div>').addClass('datepicker-years').append($('<table>').addClass('table-condensed').append(headTemplate.clone()).append(contTemplate.clone())), $('<div>').addClass('datepicker-decades').append($('<table>').addClass('table-condensed').append(headTemplate.clone()).append(contTemplate.clone()))];
        }

        _getTimePickerMainTemplate() {
            const topRow = $('<tr>'),
                middleRow = $('<tr>'),
                bottomRow = $('<tr>');

            if (this._isEnabled('h')) {
                topRow.append($('<td>').append($('<a>').attr({
                    href: '#',
                    tabindex: '-1',
                    'title': this._options.tooltips.incrementHour
                }).addClass('btn').attr('data-action', 'incrementHours').append(this._iconTag(this._options.icons.up))));
                middleRow.append($('<td>').append($('<span>').addClass('timepicker-hour').attr({
                    'data-time-component': 'hours',
                    'title': this._options.tooltips.pickHour
                }).attr('data-action', 'showHours')));
                bottomRow.append($('<td>').append($('<a>').attr({
                    href: '#',
                    tabindex: '-1',
                    'title': this._options.tooltips.decrementHour
                }).addClass('btn').attr('data-action', 'decrementHours').append(this._iconTag(this._options.icons.down))));
            }
            if (this._isEnabled('m')) {
                if (this._isEnabled('h')) {
                    topRow.append($('<td>').addClass('separator'));
                    middleRow.append($('<td>').addClass('separator').html(':'));
                    bottomRow.append($('<td>').addClass('separator'));
                }
                topRow.append($('<td>').append($('<a>').attr({
                    href: '#',
                    tabindex: '-1',
                    'title': this._options.tooltips.incrementMinute
                }).addClass('btn').attr('data-action', 'incrementMinutes').append(this._iconTag(this._options.icons.up))));
                middleRow.append($('<td>').append($('<span>').addClass('timepicker-minute').attr({
                    'data-time-component': 'minutes',
                    'title': this._options.tooltips.pickMinute
                }).attr('data-action', 'showMinutes')));
                bottomRow.append($('<td>').append($('<a>').attr({
                    href: '#',
                    tabindex: '-1',
                    'title': this._options.tooltips.decrementMinute
                }).addClass('btn').attr('data-action', 'decrementMinutes').append(this._iconTag(this._options.icons.down))));
            }
            if (this._isEnabled('s')) {
                if (this._isEnabled('m')) {
                    topRow.append($('<td>').addClass('separator'));
                    middleRow.append($('<td>').addClass('separator').html(':'));
                    bottomRow.append($('<td>').addClass('separator'));
                }
                topRow.append($('<td>').append($('<a>').attr({
                    href: '#',
                    tabindex: '-1',
                    'title': this._options.tooltips.incrementSecond
                }).addClass('btn').attr('data-action', 'incrementSeconds').append(this._iconTag(this._options.icons.up))));
                middleRow.append($('<td>').append($('<span>').addClass('timepicker-second').attr({
                    'data-time-component': 'seconds',
                    'title': this._options.tooltips.pickSecond
                }).attr('data-action', 'showSeconds')));
                bottomRow.append($('<td>').append($('<a>').attr({
                    href: '#',
                    tabindex: '-1',
                    'title': this._options.tooltips.decrementSecond
                }).addClass('btn').attr('data-action', 'decrementSeconds').append(this._iconTag(this._options.icons.down))));
            }

            if (!this.use24Hours) {
                topRow.append($('<td>').addClass('separator'));
                middleRow.append($('<td>').append($('<button>').addClass('btn btn-primary').attr({
                    'data-action': 'togglePeriod',
                    tabindex: '-1',
                    'title': this._options.tooltips.togglePeriod
                })));
                bottomRow.append($('<td>').addClass('separator'));
            }

            return $('<div>').addClass('timepicker-picker').append($('<table>').addClass('table-condensed').append([topRow, middleRow, bottomRow]));
        }

        _getTimePickerTemplate() {
            const hoursView = $('<div>').addClass('timepicker-hours').append($('<table>').addClass('table-condensed')),
                minutesView = $('<div>').addClass('timepicker-minutes').append($('<table>').addClass('table-condensed')),
                secondsView = $('<div>').addClass('timepicker-seconds').append($('<table>').addClass('table-condensed')),
                ret = [this._getTimePickerMainTemplate()];

            if (this._isEnabled('h')) {
                ret.push(hoursView);
            }
            if (this._isEnabled('m')) {
                ret.push(minutesView);
            }
            if (this._isEnabled('s')) {
                ret.push(secondsView);
            }

            return ret;
        }

        _getToolbar() {
            const row = [];
            if (this._options.buttons.showToday) {
                row.push($('<td>').append($('<a>').attr({
                    href: '#',
                    tabindex: '-1',
                    'data-action': 'today',
                    'title': this._options.tooltips.today
                }).append(this._iconTag(this._options.icons.today))));
            }
            if (!this._options.sideBySide && this._options.collapse && this._hasDate() && this._hasTime()) {
                let title, icon;
                if (this._options.viewMode === 'times') {
                    title = this._options.tooltips.selectDate;
                    icon = this._options.icons.date;
                } else {
                    title = this._options.tooltips.selectTime;
                    icon = this._options.icons.time;
                }
                row.push($('<td>').append($('<a>').attr({
                    href: '#',
                    tabindex: '-1',
                    'data-action': 'togglePicker',
                    'title': title
                }).append(this._iconTag(icon))));
            }
            if (this._options.buttons.showClear) {
                row.push($('<td>').append($('<a>').attr({
                    href: '#',
                    tabindex: '-1',
                    'data-action': 'clear',
                    'title': this._options.tooltips.clear
                }).append(this._iconTag(this._options.icons.clear))));
            }
            if (this._options.buttons.showClose) {
                row.push($('<td>').append($('<a>').attr({
                    href: '#',
                    tabindex: '-1',
                    'data-action': 'close',
                    'title': this._options.tooltips.close
                }).append(this._iconTag(this._options.icons.close))));
            }
            return row.length === 0 ? '' : $('<table>').addClass('table-condensed').append($('<tbody>').append($('<tr>').append(row)));
        }

        _getTemplate() {
            const template = $('<div>').addClass((
                    `bootstrap-datetimepicker-widget dropdown-menu ${this._options.calendarWeeks ? 'tempusdominus-bootstrap-datetimepicker-widget-with-calendar-weeks' : ''} `
                    +
                    `${this._useFeatherIcons() ? 'tempusdominus-bootstrap-datetimepicker-widget-with-feather-icons' : ''} `
                ).trim()),
                dateView = $('<div>').addClass('datepicker').append(this._getDatePickerTemplate()),
                timeView = $('<div>').addClass('timepicker').append(this._getTimePickerTemplate()),
                content = $('<ul>').addClass('list-unstyled'),
                toolbar = $('<li>').addClass((
                    `picker-switch${this._options.collapse ? ' accordion-toggle' : ''} ` +
                    `${this._useFeatherIcons() ? 'picker-switch-with-feathers-icons' : ''}`
                ).trim()).append(this._getToolbar());

            if (this._options.inline) {
                template.removeClass('dropdown-menu');
            }

            if (this.use24Hours) {
                template.addClass('usetwentyfour');
            }
            if ((this.input !== undefined && this.input.prop('readonly')) || this._options.readonly) {
                template.addClass('bootstrap-datetimepicker-widget-readonly');
            }
            if (this._isEnabled('s') && !this.use24Hours) {
                template.addClass('wider');
            }

            if (this._options.sideBySide && this._hasDate() && this._hasTime()) {
                template.addClass('timepicker-sbs');
                if (this._options.toolbarPlacement === 'top') {
                    template.append(toolbar);
                }
                template.append($('<div>').addClass('row').append(dateView.addClass('col-md-6')).append(timeView.addClass('col-md-6')));
                if (this._options.toolbarPlacement === 'bottom' || this._options.toolbarPlacement === 'default') {
                    template.append(toolbar);
                }
                return template;
            }

            if (this._options.toolbarPlacement === 'top') {
                content.append(toolbar);
            }
            if (this._hasDate()) {
                content.append($('<li>').addClass(this._options.collapse && this._hasTime() ? 'collapse' : '')
                    .addClass((this._options.collapse && this._hasTime() && this._options.viewMode === 'times' ? '' : 'show'))
                    .append(dateView));
            }
            if (this._options.toolbarPlacement === 'default') {
                content.append(toolbar);
            }
            if (this._hasTime()) {
                content.append($('<li>').addClass(this._options.collapse && this._hasDate() ? 'collapse' : '')
                    .addClass((this._options.collapse && this._hasDate() && this._options.viewMode === 'times' ? 'show' : ''))
                    .append(timeView));
            }
            if (this._options.toolbarPlacement === 'bottom') {
                content.append(toolbar);
            }
            return template.append(content);
        }

        _place(e) {
            let self = (e && e.data && e.data.picker) || this, vertical = self._options.widgetPositioning.vertical,
                horizontal = self._options.widgetPositioning.horizontal,
                parent;
            const position = (self.component && self.component.length ? self.component : self._element).position(),
                offset = (self.component && self.component.length ? self.component : self._element).offset();
            if (self._options.widgetParent) {
                parent = self._options.widgetParent.append(self.widget);
            } else if (self._element.is('input')) {
                parent = self._element.after(self.widget).parent();
            } else if (self._options.inline) {
                parent = self._element.append(self.widget);
                return;
            } else {
                parent = self._element;
                self._element.children().first().after(self.widget);
            }

            // Top and bottom logic
            if (vertical === 'auto') {
                //noinspection JSValidateTypes
                if (offset.top + self.widget.height() * 1.5 >= $(window).height() + $(window).scrollTop() && self.widget.height() + self._element.outerHeight() < offset.top) {
                    vertical = 'top';
                } else {
                    vertical = 'bottom';
                }
            }

            // Left and right logic
            if (horizontal === 'auto') {
                if (parent.width() < offset.left + self.widget.outerWidth() / 2 && offset.left + self.widget.outerWidth() > $(window).width()) {
                    horizontal = 'right';
                } else {
                    horizontal = 'left';
                }
            }

            if (vertical === 'top') {
                self.widget.addClass('top').removeClass('bottom');
            } else {
                self.widget.addClass('bottom').removeClass('top');
            }

            if (horizontal === 'right') {
                self.widget.addClass('float-right');
            } else {
                self.widget.removeClass('float-right');
            }

            // find the first parent element that has a relative css positioning
            if (parent.css('position') !== 'relative') {
                parent = parent.parents().filter(function () {
                    return $(this).css('position') === 'relative';
                }).first();
            }

            if (parent.length === 0) {
                throw new Error('datetimepicker component should be placed within a relative positioned container');
            }

            self.widget.css({
                top: vertical === 'top' ? 'auto' : position.top + self._element.outerHeight() + 'px',
                bottom: vertical === 'top' ? parent.outerHeight() - (parent === self._element ? 0 : position.top) + 'px' : 'auto',
                left: horizontal === 'left' ? (parent === self._element ? 0 : position.left) + 'px' : 'auto',
                right: horizontal === 'left' ? 'auto' : parent.outerWidth() - self._element.outerWidth() - (parent === self._element ? 0 : position.left) + 'px'
            });
        }

        _fillDow() {
            const row = $('<tr>'),
                currentDate = this._viewDate.clone().startOf('w').startOf('d');

            if (this._options.calendarWeeks === true) {
                row.append($('<th>').addClass('cw').text('#'));
            }

            while (currentDate.isBefore(this._viewDate.clone().endOf('w'))) {
                row.append($('<th>').addClass('dow').text(currentDate.format('dd')));
                currentDate.add(1, 'd');
            }
            this.widget.find('.datepicker-days thead').append(row);
        }

        _fillMonths() {
            const spans = [],
                monthsShort = this._viewDate.clone().startOf('y').startOf('d');
            while (monthsShort.isSame(this._viewDate, 'y')) {
                spans.push($('<span>').attr('data-action', 'selectMonth').addClass('month').text(monthsShort.format('MMM')));
                monthsShort.add(1, 'M');
            }
            this.widget.find('.datepicker-months td').empty().append(spans);
        }

        _updateMonths() {
            const monthsView = this.widget.find('.datepicker-months'),
                monthsViewHeader = monthsView.find('th'),
                months = monthsView.find('tbody').find('span'), self = this,
                lastPickedDate = this._getLastPickedDate();

            monthsViewHeader.eq(0).find('span').attr('title', this._options.tooltips.prevYear);
            monthsViewHeader.eq(1).attr('title', this._options.tooltips.selectYear);
            monthsViewHeader.eq(2).find('span').attr('title', this._options.tooltips.nextYear);

            monthsView.find('.disabled').removeClass('disabled');

            if (!this._isValid(this._viewDate.clone().subtract(1, 'y'), 'y')) {
                monthsViewHeader.eq(0).addClass('disabled');
            }

            monthsViewHeader.eq(1).text(this._viewDate.year());

            if (!this._isValid(this._viewDate.clone().add(1, 'y'), 'y')) {
                monthsViewHeader.eq(2).addClass('disabled');
            }

            months.removeClass('active');
            if (lastPickedDate && lastPickedDate.isSame(this._viewDate, 'y') && !this.unset) {
                months.eq(lastPickedDate.month()).addClass('active');
            }

            months.each(function (index) {
                if (!self._isValid(self._viewDate.clone().month(index), 'M')) {
                    $(this).addClass('disabled');
                }
            });
        }

        _getStartEndYear(factor, year) {
            const step = factor / 10,
                startYear = Math.floor(year / factor) * factor,
                endYear = startYear + step * 9,
                focusValue = Math.floor(year / step) * step;
            return [startYear, endYear, focusValue];
        }

        _updateYears() {
            const yearsView = this.widget.find('.datepicker-years'),
                yearsViewHeader = yearsView.find('th'),
                yearCaps = this._getStartEndYear(10, this._viewDate.year()),
                startYear = this._viewDate.clone().year(yearCaps[0]),
                endYear = this._viewDate.clone().year(yearCaps[1]);
            let html = '';

            yearsViewHeader.eq(0).find('span').attr('title', this._options.tooltips.prevDecade);
            yearsViewHeader.eq(1).attr('title', this._options.tooltips.selectDecade);
            yearsViewHeader.eq(2).find('span').attr('title', this._options.tooltips.nextDecade);

            yearsView.find('.disabled').removeClass('disabled');

            if (this._options.minDate && this._options.minDate.isAfter(startYear, 'y')) {
                yearsViewHeader.eq(0).addClass('disabled');
            }

            yearsViewHeader.eq(1).text(`${startYear.year()}-${endYear.year()}`);

            if (this._options.maxDate && this._options.maxDate.isBefore(endYear, 'y')) {
                yearsViewHeader.eq(2).addClass('disabled');
            }

            html += `<span data-action="selectYear" class="year old${!this._isValid(startYear, 'y') ? ' disabled' : ''}">${startYear.year() - 1}</span>`;
            while (!startYear.isAfter(endYear, 'y')) {
                html += `<span data-action="selectYear" class="year${startYear.isSame(this._getLastPickedDate(), 'y') && !this.unset ? ' active' : ''}${!this._isValid(startYear, 'y') ? ' disabled' : ''}">${startYear.year()}</span>`;
                startYear.add(1, 'y');
            }
            html += `<span data-action="selectYear" class="year old${!this._isValid(startYear, 'y') ? ' disabled' : ''}">${startYear.year()}</span>`;

            yearsView.find('td').html(html);
        }

        _updateDecades() {
            const decadesView = this.widget.find('.datepicker-decades'),
                decadesViewHeader = decadesView.find('th'),
                yearCaps = this._getStartEndYear(100, this._viewDate.year()),
                startDecade = this._viewDate.clone().year(yearCaps[0]),
                endDecade = this._viewDate.clone().year(yearCaps[1]),
                lastPickedDate = this._getLastPickedDate();
            let minDateDecade = false,
                maxDateDecade = false,
                endDecadeYear,
                html = '';

            decadesViewHeader.eq(0).find('span').attr('title', this._options.tooltips.prevCentury);
            decadesViewHeader.eq(2).find('span').attr('title', this._options.tooltips.nextCentury);

            decadesView.find('.disabled').removeClass('disabled');

            if (startDecade.year() === 0 || this._options.minDate && this._options.minDate.isAfter(startDecade, 'y')) {
                decadesViewHeader.eq(0).addClass('disabled');
            }

            decadesViewHeader.eq(1).text(`${startDecade.year()}-${endDecade.year()}`);

            if (this._options.maxDate && this._options.maxDate.isBefore(endDecade, 'y')) {
                decadesViewHeader.eq(2).addClass('disabled');
            }

            if (startDecade.year() - 10 < 0) {
                html += '<span>&nbsp;</span>';
            } else {
                html += `<span data-action="selectDecade" class="decade old" data-selection="${startDecade.year() + 6}">${startDecade.year() - 10}</span>`;
            }

            while (!startDecade.isAfter(endDecade, 'y')) {
                endDecadeYear = startDecade.year() + 11;
                minDateDecade = this._options.minDate && this._options.minDate.isAfter(startDecade, 'y') && this._options.minDate.year() <= endDecadeYear;
                maxDateDecade = this._options.maxDate && this._options.maxDate.isAfter(startDecade, 'y') && this._options.maxDate.year() <= endDecadeYear;
                html += `<span data-action="selectDecade" class="decade${lastPickedDate && lastPickedDate.isAfter(startDecade) && lastPickedDate.year() <= endDecadeYear ? ' active' : ''}${!this._isValid(startDecade, 'y') && !minDateDecade && !maxDateDecade ? ' disabled' : ''}" data-selection="${startDecade.year() + 6}">${startDecade.year()}</span>`;
                startDecade.add(10, 'y');
            }
            html += `<span data-action="selectDecade" class="decade old" data-selection="${startDecade.year() + 6}">${startDecade.year()}</span>`;

            decadesView.find('td').html(html);
        }

        _fillDate() {
            super._fillDate();
            const daysView = this.widget.find('.datepicker-days'),
                daysViewHeader = daysView.find('th'),
                html = [];
            let currentDate, row, clsName, i;

            if (!this._hasDate()) {
                return;
            }

            daysViewHeader.eq(0).find('span').attr('title', this._options.tooltips.prevMonth);
            daysViewHeader.eq(1).attr('title', this._options.tooltips.selectMonth);
            daysViewHeader.eq(2).find('span').attr('title', this._options.tooltips.nextMonth);

            daysView.find('.disabled').removeClass('disabled');
            daysViewHeader.eq(1).text(this._viewDate.format(this._options.dayViewHeaderFormat));

            if (!this._isValid(this._viewDate.clone().subtract(1, 'M'), 'M')) {
                daysViewHeader.eq(0).addClass('disabled');
            }
            if (!this._isValid(this._viewDate.clone().add(1, 'M'), 'M')) {
                daysViewHeader.eq(2).addClass('disabled');
            }

            currentDate = this._viewDate.clone().startOf('M').startOf('w').add(12, 'hours');

            for (i = 0; i < 42; i++) {
                //always display 42 days (should show 6 weeks)
                if (currentDate.weekday() === 0) {
                    row = $('<tr>');
                    if (this._options.calendarWeeks) {
                        row.append(`<td class="cw">${currentDate.week()}</td>`);
                    }
                    html.push(row);
                }
                clsName = '';
                if (currentDate.isBefore(this._viewDate, 'M')) {
                    clsName += ' old';
                }
                if (currentDate.isAfter(this._viewDate, 'M')) {
                    clsName += ' new';
                }
                if (this._options.allowMultidate) {
                    var index = this._datesFormatted.indexOf(currentDate.format('YYYY-MM-DD'));
                    if (index !== -1) {
                        if (currentDate.isSame(this._datesFormatted[index], 'd') && !this.unset) {
                            clsName += ' active';
                        }
                    }
                } else {
                    if (currentDate.isSame(this._getLastPickedDate(), 'd') && !this.unset) {
                        clsName += ' active';
                    }
                }
                if (!this._isValid(currentDate, 'd')) {
                    clsName += ' disabled';
                }
                if (currentDate.isSame(this.getMoment(), 'd')) {
                    clsName += ' today';
                }
                if (currentDate.day() === 0 || currentDate.day() === 6) {
                    clsName += ' weekend';
                }
                row.append(`<td data-action="selectDay" data-day="${currentDate.format('L')}" class="day${clsName}">${currentDate.date()}</td>`);
                currentDate.add(1, 'd');
            }

            $('body').addClass('tempusdominus-bootstrap-datetimepicker-widget-day-click');
            $('body').append('<div class="tempusdominus-bootstrap-datetimepicker-widget-day-click-glass-panel"></div>');
            daysView.find('tbody').empty().append(html);
            $('body').find('.tempusdominus-bootstrap-datetimepicker-widget-day-click-glass-panel').remove();
            $('body').removeClass('tempusdominus-bootstrap-datetimepicker-widget-day-click');

            this._updateMonths();

            this._updateYears();

            this._updateDecades();
        }

        _fillHours() {
            const table = this.widget.find('.timepicker-hours table'),
                currentHour = this._viewDate.clone().startOf('d'),
                html = [];
            let row = $('<tr>');

            if (this._viewDate.hour() > 11 && !this.use24Hours) {
                currentHour.hour(12);
            }
            while (currentHour.isSame(this._viewDate, 'd') && (this.use24Hours || this._viewDate.hour() < 12 && currentHour.hour() < 12 || this._viewDate.hour() > 11)) {
                if (currentHour.hour() % 4 === 0) {
                    row = $('<tr>');
                    html.push(row);
                }
                row.append(`<td data-action="selectHour" class="hour${!this._isValid(currentHour, 'h') ? ' disabled' : ''}">${currentHour.format(this.use24Hours ? 'HH' : 'hh')}</td>`);
                currentHour.add(1, 'h');
            }
            table.empty().append(html);
        }

        _fillMinutes() {
            const table = this.widget.find('.timepicker-minutes table'),
                currentMinute = this._viewDate.clone().startOf('h'),
                html = [],
                step = this._options.stepping === 1 ? 5 : this._options.stepping;
            let row = $('<tr>');

            while (this._viewDate.isSame(currentMinute, 'h')) {
                if (currentMinute.minute() % (step * 4) === 0) {
                    row = $('<tr>');
                    html.push(row);
                }
                row.append(`<td data-action="selectMinute" class="minute${!this._isValid(currentMinute, 'm') ? ' disabled' : ''}">${currentMinute.format('mm')}</td>`);
                currentMinute.add(step, 'm');
            }
            table.empty().append(html);
        }

        _fillSeconds() {
            const table = this.widget.find('.timepicker-seconds table'),
                currentSecond = this._viewDate.clone().startOf('m'),
                html = [];
            let row = $('<tr>');

            while (this._viewDate.isSame(currentSecond, 'm')) {
                if (currentSecond.second() % 20 === 0) {
                    row = $('<tr>');
                    html.push(row);
                }
                row.append(`<td data-action="selectSecond" class="second${!this._isValid(currentSecond, 's') ? ' disabled' : ''}">${currentSecond.format('ss')}</td>`);
                currentSecond.add(5, 's');
            }

            table.empty().append(html);
        }

        _fillTime() {
            let toggle, newDate;
            const timeComponents = this.widget.find('.timepicker span[data-time-component]'),
                lastPickedDate = this._getLastPickedDate();

            if (!this.use24Hours) {
                toggle = this.widget.find('.timepicker [data-action=togglePeriod]');
                newDate = lastPickedDate ? lastPickedDate.clone().add(lastPickedDate.hours() >= 12 ? -12 : 12, 'h') : void 0;

                lastPickedDate && toggle.text(lastPickedDate.format('A'));

                if (this._isValid(newDate, 'h')) {
                    toggle.removeClass('disabled');
                } else {
                    toggle.addClass('disabled');
                }
            }
            lastPickedDate && timeComponents.filter('[data-time-component=hours]').text(lastPickedDate.format(`${this.use24Hours ? 'HH' : 'hh'}`));
            lastPickedDate && timeComponents.filter('[data-time-component=minutes]').text(lastPickedDate.format('mm'));
            lastPickedDate && timeComponents.filter('[data-time-component=seconds]').text(lastPickedDate.format('ss'));

            this._fillHours();
            this._fillMinutes();
            this._fillSeconds();
        }

        _doAction(e, action) {
            let lastPicked = this._getLastPickedDate();
            if ($(e.currentTarget).is('.disabled')) {
                return false;
            }
            action = action || $(e.currentTarget).data('action');
            switch (action) {
                case 'next':
                    {
                        const navFnc = DateTimePicker.DatePickerModes[this.currentViewMode].NAV_FUNCTION;
                        this._viewDate.add(DateTimePicker.DatePickerModes[this.currentViewMode].NAV_STEP, navFnc);
                        this._fillDate();
                        this._viewUpdate(navFnc);
                        break;
                    }
                case 'previous':
                    {
                        const navFnc = DateTimePicker.DatePickerModes[this.currentViewMode].NAV_FUNCTION;
                        this._viewDate.subtract(DateTimePicker.DatePickerModes[this.currentViewMode].NAV_STEP, navFnc);
                        this._fillDate();
                        this._viewUpdate(navFnc);
                        break;
                    }
                case 'pickerSwitch':
                    this._showMode(1);
                    break;
                case 'selectMonth':
                    {
                        const month = $(e.target).closest('tbody').find('span').index($(e.target));
                        this._viewDate.month(month);
                        if (this.currentViewMode === this.MinViewModeNumber) {
                            this._setValue(lastPicked.clone().year(this._viewDate.year()).month(this._viewDate.month()), this._getLastPickedDateIndex());
                            if (!this._options.inline) {
                                this.hide();
                            }
                        } else {
                            this._showMode(-1);
                            this._fillDate();
                        }
                        this._viewUpdate('M');
                        break;
                    }
                case 'selectYear':
                    {
                        const year = parseInt($(e.target).text(), 10) || 0;
                        this._viewDate.year(year);
                        if (this.currentViewMode === this.MinViewModeNumber) {
                            this._setValue(lastPicked.clone().year(this._viewDate.year()), this._getLastPickedDateIndex());
                            if (!this._options.inline) {
                                this.hide();
                            }
                        } else {
                            this._showMode(-1);
                            this._fillDate();
                        }
                        this._viewUpdate('YYYY');
                        break;
                    }
                case 'selectDecade':
                    {
                        const year = parseInt($(e.target).data('selection'), 10) || 0;
                        this._viewDate.year(year);
                        if (this.currentViewMode === this.MinViewModeNumber) {
                            this._setValue(lastPicked.clone().year(this._viewDate.year()), this._getLastPickedDateIndex());
                            if (!this._options.inline) {
                                this.hide();
                            }
                        } else {
                            this._showMode(-1);
                            this._fillDate();
                        }
                        this._viewUpdate('YYYY');
                        break;
                    }
                case 'selectDay':
                    {
                        const day = this._viewDate.clone();
                        if ($(e.target).is('.old')) {
                            day.subtract(1, 'M');
                        }
                        if ($(e.target).is('.new')) {
                            day.add(1, 'M');
                        }

                        var selectDate = day.date(parseInt($(e.target).text(), 10)), index = 0;
                        if (this._options.allowMultidate) {
                            index = this._datesFormatted.indexOf(selectDate.format('YYYY-MM-DD'));
                            if (index !== -1) {
                                this._setValue(null, index); //deselect multidate
                            } else {
                                this._setValue(selectDate, this._getLastPickedDateIndex() + 1);
                            }
                        } else {
                            this._setValue(selectDate, this._getLastPickedDateIndex());
                        }

                        if (!this._hasTime() && !this._options.keepOpen && !this._options.inline && !this._options.allowMultidate) {
                            this.hide();
                        }
                        break;
                    }
                case 'incrementHours':
                    {
                        if (!lastPicked) {
                          break;
                        }
                        const newDate = lastPicked.clone().add(1, 'h');
                        if (this._isValid(newDate, 'h')) {
                            if (this._getLastPickedDateIndex() < 0) {
                                this.date(newDate);
                            }
                            this._setValue(newDate, this._getLastPickedDateIndex());
                        }
                        break;
                    }
                case 'incrementMinutes':
                    {
                        if (!lastPicked) {
                          break;
                        }
                        const newDate = lastPicked.clone().add(this._options.stepping, 'm');
                        if (this._isValid(newDate, 'm')) {
                            if (this._getLastPickedDateIndex() < 0) {
                                this.date(newDate);
                            }
                            this._setValue(newDate, this._getLastPickedDateIndex());
                        }
                        break;
                    }
                case 'incrementSeconds':
                    {
                        if (!lastPicked) {
                          break;
                        }
                        const newDate = lastPicked.clone().add(1, 's');
                        if (this._isValid(newDate, 's')) {
                            if (this._getLastPickedDateIndex() < 0) {
                                this.date(newDate);
                            }
                            this._setValue(newDate, this._getLastPickedDateIndex());
                        }
                        break;
                    }
                case 'decrementHours':
                    {
                        if (!lastPicked) {
                          break;
                        }
                        const newDate = lastPicked.clone().subtract(1, 'h');
                        if (this._isValid(newDate, 'h')) {
                            if (this._getLastPickedDateIndex() < 0) {
                                this.date(newDate);
                            }
                            this._setValue(newDate, this._getLastPickedDateIndex());
                        }
                        break;
                    }
                case 'decrementMinutes':
                    {
                        if (!lastPicked) {
                          break;
                        }
                        const newDate = lastPicked.clone().subtract(this._options.stepping, 'm');
                        if (this._isValid(newDate, 'm')) {
                            if (this._getLastPickedDateIndex() < 0) {
                                this.date(newDate);
                            }
                            this._setValue(newDate, this._getLastPickedDateIndex());
                        }
                        break;
                    }
                case 'decrementSeconds':
                    {
                        if (!lastPicked) {
                          break;
                        }
                        const newDate = lastPicked.clone().subtract(1, 's');
                        if (this._isValid(newDate, 's')) {
                            if (this._getLastPickedDateIndex() < 0) {
                                this.date(newDate);
                            }
                            this._setValue(newDate, this._getLastPickedDateIndex());
                        }
                        break;
                    }
                case 'togglePeriod':
                    {
                        this._setValue(lastPicked.clone().add(lastPicked.hours() >= 12 ? -12 : 12, 'h'), this._getLastPickedDateIndex());
                        break;
                    }
                case 'togglePicker':
                    {
                        const $this = $(e.target),
                            $link = $this.closest('a'),
                            $parent = $this.closest('ul'),
                            expanded = $parent.find('.show'),
                            closed = $parent.find('.collapse:not(.show)'),
                            $span = $this.is('span') ? $this : $this.find('span');
                        let collapseData, inactiveIcon, iconTest;

                        if (expanded && expanded.length) {
                            collapseData = expanded.data('collapse');
                            if (collapseData && collapseData.transitioning) {
                                return true;
                            }
                            if (expanded.collapse) {
                                // if collapse plugin is available through bootstrap.js then use it
                                expanded.collapse('hide');
                                closed.collapse('show');
                            } else {
                                // otherwise just toggle in class on the two views
                                expanded.removeClass('show');
                                closed.addClass('show');
                            }

                            if (this._useFeatherIcons()) {
                                $link.toggleClass(this._options.icons.time + ' ' + this._options.icons.date);
                                inactiveIcon = ($link.hasClass(this._options.icons.time)) ? this._options.icons.date : this._options.icons.time;
                                $link.html(this._iconTag(inactiveIcon));
                            }
                            else {
                                $span.toggleClass(this._options.icons.time + ' ' + this._options.icons.date);
                            }

                            if (this._useFeatherIcons()) {
                                iconTest = $link.hasClass(this._options.icons.date);
                            }
                            else {
                                iconTest = $span.hasClass(this._options.icons.date);
                            }

                            if (iconTest) {
                                $link.attr('title', this._options.tooltips.selectDate);
                            } else {
                                $link.attr('title', this._options.tooltips.selectTime);
                            }
                        }
                    }
                    break;
                case 'showPicker':
                    this.widget.find('.timepicker > div:not(.timepicker-picker)').hide();
                    this.widget.find('.timepicker .timepicker-picker').show();
                    break;
                case 'showHours':
                    this.widget.find('.timepicker .timepicker-picker').hide();
                    this.widget.find('.timepicker .timepicker-hours').show();
                    break;
                case 'showMinutes':
                    this.widget.find('.timepicker .timepicker-picker').hide();
                    this.widget.find('.timepicker .timepicker-minutes').show();
                    break;
                case 'showSeconds':
                    this.widget.find('.timepicker .timepicker-picker').hide();
                    this.widget.find('.timepicker .timepicker-seconds').show();
                    break;
                case 'selectHour':
                    {
                        let hour = parseInt($(e.target).text(), 10);

                        if (!this.use24Hours) {
                            if (lastPicked.hours() >= 12) {
                                if (hour !== 12) {
                                    hour += 12;
                                }
                            } else {
                                if (hour === 12) {
                                    hour = 0;
                                }
                            }
                        }
                        this._setValue(lastPicked.clone().hours(hour), this._getLastPickedDateIndex());
                        if (!this._isEnabled('a') && !this._isEnabled('m') && !this._options.keepOpen && !this._options.inline) {
                            this.hide();
                        }
                        else {
                            this._doAction(e, 'showPicker');
                        }
                        break;
                    }
                case 'selectMinute':
                    this._setValue(lastPicked.clone().minutes(parseInt($(e.target).text(), 10)), this._getLastPickedDateIndex());
                    if (!this._isEnabled('a') && !this._isEnabled('s') && !this._options.keepOpen && !this._options.inline) {
                        this.hide();
                    }
                    else {
                        this._doAction(e, 'showPicker');
                    }
                    break;
                case 'selectSecond':
                    this._setValue(lastPicked.clone().seconds(parseInt($(e.target).text(), 10)), this._getLastPickedDateIndex());
                    if (!this._isEnabled('a') && !this._options.keepOpen && !this._options.inline) {
                        this.hide();
                    }
                    else {
                        this._doAction(e, 'showPicker');
                    }
                    break;
                case 'clear':
                    this.clear();
                    break;
                case 'close':
                    this.hide();
                    break;
                case 'today':
                    {
                        const todaysDate = this.getMoment();
                        if (this._isValid(todaysDate, 'd')) {
                            this._setValue(todaysDate, this._getLastPickedDateIndex());
                        }
                        break;
                    }
            }
            return false;
        }

        //public
        hide() {
            let transitioning = false;
            if (!this.widget) {
                return;
            }
            // Ignore event if in the middle of a picker transition
            this.widget.find('.collapse').each(function () {
                const collapseData = $(this).data('collapse');
                if (collapseData && collapseData.transitioning) {
                    transitioning = true;
                    return false;
                }
                return true;
            });
            if (transitioning) {
                return;
            }
            if (this.component && this.component.hasClass('btn')) {
                this.component.toggleClass('active');
            }
            this.widget.hide();

            $(window).off('resize', this._place);
            this.widget.off('click', '[data-action]');
            this.widget.off('mousedown', false);

            this.widget.remove();
            this.widget = false;

            if (this.input !== undefined && this.input.val() !== undefined && this.input.val().trim().length !== 0) {
                this._setValue(this._parseInputDate(this.input.val().trim(), {
                    isPickerShow: false
                }), 0);
            }
            const lastPickedDate = this._getLastPickedDate();
            this._notifyEvent({
                type: DateTimePicker.Event.HIDE,
                date: this.unset ? null : (lastPickedDate ? lastPickedDate.clone() : void 0)
            });

            if (this.input !== undefined) {
                this.input.blur();
            }

            this._viewDate = lastPickedDate ? lastPickedDate.clone() : this.getMoment();
        }

        show() {
            let currentMoment, shouldUseCurrentIfUnset = false;
            const useCurrentGranularity = {
                'year': function (m) {
                    return m.month(0).date(1).hours(0).seconds(0).minutes(0);
                },
                'month': function (m) {
                    return m.date(1).hours(0).seconds(0).minutes(0);
                },
                'day': function (m) {
                    return m.hours(0).seconds(0).minutes(0);
                },
                'hour': function (m) {
                    return m.seconds(0).minutes(0);
                },
                'minute': function (m) {
                    return m.seconds(0);
                }
            };

            if (this.input !== undefined) {
                if (this.input.prop('disabled') || !this._options.ignoreReadonly && this.input.prop('readonly') || this.widget) {
                    return;
                }
                if (this.input.val() !== undefined && this.input.val().trim().length !== 0) {
                    this._setValue(this._parseInputDate(this.input.val().trim(), {
                        isPickerShow: true
                    }), 0);
                } else {
                    shouldUseCurrentIfUnset = true
                }
            }
            else {
              shouldUseCurrentIfUnset = true;
            }

            if (shouldUseCurrentIfUnset && this.unset && this._options.useCurrent) {
                currentMoment = this.getMoment();
                if (typeof this._options.useCurrent === 'string') {
                    currentMoment = useCurrentGranularity[this._options.useCurrent](currentMoment);
                }
                this._setValue(currentMoment, 0);
            }

            this.widget = this._getTemplate();

            this._fillDow();
            this._fillMonths();

            this.widget.find('.timepicker-hours').hide();
            this.widget.find('.timepicker-minutes').hide();
            this.widget.find('.timepicker-seconds').hide();

            this._update();
            this._showMode();

            $(window).on('resize', { picker: this }, this._place);
            this.widget.on('click', '[data-action]', $.proxy(this._doAction, this)); // this handles clicks on the widget
            this.widget.on('mousedown', false);

            if (this.component && this.component.hasClass('btn')) {
                this.component.toggleClass('active');
            }
            this._place();
            this.widget.show();
            if (this.input !== undefined && this._options.focusOnShow && !this.input.is(':focus')) {
                this.input.focus();
            }

            this._notifyEvent({
                type: DateTimePicker.Event.SHOW
            });
        }

        destroy() {
            this.hide();
            //todo doc off?
            this._element.removeData(DateTimePicker.DATA_KEY);
            this._element.removeData('date');
        }

        disable() {
            this.hide();
            if (this.component && this.component.hasClass('btn')) {
                this.component.addClass('disabled');
            }
            if (this.input !== undefined) {
                this.input.prop('disabled', true); //todo disable this/comp if input is null
            }
        }

        enable() {
            if (this.component && this.component.hasClass('btn')) {
                this.component.removeClass('disabled');
            }
            if (this.input !== undefined) {
                this.input.prop('disabled', false); //todo enable comp/this if input is null
            }
        }

        toolbarPlacement(toolbarPlacement) {
            if (arguments.length === 0) {
                return this._options.toolbarPlacement;
            }

            if (typeof toolbarPlacement !== 'string') {
                throw new TypeError('toolbarPlacement() expects a string parameter');
            }
            if (toolbarPlacements.indexOf(toolbarPlacement) === -1) {
                throw new TypeError(`toolbarPlacement() parameter must be one of (${toolbarPlacements.join(', ')}) value`);
            }
            this._options.toolbarPlacement = toolbarPlacement;

            if (this.widget) {
                this.hide();
                this.show();
            }
        }

        widgetPositioning(widgetPositioning) {
            if (arguments.length === 0) {
                return $.extend({}, this._options.widgetPositioning);
            }

            if ({}.toString.call(widgetPositioning) !== '[object Object]') {
                throw new TypeError('widgetPositioning() expects an object variable');
            }
            if (widgetPositioning.horizontal) {
                if (typeof widgetPositioning.horizontal !== 'string') {
                    throw new TypeError('widgetPositioning() horizontal variable must be a string');
                }
                widgetPositioning.horizontal = widgetPositioning.horizontal.toLowerCase();
                if (horizontalModes.indexOf(widgetPositioning.horizontal) === -1) {
                    throw new TypeError(`widgetPositioning() expects horizontal parameter to be one of (${horizontalModes.join(', ')})`);
                }
                this._options.widgetPositioning.horizontal = widgetPositioning.horizontal;
            }
            if (widgetPositioning.vertical) {
                if (typeof widgetPositioning.vertical !== 'string') {
                    throw new TypeError('widgetPositioning() vertical variable must be a string');
                }
                widgetPositioning.vertical = widgetPositioning.vertical.toLowerCase();
                if (verticalModes.indexOf(widgetPositioning.vertical) === -1) {
                    throw new TypeError(`widgetPositioning() expects vertical parameter to be one of (${verticalModes.join(', ')})`);
                }
                this._options.widgetPositioning.vertical = widgetPositioning.vertical;
            }
            this._update();
        }

        widgetParent(widgetParent) {
            if (arguments.length === 0) {
                return this._options.widgetParent;
            }

            if (typeof widgetParent === 'string') {
                widgetParent = $(widgetParent);
            }

            if (widgetParent !== null && typeof widgetParent !== 'string' && !(widgetParent instanceof $)) {
                throw new TypeError('widgetParent() expects a string or a jQuery object parameter');
            }

            this._options.widgetParent = widgetParent;
            if (this.widget) {
                this.hide();
                this.show();
            }
        }

        setMultiDate(multiDateArray) {
            var dateFormat = this._options.format;
            this.clear();
            for (let index = 0; index < multiDateArray.length; index++) {
                let date = moment(multiDateArray[index], dateFormat);
                this._setValue(date, index);
            }
        }

        //static
        static _jQueryHandleThis(me, option, argument) {
            let data = $(me).data(DateTimePicker.DATA_KEY);
            if (typeof option === 'object') {
                $.extend({}, DateTimePicker.Default, option);
            }

            if (!data) {
                data = new TempusDominusBootstrap4($(me), option);
                $(me).data(DateTimePicker.DATA_KEY, data);
            }

            if (typeof option === 'string') {
                if (data[option] === undefined) {
                    throw new Error(`No method named "${option}"`);
                }
                if (argument === undefined) {
                    return data[option]();
                }
                else {
                    if (option === 'date') {
                        data.isDateUpdateThroughDateOptionFromClientCode = true;
                    }
                    const ret = data[option](argument);
                    data.isDateUpdateThroughDateOptionFromClientCode = false;
                    return ret;
                }
            }
        }


        static _jQueryInterface(option, argument) {
            if (this.length === 1) {
                return TempusDominusBootstrap4._jQueryHandleThis(this[0], option, argument);
            }
            return this.each(function () {
                TempusDominusBootstrap4._jQueryHandleThis(this, option, argument);
            });
        }
    }

    /**
    * ------------------------------------------------------------------------
    * jQuery
    * ------------------------------------------------------------------------
    */
    $(document).on(DateTimePicker.Event.CLICK_DATA_API, DateTimePicker.Selector.DATA_TOGGLE, function () {
        const $originalTarget = $(this), $target = getSelectorFromElement($originalTarget), config = $target.data(DateTimePicker.DATA_KEY);
        if ($target.length === 0) {
            return;
        }
        if (config._options.allowInputToggle && $originalTarget.is('input[data-toggle="datetimepicker"]')) {
          return;
        }
        TempusDominusBootstrap4._jQueryInterface.call($target, 'toggle');
    }).on(DateTimePicker.Event.CHANGE, `.${DateTimePicker.ClassName.INPUT}`, function (event) {
        const $target = getSelectorFromElement($(this));
        if ($target.length === 0 || event.isInit) {
            return;
        }
        TempusDominusBootstrap4._jQueryInterface.call($target, '_change', event);
    }).on(DateTimePicker.Event.BLUR, `.${DateTimePicker.ClassName.INPUT}`, function (event) {
        const $target = getSelectorFromElement($(this)), config = $target.data(DateTimePicker.DATA_KEY);
        if ($target.length === 0) {
            return;
        }
        if (config._options.debug || window.debug) {
            return;
        }
        TempusDominusBootstrap4._jQueryInterface.call($target, 'hide', event);
    }).on(DateTimePicker.Event.KEYDOWN, `.${DateTimePicker.ClassName.INPUT}`, function (event) {
        const $target = getSelectorFromElement($(this));
        if ($target.length === 0) {
            return;
        }
        TempusDominusBootstrap4._jQueryInterface.call($target, '_keydown', event);
    }).on(DateTimePicker.Event.KEYUP, `.${DateTimePicker.ClassName.INPUT}`, function (event) {
        const $target = getSelectorFromElement($(this));
        if ($target.length === 0) {
            return;
        }
        TempusDominusBootstrap4._jQueryInterface.call($target, '_keyup', event);
    }).on(DateTimePicker.Event.FOCUS, `.${DateTimePicker.ClassName.INPUT}`, function (event) {
        const $target = getSelectorFromElement($(this)), config = $target.data(DateTimePicker.DATA_KEY);
        if ($target.length === 0) {
            return;
        }
        if (!config._options.allowInputToggle) {
            return;
        }
        TempusDominusBootstrap4._jQueryInterface.call($target, 'show', event);
    });

    $.fn[DateTimePicker.NAME] = TempusDominusBootstrap4._jQueryInterface;
    $.fn[DateTimePicker.NAME].Constructor = TempusDominusBootstrap4;
    $.fn[DateTimePicker.NAME].noConflict = function () {
        $.fn[DateTimePicker.NAME] = JQUERY_NO_CONFLICT;
        return TempusDominusBootstrap4._jQueryInterface;
    };

    return TempusDominusBootstrap4;
})(jQuery);
