"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;