import { IgniteProperty } from './ignite-html.js';
import { IgniteTemplate } from './ignite-template.js';

/**
 * The outline of a ignite element that extends the html element
 * and can be used to create custom components.
 */
class IgniteElement extends HTMLElement {
    constructor() {
        super();

        this.onDisconnected = null;
        this.template = null;
        this.elements = [];
        this.createProperties();
    }

    /**
     * Returns the properties for this ignite element.
     */
    get properties() {
        return {};
    }

    /**
     * Returns any CSS styling code for this ignite element.
     */
    get styles() {
        return null;
    }

    /**
     * Creates the getters/setters for properties in this
     * ignite element and initializes everything.
     */
    createProperties() {
        var props = this.properties;

        if (props != null) {
            var keys = Object.keys(props);
            for (var i = 0; i < keys.length; i++) {
                let prop = new IgniteProperty(props[keys[i]]);
                this[`_${keys[i]}`] = prop;

                ((propName) => {
                    Object.defineProperty(this, propName, {
                        get: function () {
                            if (IgniteRenderingContext.rendering == false) {
                                return this[`_${propName}`].value;
                            } else {
                                return this[`_${propName}`];
                            }
                        },

                        set: function (value) {
                            this[`_${propName}`].value = value;
                        }
                    });
                })(keys[i]);
            }
        }
    }

    /**
     * Resets the properties for this element back to their original default
     * value.
     */
    resetProperties() {
        var props = this.properties;

        if (props != null) {
            var keys = Object.keys(props);
            for (var i = 0; i < keys.length; i++) {
                this[keys[i]] = props[keys[i]];
            }
        }
    }

    /**
     * Setups this ignite element and constructs it's template when
     * this function is called by the DOM upon this element being created somewhere.
     */
    connectedCallback() {
        //See if a styling sheet has been created for this element if it's needed.
        if (this.styles !== null && this.styles !== "") {
            if (document.getElementsByClassName(`_${this.tagName}_styling_`).length == 0) {
                var styleEl = document.createElement("style");
                styleEl.classList.add(`_${this.tagName}_styling_`);
                styleEl.innerHTML = this.styles;
                document.body.prepend(styleEl);
            }
        }

        //If we don't already have a template, make sure we create one,
        //this can happen if this element was constructed in the DOM instead of within a template.
        if (!this.template) {
            this.template = new IgniteTemplate();
            this.template.element = this;
            this.template.tagName = this.tagName;
        }

        //Add any childNodes we have to the elements list within this
        this.childNodes.forEach((item) => this.elements.push(item));

        //Enter a rendering context so properties don't expose their values until we are done.
        IgniteRenderingContext.enter();

        //Make sure the render template is our template, if not, add it as a child.
        var renderTemplate = this.render();
        if (renderTemplate !== this.template && renderTemplate) {
            this.template.child(renderTemplate);
        } else if (!renderTemplate) {
            throw `RenderTemplate was null for template: ${this.tagName}. Is render() returning null or not returning a template?`;
        }

        //Construct our template.
        try {
            this.template.construct(this.parentElement);
        } catch (error) {
            console.error(error);
        }

        //Leave the rendering context.
        IgniteRenderingContext.leave();
    }

    /**
     * Cleanups this element and deconstructs everything when this element is removed from
     * the DOM.
     */
    disconnectedCallback() {
        //If we still have a reference to our template, deconstruct it.
        if (this.template) {
            this.template.deconstruct();
        }

        //If we have a onDisconnected callback, call it and then remove the reference.
        if (this.onDisconnected) {
            this.onDisconnected();
            this.onDisconnected = null;
        }
    }

    /**
     * Returns the template to be rendered for this element.
     */
    render() {
        return null;
    }
}

export {
    IgniteElement
};