'use strict';

const router = require('../router.js');
const views = require('../util/views.js');

const holderTemplate = views.getTemplate('endless-pager');
const pageTemplate = views.getTemplate('endless-pager-page');

function isScrolledIntoView(element) {
    let top = 0;
    do {
        top += element.offsetTop || 0;
        element = element.offsetParent;
    } while(element);
    return (
        (top >= window.scrollY) &&
        (top <= window.scrollY + window.innerHeight));
}

class EndlessPageView {
    constructor(ctx) {
        this._hostNode = document.getElementById('content-holder');
        views.replaceContent(this._hostNode, holderTemplate());
    }

    run(ctx) {
        this._destroy();

        this._active = true;
        this._runningRequests = 0;
        this._initialPageLoad = true;

        this.clearMessages();
        views.emptyContent(this._pagesHolderNode);

        this.minOffsetShown = null;
        this.maxOffsetShown = null;
        this.totalRecords = null;
        this.currentOffset = 0;
        this.defaultLimit = parseInt(ctx.parameters.limit || ctx.defaultLimit);

        const initialOffset = parseInt(ctx.parameters.offset || 0);
        this._loadPage(ctx, initialOffset, this.defaultLimit, true)
            .then(pageNode => {
                if (initialOffset !== 0) {
                    pageNode.scrollIntoView();
                }
            });

        this._timeout = window.setInterval(() => {
            window.requestAnimationFrame(() => {
                this._probePageLoad(ctx);
                this._syncUrl(ctx);
            });
        }, 250);

        views.monitorNodeRemoval(this._pagesHolderNode, () => this._destroy());
    }

    get pageHeaderHolderNode() {
        return this._hostNode.querySelector('.page-header-holder');
    }

    get topPageGuardNode() {
        return this._hostNode.querySelector('.page-guard.top');
    }

    get bottomPageGuardNode() {
        return this._hostNode.querySelector('.page-guard.bottom');
    }

    get _pagesHolderNode() {
        return this._hostNode.querySelector('.pages-holder');
    }

    _destroy() {
        window.clearInterval(this._timeout);
        this._active = false;
    }

    _syncUrl(ctx) {
        let topPageNode = null;
        let element = document.elementFromPoint(
            window.innerWidth / 2,
            window.innerHeight / 2);
        while (element.parentNode !== null) {
            if (element.classList.contains('page')) {
                topPageNode = element;
                break;
            }
            element = element.parentNode;
        }
        if (!topPageNode) {
            return;
        }
        let topOffset = parseInt(topPageNode.getAttribute('data-offset'));
        let topLimit = parseInt(topPageNode.getAttribute('data-limit'));
        if (topOffset !== this.currentOffset) {
            router.replace(
                ctx.getClientUrlForPage(
                    topOffset,
                    topLimit === ctx.defaultLimit ? null : topLimit),
                ctx.state,
                false);
            this.currentOffset = topOffset;
        }
    }

    _probePageLoad(ctx) {
        if (!this._active || this._runningRequests) {
            return;
        }

        if (this.totalRecords === null) {
            return;
        }

        if (this.minOffsetShown > 0 &&
                isScrolledIntoView(this.topPageGuardNode)) {
            this._loadPage(
                ctx,
                this.minOffsetShown - this.defaultLimit,
                this.defaultLimit,
                false);
        }

        if (this.maxOffsetShown < this.totalRecords &&
                isScrolledIntoView(this.bottomPageGuardNode)) {
            this._loadPage(
                ctx,
                this.maxOffsetShown,
                this.defaultLimit,
                true);
        }
    }

    _loadPage(ctx, offset, limit, append) {
        this._runningRequests++;
        return new Promise((resolve, reject) => {
            ctx.requestPage(offset, limit).then(response => {
                if (!this._active) {
                    this._runningRequests--;
                    return Promise.reject();
                }
                window.requestAnimationFrame(() => {
                    let pageNode = this._renderPage(ctx, append, response);
                    this._runningRequests--;
                    resolve(pageNode);
                });
            }, error => {
                this.showError(error.message);
                this._runningRequests--;
                reject();
            });
        });
    }

    _renderPage(ctx, append, response) {
        let pageNode = null;

        if (response.total) {
            pageNode = pageTemplate({
                totalPages: Math.ceil(response.total / response.limit),
                page: Math.ceil(
                    (response.offset + response.limit) / response.limit),
            });
            pageNode.setAttribute('data-offset', response.offset);
            pageNode.setAttribute('data-limit', response.limit);

            ctx.pageRenderer({
                parameters: ctx.parameters,
                response: response,
                hostNode: pageNode.querySelector('.page-content-holder'),
            });

            this.totalRecords = response.total;

            if (response.offset < this.minOffsetShown ||
                    this.minOffsetShown === null) {
                this.minOffsetShown = response.offset;
            }
            if (response.offset + response.results.length
                    > this.maxOffsetShown ||
                    this.maxOffsetShown === null) {
                this.maxOffsetShown =
                    response.offset + response.results.length;
            }
            response.results.addEventListener('remove', e => {
                this.maxOffsetShown--;
                this.totalRecords--;
            });

            if (append) {
                this._pagesHolderNode.appendChild(pageNode);
                if (this._initialPageLoad && response.offset > 0) {
                    window.scroll(0, pageNode.getBoundingClientRect().top);
                }
            } else {
                this._pagesHolderNode.prependChild(pageNode);

                window.scroll(
                    window.scrollX,
                    window.scrollY + pageNode.offsetHeight);
            }
        } else if (!response.results.length) {
            this.showInfo('No data to show');
        }

        this._initialPageLoad = false;
        return pageNode;
    }

    clearMessages() {
        views.clearMessages(this._hostNode);
    }

    showSuccess(message) {
        views.showSuccess(this._hostNode, message);
    }

    showError(message) {
        views.showError(this._hostNode, message);
    }

    showInfo(message) {
        views.showInfo(this._hostNode, message);
    }
}

module.exports = EndlessPageView;
