import Loader from "./Loader.js";

/** 
 *  @class InfiniteScroll
 *  
 *  @description
 *  This object has several resposabilities (maybe too much, anyway...):
 * 
 *  1. Keeps track of the viewport position in page 
 *  2. Triggers the ajax requests for more contents
 *  3. Changes the url in the address bar according to the content shown in viewport
 *  
 */
export class InfiniteScroll {

    /**    
     * @param { Array }    relateds  - array of objects 
     * @param { string }   container - element selector - where to append new contents 
     * @param { string }   content   - element selector - content to append, the first is the main content alreay loaded with the page
     * @param { int }      maxLoads  - @default 2
     * @param { string }   loadMoreSelector - the selector that trigger new loadind when gets into viewport @default '#loadmore'
     * @param { boolean }  change_addressbar - if true changes the addressbar @default true
     * @param { function } onMaxLoads - callback to execute when loadings are finished @default null
     *                                    
     */
    constructor(relateds = [], container = 'body', content = 'article', preloader = '#wait-loading', maxLoads = 2, loadMoreSelector = '#loadmore', change_addressbar = true, onMaxLoads = null) {

        this.container = container;  // sets the container's selector
        this.containerElement = document.querySelector(this.container);
        this.content = content;    // sets the content's selector
        this.relateds = relateds;
        this.loadedContents = []; // already loaded pages
        this.loadingContent = { ...relateds.shift(), count: 0 };
        this.currentReading = {intersectionRatio: 0,target:null}

        // set it to false when there's no need to change the url in the address bar
        this.change_addressbar = change_addressbar;

        // element to show while loading
        this.preloader = document.querySelector(preloader);

        // element that triggers the next content loading
        this.loadMore = document.querySelector(loadMoreSelector);

        /// imposta il primo elemento come caricato e gli associa i suoi dati
        this.setLoadedContent(this.containerElement.querySelector(this.content));

        // initializes the current url ...
        this.current_url = this.loadingContent.url;
        // `${window.location.protocol}//${window.location.host}${window.location.pathname}`;

        // ... and the content index in the contents array
        this.current_content = 0;

        /// imposta le callback per i caricamenti
        this.loader = new Loader(this.complete.bind(this), null, this.loading.bind(this));

        /// abilita il trigger per il caricamento del uovo contenuto
        this.enableTrigger(this.loadMore, this.loadNext);

        this.loadCallbackStack = [];
        this.viewportCallbackStack = [];

    }



    /**
     * If the user reached the bottom of the content
     * loadNext tries to load the next one
     */
    loadNext() {

        // does nothing if loading        
        if (this.loader.loading) return;

        // ... and if there nothing to load
        if (this.relateds.length == 0) return;

        // take the first of the line        
        this.loadingContent = { ...this.relateds.shift(), count: this.loadedContents.length };

        // needed to add variable like "is_ajax_request"
        this.loader.load(`${this.loadingContent.url}?is_ajax_request`);
    }

    loading() {
        this.preloader.style.display = "block";
    }

    complete(payload) {
        this.preloader.style.display = "none";

        let temp = document.createElement('div');
        temp.insertAdjacentHTML("beforeend", payload);

        /// prende il tag che risulta come contenuto
        let content = temp.querySelector(this.content);

        /// imposta nel contenuto i data attribute 
        this.setLoadedContent(content);

        /// sposta tutti i tag che trova 
        while (temp.children.length) {

            /// add the content to the page
            this.containerElement.appendChild(temp.removeChild(temp.firstChild));
        }

        /// actions to start for each load
        this.loadCallbackStack.forEach(callback => callback.call());

        // ... and if there is nothing to load, action onMaxLoads
        if (this.relateds.length == 0) {

            if (typeof this.onMaxLoads !== 'undefined') this.onMaxLoads();
        }

    }

    onMaxLoads() {
        //LATEST LOAD CALLBACK
        this.observer.unobserve(this.loadMore)
    }

    setLoadedContent(content) {

        /// add some data to the content
        content.dataset.url = this.loadingContent.url;
        content.dataset.count = this.loadingContent.count;
        content.dataset.title = this.loadingContent.title;

        /// add the content to the loaded ones
        this.loadedContents.push(this.loadingContent);

        /// add some actions triggering when content is in viewport        
        this.enableTrigger(content, this.onElementShow)
    }


    onElementShow(args) {
        this.current_content = args.count;
        this.current_url = args.url;
        this.current_title = args.title;
        this.current_tracking_url = '';

        if (this.current_url != window.location.href) {
            this.setAddressBar();
        }

        /// actions to start when the page appears into  viewport
        if (args.count > 0) {
            this.viewportCallbackStack.forEach(callback => callback.call());
        }
    }


    // changes the url of the current page in the address bar
    setAddressBar(title, url) {

        // changes the values for URL in the address bar
        window.history.replaceState(null, this.current_title, this.current_url);

        // updates the title
        document.title = this.current_title;
    }


    /** 
     * set the trigger to sense the loadMore in Viewport
     * and execute the callback
     */
    enableTrigger(trigger, callback) {

        trigger.callback = callback.bind(this);
        const options = {
            root:document.querySelector('main-container'),
            threshold: [0,.2,.3,.4,.5,.6,.7,.8,.9,1]
        };
        /// setup the observer
        this.observer = new IntersectionObserver((entries, observer) => {

            entries.forEach(entry => {
                if (this.currentReading.intersectionRatio < entry.intersectionRatio) {
                    entry.target.callback(entry.target.dataset); /// call the callback

                    (entry.target.id != 'loadmore') ?  (this.currentReading = entry) : null;
                }

                if(this.currentReading.target == entry.target){
                    this.currentReading = entry
                }
            });
        }, options);

        /// start observing
        this.observer.observe(trigger);
    }

}