class Meta {
    constructor() {
        this.document = document;
    }
    /**
     * Create and render in the dom the meta tags
     */
    create(metas) {
        this.setMetas(metas);
    }
    /**
     * Return the meta tags in HTML|String format, so you can manipulate.
     * Note: this does not add it to the Dom.
     */
    html(metas) {
        const metasForHtml = [];
        metas.forEach((meta) => metasForHtml.push(this.createNode(meta)));
        return metasForHtml;
    }
    setMetas(metas) {
        const metasForRender = [];
        metas.forEach((meta) => {
            const hasMeta = this.hasMeta(meta);
            if (hasMeta) {
                for (let key in meta) {
                    hasMeta.setAttribute(key, meta[key]);
                }
            }
            else {
                metasForRender.push(this.createNode(meta));
            }
        });
        if (metasForRender) {
            const lastMetaNode = this.document.querySelector('head').getElementsByTagName('meta');
            const last = lastMetaNode && lastMetaNode[lastMetaNode.length - 1] || this.document.querySelector('head title');
            metasForRender.forEach((element, index) => {
                const refElement = index === 0 ? last : metasForRender[index - 1];
                refElement.parentNode.insertBefore(element, refElement.nextSibling);
            });
        }
    }
    hasMeta(meta) {
        if (meta.hasOwnProperty('name')) {
            const metaElement = this.document.querySelector('head meta[name="' + meta.name + '"]');
            if (metaElement) {
                return metaElement;
            }
        }
        if (meta.hasOwnProperty('property')) {
            const metaElement = this.document.querySelector('head meta[property="' + meta.property + '"]');
            if (metaElement) {
                return metaElement;
            }
        }
        if (meta.hasOwnProperty('itemprop')) {
            const metaElement = this.document.querySelector('head meta[itemprop="' + meta.itemprop + '"]');
            if (metaElement) {
                return metaElement;
            }
        }
        if (meta.hasOwnProperty('http-equiv')) {
            const metaElement = this.document.querySelector('head meta[itemprop="' + meta['http-equiv'] + '"]');
            if (metaElement) {
                return metaElement;
            }
        }
        return null;
    }
    createNode(meta) {
        const metaElement = this.document.createElement('meta');
        for (let key in meta) {
            metaElement.setAttribute(key, meta[key]);
        }
        return metaElement;
    }
}

class Link {
    constructor() {
        this.document = document;
    }
    /**
     * Create and render in the dom the link tags
     */
    create(links) {
        this.setLinks(links);
    }
    /**
     * Return the link tags in HTML|String format, so you can manipulate.
     * Note: this does not add it to the Dom.
     */
    html(links) {
        const metasForHtml = [];
        links.forEach((link) => metasForHtml.push(Link.createNode(link)));
        return metasForHtml;
    }
    setLinks(links) {
        const linksForRender = [];
        links.forEach((link) => linksForRender.push(Link.createNode(link)));
        if (linksForRender) {
            const lastMetaNode = this.document.querySelector('head').getElementsByTagName('link');
            const last = lastMetaNode && lastMetaNode[lastMetaNode.length - 1] || this.document.querySelector('head title');
            linksForRender.forEach((element, index) => {
                const refElement = index === 0 ? last : linksForRender[index - 1];
                refElement.parentNode.insertBefore(element, refElement.nextSibling);
            });
        }
    }
    static createNode(link) {
        const linkElement = document.createElement('link');
        for (let key in link) {
            linkElement.setAttribute(key, link[key]);
        }
        return linkElement;
    }
}

class Script {
    constructor() {
        this.document = document;
    }
    /**
     * Create and render in the dom the link tags
     */
    create(scripts) {
        this.setScripts(scripts);
    }
    /**
     * Return the link tags in HTML|String format, so you can manipulate.
     * Note: this does not add it to the Dom.
     */
    html(scripts) {
        const scriptsForHtml = [];
        scripts.forEach((script) => {
            scriptsForHtml.push(Script.createNode(script));
        });
        return scriptsForHtml;
    }
    setScripts(scripts) {
        const scriptsForRender = [];
        scripts.forEach((script) => {
            scriptsForRender.push(Script.createNode(script));
        });
        if (scriptsForRender) {
            const lastMetaNode = this.document.querySelector('head').getElementsByTagName('script');
            const last = lastMetaNode && lastMetaNode[lastMetaNode.length - 1] || this.document.querySelector('head title');
            scriptsForRender.forEach((element, index) => {
                const refElement = index === 0 ? last : scriptsForRender[index - 1];
                refElement.parentNode.insertBefore(element, refElement.nextSibling);
            });
        }
    }
    static createNode(script) {
        const scriptElement = document.createElement('script');
        if (script.code) {
            scriptElement.innerHTML = script.code;
            delete script.code;
        }
        for (let key in script) {
            scriptElement.setAttribute(key, script[key]);
        }
        return scriptElement;
    }
}

class Style {
    constructor() {
        this.document = document;
    }
    /**
     * Create and render in the dom the style tags
     */
    create(styles) {
        this.setStyles(styles);
    }
    /**
     * Return the style tags in HTML|String format, so you can manipulate.
     * Note: this does not add it to the Dom.
     */
    html(styles) {
        const stylesForHtml = [];
        styles.forEach((style) => {
            stylesForHtml.push(Style.createNode(style));
        });
        return stylesForHtml;
    }
    setStyles(styles) {
        const stylesForRender = [];
        styles.forEach((style) => {
            stylesForRender.push(Style.createNode(style));
        });
        if (stylesForRender) {
            const lastMetaNode = this.document.querySelector('head').getElementsByTagName('style');
            const last = lastMetaNode && lastMetaNode[lastMetaNode.length - 1];
            if (last) {
                stylesForRender.forEach((element, index) => {
                    const refElement = index === 0 ? last : stylesForRender[index - 1];
                    refElement.parentNode.insertBefore(element, refElement.nextSibling);
                });
            }
            else {
                document.querySelector('head').append(...stylesForRender);
            }
        }
    }
    static createNode(style) {
        const styleElement = document.createElement('style');
        styleElement.setAttribute('type', 'text/css');
        styleElement.innerHTML = style;
        return styleElement;
    }
}

class Html {
    constructor() {
        this.document = document;
    }
    /**
     * Set html attributes in the dom
     */
    create(htmlAtt) {
        this.setHtmlAtt(htmlAtt);
    }
    /**
     * Return the html attributes in HTML|String format, so you can manipulate.
     * Note: this does not add it to the Dom.
     */
    html(htmlAtt) {
        const htmlElement = document.createElement('html');
        for (let key in htmlAtt) {
            htmlElement.setAttribute(key, htmlAtt[key]);
        }
        return htmlElement;
    }
    setHtmlAtt(htmlAtt) {
        const html = this.document.querySelector('html');
        for (let key in htmlAtt) {
            html.setAttribute(key, htmlAtt[key]);
        }
    }
}

class BaseHead {
    constructor(data) {
        this.document = document;
        this.data = data;
    }
    /**
     * Create and render in the dom all meta tags
     */
    async createHead() {
        if (this.data.html) {
            const html = new Html();
            await html.create(this.data.html);
        }
        if (this.data.title) {
            await this.setTitle(this.data.title);
        }
        if (this.data.baseHref) {
            await this.setBaseHref(this.data.baseHref);
        }
        if (this.data.charset) {
            await this.setCharset(this.data.charset);
        }
        if (this.data.metas) {
            const meta = new Meta();
            await meta.create(this.data.metas);
        }
        if (this.data.links) {
            const link = new Link();
            link.create(this.data.links);
        }
        if (this.data.scripts) {
            const script = new Script();
            script.create(this.data.scripts);
        }
        if (this.data.styles) {
            const style = new Style();
            style.create(this.data.styles);
        }
    }
    /**
     * Return all head tags in HTML|String format, so you can manipulate.
     * Note: this does not add it to the Dom.
     */
    html() {
        const head = [];
        if (this.data.html) {
            const html = new Html();
            const htmlElement = html.html(this.data.html);
            head.push(htmlElement);
        }
        if (this.data.charset) {
            head.push(this.getHtmlCharset(this.data.charset));
        }
        if (this.data.baseHref) {
            head.push(this.getBaseHref(this.data.baseHref));
        }
        if (this.data.title) {
            const title = document.createElement('title');
            title.text = this.data.title;
            head.push(title);
        }
        if (this.data.metas) {
            const meta = new Meta();
            head.push(meta.html(this.data.metas));
        }
        if (this.data.links) {
            const link = new Link();
            head.push(link.html(this.data.links));
        }
        if (this.data.scripts) {
            const script = new Script();
            head.push(script.html(this.data.scripts));
        }
        if (this.data.styles) {
            const style = new Style();
            head.push(style.html(this.data.styles));
        }
        return head.map((element) => {
            if (element instanceof Array) {
                return element.map((elementFromArray) => elementFromArray.outerHTML).join('\n');
            }
            return element.outerHTML;
        }).join('\n');
    }
    setTitle(title) {
        const titleElement = this.document.querySelector('head title');
        if (titleElement) {
            titleElement.text = title;
        }
        else {
            const newTitleElement = this.document.createElement('title');
            newTitleElement.text = title;
            this.document.querySelector('head').prepend(newTitleElement);
        }
    }
    setCharset(charset) {
        const charsetMeta = this.document.querySelector('head meta[charset]');
        if (charsetMeta) {
            charsetMeta.setAttribute('charset', charset);
        }
        else {
            const meta = this.document.createElement('meta');
            meta.setAttribute('charset', charset);
            this.document.querySelector('head').prepend(meta);
        }
    }
    getHtmlCharset(charset) {
        const charsetElement = this.document.createElement('meta');
        charsetElement.setAttribute('charset', charset);
        return charsetElement;
    }
    setBaseHref(baseHref) {
        const baseHrefElement = this.document.querySelector('head base');
        if (baseHrefElement) {
            baseHrefElement.setAttribute('href', baseHref);
        }
        else {
            const newBaseHrefElement = this.document.createElement('base');
            newBaseHrefElement.setAttribute('href', baseHref);
            this.document.querySelector('head').prepend(newBaseHrefElement);
        }
    }
    getBaseHref(baseHref) {
        const baseHrefElement = this.document.createElement('base');
        baseHrefElement.setAttribute('href', baseHref);
        return baseHrefElement;
    }
}

export { BaseHead as B };
