(function ($, document, window, viewport) {
    'use strict';

    // PLUGIN CLASS
    // Table of Contents plugin.
    // ============
    const TocPluginClass = {

        contentSelector: '#content',
        listSelector: '.et-toc-list',
        $headings: null,
        context: null,
        readingTimeFactors: {
            slow: 150,
            average: 200,
            fast: 300
        },
        readingTimeSelector: '.et-toc-reading-time-estimate',
        filteredParentsSelectors: '.blog-latest-news, [data-slick], .et-slider, .et-content-boxes-wrapper, .et-news-items-slider, .et-usps-items, .et-hero-product-price, .et-link-groups-wrapper.et-link-groups-display-column, .et-masonry-grid-wrapper, .et-countdown-content-wrapper',

        /*
         * This function handles element class adding/removal in case the table of content has not found anything to list.
         */
        handleNoToc: function () {
            let targets = $('[data-no-toc]');
            targets.each((index, element) => {
                let $element = $(element);
                let noToc = $element.data('no-toc');
                if(noToc.remove && Array.isArray(noToc.remove) && noToc.remove.length) {
                    $element.removeClass(noToc.remove.join(" "));
                }
                if(noToc.add && Array.isArray(noToc.add) && noToc.add.length) {
                    $element.addClass(noToc.add.join(" "));
                }
            });
        },

        computeReadingTime: function() {
            let readingTimeSpeed = this.$el.data('tocReadingTime');
            if(!readingTimeSpeed || !readingTimeSpeed.length || !this.readingTimeFactors.hasOwnProperty(readingTimeSpeed)) {
                return;
            }
            /* We select all leaf nodes (:not(:has(*)) that are visible (:visible) to collect our text. */
            let content = $(this.contentSelector + ' *:not(:has(*),[class^="et-toc-"]):visible').text();
            let count = content.split(/\s+/).length;
            let readingTime = Math.ceil(count / this.readingTimeFactors[readingTimeSpeed]);
            this.$el.find(this.readingTimeSelector).text(readingTime);
        },

        makeAbsoluteUrl: function(id) {
            return location.protocol + '//' + location.host + location.pathname + (location.search ? location.search : "") + "#" + id;
        },

        /**
         * @this {PluginBase & TocPluginClass}
         * @param {HTMLElement} element
         * @param {Object} options
         */
        init: function (element, options) {
            if(this.$el.hasClass('et-toc-initialized')) {
                return;
            }
            this.$el.addClass('et-toc-initialized');
            let headingTypes = this.$el.data('tocHeadings');
            let $list = this.$el.find(this.listSelector);
            this.context = this.$el.data('tocContext');
            if (!headingTypes || !$list.length) {
                this.handleNoToc();
                return;
            }
            // Automatically add the class equivalents for HTML headings and build a single selector string
            let headingSelectors = headingTypes.concat(headingTypes.map((headingType) => `.${headingType}`)).join(',');
            this.$headings = $(this.contentSelector).find(headingSelectors);
            if (!this.$headings.length) {
                // No headings found on the page
                this.handleNoToc();
                return;
            }
            // Note: We start at 1 to skip the first heading which will be the page title itself
            let minHeadingTypeIndex = 6;
            let listHeadings = [];
            let generatedIndex = 0;
            for (let i = 1; i < this.$headings.length; i++) {
                let $currentHeading = $(this.$headings.get(i));
                for (let j = 0; j < headingTypes.length; j++) {
                    if ($currentHeading.is(`${headingTypes[j]}, .${headingTypes[j]}`)) {
                        // Ignore the heading in excluded parents
                        if($currentHeading.closest(this.filteredParentsSelectors).length || !$currentHeading.is(':visible')) {
                            continue;
                        }

                        let targetId = null;
                        let textContent = $currentHeading.text().trim();
                        if ($currentHeading.attr('id')) {
                            targetId = $currentHeading.attr('id');
                        } else {
                            targetId = textContent.toLowerCase().replaceAll(/([^a-z0-9-])/g, '-').replaceAll(/-{2,}/g, '').replace(/^-+/, '').replace(/-+$/, '') + '-' + (++generatedIndex);
                            $currentHeading.attr('id', targetId);
                        }
                        listHeadings.push({level: j, textContent: textContent, targetId: targetId});
                        if (j < minHeadingTypeIndex) {
                            minHeadingTypeIndex = j;
                        }
                        break;
                    }
                }
            }
            if (!listHeadings.length) {
                // Found nothing to display.
                this.handleNoToc();
                return;
            }
            for (let i = 0; i < listHeadings.length; i++) {
                $('<li/>').addClass('et-toc-entry-level-' + (listHeadings[i]['level'] - minHeadingTypeIndex)).append($('<a/>').addClass('et-toc-link-entry-link').attr('href', this.makeAbsoluteUrl(listHeadings[i]['targetId'])).text(listHeadings[i]['textContent'])).appendTo($list);
            }
            this.computeReadingTime();
        }
    };

    // REGISTER AND ADD THE PLUGIN
    // ===========================
    if (!window.easytemplate && !window.easytemplate.PluginManager) {
        throw Error('Cannot initialize Easytemplate Plugin. plugin-manager.js has to be loaded first!');
    }

    /** @type {PluginManager} manager */
    const manager = window.easytemplate.PluginManager;
    manager.addPlugin('[data-toc]', TocPluginClass, 'etTocPlugin');
})(jQuery, document, window, ResponsiveBootstrapToolkit);