import { customAttribute } from '../decorator/exported';
import { IAttribute, IListener, IBindingContext } from '../interface/exported';
import { ViewController, View, AttributeController } from '../view/exported';
import { BindingEngine } from '../binding/exported';
import { DOM } from '../utils/exported';



/**
 * removes dom when expression is undefined/null''0
 *
 */
@customAttribute('if.bind')
export class IfAttribute implements IAttribute {
    public $element: HTMLElement;
    public $attribute: Attr;
    public $bindingContext: IBindingContext;
    public $controller: AttributeController;
    private value: string | null | undefined;
    private elementClone: Node;
    private anchor: Node;
    private added: boolean;
    private subscribeCall: IListener;
    private $view: ViewController;
    private templateElement: boolean;
    private childNodes: Node[];
    private $templateView: ViewController;


    /**
     * created
     *
     */
    public created() {

        // get current value;
        this.value = this.$attribute.value;

        // remove the if.bind attribute
        this.$element.attributes.removeNamedItem('if.bind');
        this.$view = this.$controller.getView();


        // clone node, we need this clone later when we add it
        this.elementClone = this.$element.cloneNode(true);
        this.templateElement = (this.elementClone as any).tagName === 'TEMPLATE' ? true : false;



        // hide it for now, we will remove it when we are attached to parent
        this.$element.style.display = 'None';

        // set variable for later
        this.added = false;


    }



    /**
     * attached
     *
     */
    public attached() {

        // create anchor so where know where to inject it
        // this needs to be after we attched it since we need parentNode to do this
        this.anchor = DOM.document.createComment('mf-if-bind');


        // add anchor
        this.$element.parentNode.insertBefore(this.anchor, this.$element);

        // remove main element
        this.remove();

        this.subscribeCall = {
            name: 'ifAttribute',
            value: this.value,
            call: (newValue: any) => {

                switch (true) {
                    case newValue && !this.added:
                        this.added = true;
                        this.add();
                        break;
                    case !newValue && this.added:
                        this.remove();
                        break;
                }
            }
        } as IListener;


        BindingEngine.subscribeClassProperty(this.$bindingContext, this.value, this.subscribeCall);
    }



    /**
     * detached
     *
     */
    public detached(): void {
        BindingEngine.unSubscribeClassProperty(this.$bindingContext, this.subscribeCall);
        this.remove();
        if (this.anchor) {
            this.anchor.parentNode.removeChild(this.anchor);
            this.anchor = null;
        }
    }



    /**
     * shows element/content
     *
     */
    public add() {
        // create clone
        const template = this.elementClone.cloneNode(true);
        if (this.templateElement) {
            // IE11
            if (!(<any>template).content) {
                (<any>template).content = DOM.document.createDocumentFragment();
                while (template.childNodes[0]) {
                    (<any>template).content.appendChild(template.childNodes[0]);
                }
            }
        }

        // set clone as current element
        this.$element = (template as any);
        let temp: any;
        this.$templateView = new ViewController(template, this.$view);

        if (this.templateElement) {
            temp = DOM.document.createElement('div');
            temp.appendChild((template as any).content);
            this.childNodes = [];
            let anchor = DOM.document.createComment('ifbind-template-anchor-start');

            this.childNodes.push(anchor);
            for (let i = 0, ii = temp.childNodes.length; i < ii; i++) {
                this.childNodes.push(temp.childNodes[i]);
            }
            anchor = DOM.document.createComment('ifbind-template-anchor-end');

            this.childNodes.push(anchor);
        }

        // get attach event for elements
        let controllers = View.parseTemplate(this.templateElement ? temp : this.$element, this.$bindingContext, this.$templateView);
        if (this.templateElement) {
            this.anchor.parentNode.insertBefore(this.childNodes[0], this.anchor);
            for (let i = 1, ii = this.childNodes.length; i < ii; i++) {
                this.anchor.parentNode.insertBefore(this.childNodes[i], this.childNodes[i - 1].nextSibling);
            }
        } else {
            this.anchor.parentNode.insertBefore(this.$element, this.anchor);
        }

        // if this even used after last rebuild?
        controllers.forEach((x) => {
            if (x.attached) {
                x.attached();
            }
        });
        controllers = null;


    }



    /**
     * removes content
     *
     */
    private remove() {
        if (this.added) {
            this.added = false;
            this.$templateView.clearView();
            this.$templateView = null;
        }
        if (this.$element) {
            if (this.templateElement && this.childNodes) {
                for (let i = 0, ii = this.childNodes.length; i < ii; i++) {
                    if (this.childNodes[i].parentNode === this.anchor.parentNode) {
                        this.anchor.parentNode.removeChild(this.childNodes[i]);
                    }
                }
            } else {
                if (this.anchor.parentNode === this.$element.parentNode) {
                    this.anchor.parentNode.removeChild(this.$element);
                }
            }
            this.$element = null;
        }
    }



}
