class PragmaPager extends HTMLElement {
    get activePagerButtons() {
        return this.allPagerButtons.filter(pagerButton => pagerButton.selected === 'true');
    }

    get allPagerButtons() {
        return Array.from(this.querySelectorAll('pager-button'));
    }

    get allEnabledPagerButtons() {
        return this.allPagerButtons.filter(pagerButton => pagerButton.getAttribute('aria-disabled') !== 'true');
    }

    get allLinkedPages() {
        return this.allPagerButtons.map(pagerButton => { return document.getElementById(pagerButton.controls); });
    }

    get behaviour() {
        return this.getAttribute('behaviour');
    }

    set behaviour(value) {
        if (this.behaviour === value) {
            return;
        }
        this.setAttribute('behaviour', value);
    }

    get currentActivePagerIndex() {
        return this.allEnabledPagerButtons.findIndex(pragmaButton => pragmaButton.getAttribute('aria-selected') === 'true');
    }

    get firstPagerButton() {
        return this.allEnabledPagerButtons[0];
    }

    get keyCodeToPagerButtonMap() {
        return new Map([
            [window.keyCodes.end, this.lastPagerButton],
            [window.keyCodes.home, this.firstPagerButton],
            //NOTE JN: Horizontal/Default orientation
            [window.keyCodes.left, this.previousPagerButton],
            [window.keyCodes.right, this.nextPagerButton],
            //NOTE JN: Vertical orientation
            [window.keyCodes.up, this.previousPagerButton],
            [window.keyCodes.down, this.nextPagerButton]
        ]);
    }

    get lastPagerButton() {
        return this.allEnabledPagerButtons[this.allEnabledPagerButtons.length - 1];
    }

    get pagerOrientation() {
        return this.getAttribute('aria-orientation');
    }

    get previousPagerButton() {
        let previousIndex = this.currentActivePagerIndex - 1;
        return this.allEnabledPagerButtons[(previousIndex + this.allEnabledPagerButtons.length) % this.allEnabledPagerButtons.length];
    }

    get nextPagerButton() {
        const nextIndex = this.currentActivePagerIndex + 1;
        return this.allEnabledPagerButtons[nextIndex % this.allEnabledPagerButtons.length];
    }

    constructor() {
        super();
    }

    click(event) {
        if (event.target.getAttribute('role') != 'tab') {
            return;
        }
        event.preventDefault();
        const pagerButton = event.target;
        this.handleBehaviour(pagerButton);
    }

    connectedCallback() {
        this.initTemplate();
        this.clickHandler = this.click.bind(this);
        this.keyDownHandler = this.keyDown.bind(this);
        this.addEventListener('click', this.clickHandler);
        this.addEventListener('keydown', this.keyDownHandler);
    }

    disconnectedCallback() {
        this.behaviour = null;
        this.removeEventListener('keydown', this.keyDownHandler);
        this.removeEventListener('click', this.clickHandler);
        this.clickHandler = null;
        this.keyDownHandler = null;
    }

    getPageByControl(control) {
        return this.allLinkedPages.find(page => page.id === control);
    }

    //NOTE JN: if Navigation is driven programmatically then navigation should be done out of context of the pager in a navigator class
    handleBehaviour(pagerButton) {
        // TODO JN: for single page ensure nothing is done when
        if (pagerButton == undefined) {
            return;
        }
        if (this.behaviour === 'single-page')
        {
            this.resetState();
        }

        this.selectPagerButton(pagerButton);
        this.showPage(pagerButton.controls, pagerButton.id);
    }

    initTemplate() {
        this.setPagerDefaultAttributes();
        this.setPagerButtonDefaultAttributes();
        this.setPageDefaultAttributes();
        this.linkPages();
    }

    keyDown(event) {
        if (event.target.getAttribute('role') != 'tab' || event.altKey) {
            return;
        }
        if (this.pagerOrientation == 'vertical' && (event.keyCode === 37 || event.keyCode === 39)) {
            return;
        }
        if (this.pagerOrientation != 'vertical' && (event.keyCode === 38 || event.keyCode === 40)) {
            return;
        }
        event.preventDefault();
        const pagerButton = this.keyCodeToPagerButtonMap.get(event.keyCode);
        this.handleBehaviour(pagerButton);
    }

    linkPages() {
        for(const pagerButton of this.allPagerButtons) {
            const page = this.getPageByControl(pagerButton.controls);
            if (page == undefined) {
                continue;
            }
            page.setAttribute('aria-labelledby', pagerButton.id);
        }
        const defaultPagerButton = this.activePagerButtons.length > 0 ? this.activePagerButtons[0] : this.firstPagerButton;
        this.handleBehaviour(defaultPagerButton);
    }

    resetState() {
        for(const pagerButton of this.activePagerButtons) {
            pagerButton.selected = 'false';
        }

        const activePages = this.allLinkedPages.filter(page => page.hidden == false);
        for(const page of activePages) {
            this.setPageVisibility(page, false);
        }
    }

    selectPagerButton(pagerButton) {
        pagerButton.selected = 'true';
        pagerButton.focus();
    }

    setPagerDefaultAttributes() {
        if (this.behaviour == undefined) {
            this.behaviour = 'single-page';
        }
        if (this.hasAttribute('role') === false) {
            this.setAttribute('role', 'tablist');
        }
    }

    setPagerButtonDefaultAttributes() {
        this.allPagerButtons.forEach((pagerButton, index) => {
            pagerButton.id = `pb-${index}_${pagerButton.controls}`;
            pagerButton.role = 'tab';
            if (pagerButton.hasAttribute('aria-selected') === false) {
                pagerButton.selected = 'false';
            }
        });
    }

    setPageDefaultAttributes() {
        for(const page of this.allLinkedPages) {
            if (page.hasAttribute('role') === false) {
                page.setAttribute('role', 'tabpanel');
            }
        }
    }

    setPageVisibility(page, isVisible) {
        page.hidden = !isVisible;
        page.setAttribute('aria-hidden', page.hidden);
    }

    showPage(controls, labelledby) {
        const page = this.getPageByControl(controls);
        if (page == undefined) {
            return;
        }
        if (page.getAttribute('aria-labelledby') !== labelledby) {
            return;
        }
        this.setPageVisibility(page, true);
        //TODO JN: When focus is on a tab in a tablist with either horizontal or vertical orientation:
        //Space or Enter: Activates the tab if it was not activated automatically on focus.
    }
}

customElements.define('pragma-pager', PragmaPager);

class PagerButton extends HTMLElement {
    get controls() {
        return this.getAttribute('aria-controls');
    }

    set controls(value) {
        if (this.controls === value) {
            return;
        }
        this.setAttribute('aria-controls', value);
    }

    //TODO JN: Remove getter and setter for properties that will not change (controls, roles...)
    get role() {
        return this.getAttribute('role');
    }

    set role(value) {
        if (this.role === value) {
            return;
        }
        this.setAttribute('role', value);
    }

    get selected() {
        return this.getAttribute('aria-selected');
    }

    set selected(value) {
        if (this.selected === value) {
            return;
        }
        this.setAttribute('aria-selected', value);
        //TODO JN: tabindex rethink - trigger this change by attribute change callback
        this.tabIndex = this.selected === 'true' ? 0 : -1;
    }

    get tabIndex() {
        return this.getAttribute('tabindex');
    }

    set tabIndex(value) {
        if (this.tabIndex === value) {
            return;
        }
        this.setAttribute('tabindex', value);
    }

    constructor() {
        super();
    }

    disconnectedCallback() {
        this.controls = null;
        this.role = null;
        this.selected = null;
        this.tabIndex = null;
    }
}

if (window.templates == undefined) {
    window.templates = new Map();
}

customElements.define('pager-button', PagerButton);
