import { h } from '@stencil/core';
import page from "page"; // see: https://visionmedia.github.io/page.js/
export class Router {
    constructor() {
        this._url = '/';
        this.routes = [];
        this.hash = ''; //  '#';
    }
    componentDidLoad() {
        this.el.style.display = "none";
        this.postRender();
        // init the landing page
        if (this.level == 0) {
            page(window.location.pathname);
        }
    }
    componentDidUpdate() {
        this.postRender();
    }
    postRender() {
        let routeComponents = Array.from(this.el.getElementsByTagName('ema-route'));
        routeComponents.forEach((route) => {
            this.routes.push({
                'component': route.component,
                'component-props': route.componentProps,
                'url': route.url,
                'exact': route.exact,
                'action': route.action,
                'action-params': route.actionParams,
            });
        });
        this.routes.forEach((route) => {
            // Defines a route mapping path to the given callback(s). Each callback is invoked with two arguments, context and next. Much like Express invoking next will call the next registered callback with the given path.
            // Under certain conditions, links will be disregarded and will not be dispatched, such as:
            // Links that are not of the same origin
            // Links with the download attribute
            // Links with the target attribute
            // Links with the rel="external" attribute
            page(route.url, (pageContext) => {
                if (route.component) {
                    pageContext.state.component = route.component;
                }
                this._pageContext = pageContext;
                this.state = {
                    context: pageContext,
                    route: route,
                    routes: this.routes,
                    component: route.component,
                };
                this.activateRoute();
                this.changed.emit(this.state);
            });
            // page('/', user.list)
            // page('/user/:id/edit', user.load, user.edit)
            // page('*', notfound)
        });
    }
    activateRoute() {
        let breadcrumb = [];
        let component;
        let currentPathRoot;
        let pageClass = 'page';
        let pageContext = this.state.context;
        let params = pageContext.params;
        let route = this.state.route;
        let routeComponent;
        let url = this._url = pageContext.state.path;
        let urlParsed = this.urlParamsToUrl(route.url);
        if (!route.component) {
            console.error('No component defined for url: ' + url);
            return;
        }
        else {
            component = route.component;
        }
        this.deleteRouterClasses();
        pageContext.state.path.split('/').forEach((p) => {
            if (p !== '') {
                breadcrumb.push(p);
            }
        });
        if (pageContext.state.component) {
            routeComponent = pageContext.state.component;
        }
        breadcrumb.forEach((level) => {
            pageClass += ('-' + level);
            this.removeClass(document.querySelector('body'), pageClass);
        });
        if (breadcrumb.length) {
            currentPathRoot = breadcrumb[0];
        }
        // todo: fixme:
        if (component && component === routeComponent && url === urlParsed) {
            // todo: fixme:
            if (route['exact'] && route['url'] !== url) {
                // console.log(url);
                // console.log(route['url']);
                // return;
            }
            // remove the previous component
            if (this._compDiv) {
                if (this.targetSelector) {
                    if (document.querySelector(this.targetSelector)) {
                        document.querySelector(this.targetSelector).removeChild(this._compDiv);
                    }
                }
                else {
                    this.el.parentNode.removeChild(this._compDiv);
                }
            }
            // initializa the current component
            this._compDiv = document.createElement(component);
            // set the component props
            if (route['component-props']) {
                // pass props to the component
                let cprops = JSON.parse(route['component-props']);
                Object.keys(cprops).forEach((k) => {
                    let v = cprops[k];
                    if (typeof v !== 'string') {
                        v = JSON.stringify(v);
                    }
                    this._compDiv.setAttribute(k, v);
                });
            }
            // render component
            if (this.targetSelector) {
                if (document.querySelector(this.targetSelector)) {
                    document.querySelector(this.targetSelector).appendChild(this._compDiv);
                }
            }
            else {
                this.el.parentNode.insertBefore(this._compDiv, this.el.nextSibling);
            }
            // add route classes to body tag
            this.addClass(document.querySelector('body'), component);
        }
        this.addClass(document.querySelector('body'), pageClass);
    }
    deleteRouterClasses() {
        // remove body classes
        let bodyElement = document.querySelector('body');
        this.state.routes.forEach((r) => {
            if (r.component) {
                this.removeClass(bodyElement, r.component);
            }
        });
        bodyElement.classList.forEach((classId) => {
            if (classId.startsWith('page-')) {
                this.removeClass(bodyElement, classId);
            }
        });
    }
    urlParamsToUrl(url) {
        let parsedUrl = url;
        let params = this.state.context.params;
        let parts = url.split('/');
        let hasParams = false;
        parts.forEach((part, i) => {
            let variable;
            let value;
            if (part.startsWith(':')) {
                hasParams = true;
                variable = part.split(':')[1];
                value = params[variable];
                parsedUrl = parsedUrl.replace(part, value);
            }
        });
        if (hasParams) {
            return parsedUrl;
        }
        else {
            return url;
        }
    }
    async url(url) {
        if (url) {
            page(this.hash + url);
        }
        else {
            return this._url;
        }
    }
    async page() {
        return page;
    }
    async getState() {
        return this.state;
    }
    addClass(el, className) {
        if (el.classList)
            el.classList.add(className);
        else
            el.className += ' ' + className;
    }
    removeClass(el, className) {
        if (el.classList)
            el.classList.remove(className);
        else
            el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
    }
    render() {
        return (h("div", null));
    }
    static get is() { return "ema-router"; }
    static get properties() { return {
        "dataJson": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "data-json",
            "reflect": false
        },
        "hash": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "hash",
            "reflect": false,
            "defaultValue": "''"
        },
        "level": {
            "type": "number",
            "mutable": false,
            "complexType": {
                "original": "number",
                "resolved": "number",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "level",
            "reflect": false
        },
        "targetSelector": {
            "type": "string",
            "mutable": false,
            "complexType": {
                "original": "string",
                "resolved": "string",
                "references": {}
            },
            "required": false,
            "optional": false,
            "docs": {
                "tags": [],
                "text": ""
            },
            "attribute": "target-selector",
            "reflect": false
        }
    }; }
    static get states() { return {
        "state": {},
        "routes": {},
        "routerLinks": {}
    }; }
    static get events() { return [{
            "method": "changed",
            "name": "changed",
            "bubbles": true,
            "cancelable": true,
            "composed": true,
            "docs": {
                "tags": [],
                "text": ""
            },
            "complexType": {
                "original": "any",
                "resolved": "any",
                "references": {}
            }
        }]; }
    static get methods() { return {
        "url": {
            "complexType": {
                "signature": "(url: any) => Promise<string>",
                "parameters": [{
                        "tags": [],
                        "text": ""
                    }],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<string>"
            },
            "docs": {
                "text": "",
                "tags": []
            }
        },
        "page": {
            "complexType": {
                "signature": "() => Promise<any>",
                "parameters": [],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<any>"
            },
            "docs": {
                "text": "",
                "tags": []
            }
        },
        "getState": {
            "complexType": {
                "signature": "() => Promise<any>",
                "parameters": [],
                "references": {
                    "Promise": {
                        "location": "global"
                    }
                },
                "return": "Promise<any>"
            },
            "docs": {
                "text": "",
                "tags": []
            }
        }
    }; }
    static get elementRef() { return "el"; }
}
