import {IComposite} from '../interfaces/components/IComposite';
import {IAbstractObject} from '../interfaces/components/IAbstractObject';
import {IObjectBus} from '../interfaces/bus/IObjectBus';
import {TComposite} from '../types/TComposite';
import {EInputTypes} from '../types/EInputTypes';

type TFields = {
    [field:string]:IAbstractObject;
}

export class Composite implements IComposite {

    protected getHtml():string
    {
        return `
            <div>
                <div class="js-body"></div>
                <div class="object-field" style="display:flex; gap: 5px;">
                    <button class="btn btn-info btn-sm js-save-button" style="display:none">Save</button>
                    <button class="btn btn-secondary btn-sm js-back-button" style="display:none">Back</button>
                </div>
            </div>
        `
    }

    protected getCollapsedHtml():string
    {
        return `
            <div class="object-field">
                <button class="btn btn-info js-collapsed-btn">Obj</button>
            </div>
        `
    }

    protected _template:HTMLElement;

    protected _collapsedTemplate:HTMLElement;

    protected collapsedBtn:HTMLElement;

    protected body:HTMLElement;

    protected backButton:HTMLElement;

    protected saveButton:HTMLElement;
    
    protected data:TComposite;

    protected objectBus:IObjectBus;

    protected fields:TFields = {};

    protected resolve:any;

    protected reject:any;

    protected fieldCreator:(type:EInputTypes)=>IAbstractObject;

    protected formKey:string;

    public setFormKey(key:string)
    {
        this.formKey = key;
    }

    public constructor()
    {
        let div = document.createElement('div');
        div.innerHTML = this.getHtml().trim();
        this._template = <HTMLElement>div.firstChild;
        this.body = this._template.querySelector('.js-body');
        this.backButton = this._template.querySelector('.js-back-button');
        this.saveButton = this._template.querySelector('.js-save-button');
        
        div = document.createElement('div');
        div.innerHTML = this.getCollapsedHtml().trim();
        this._collapsedTemplate = <HTMLElement>div.firstChild;
        this.collapsedBtn = this._collapsedTemplate.querySelector('.js-collapsed-btn');
    }

    public get template():HTMLElement
    {
        return this._template;
    }

    public get collapsedTemplate():HTMLElement
    {
        return this._collapsedTemplate;
    }

    public setFieldCreator(callback:(type:EInputTypes)=>IAbstractObject)
    {
        this.fieldCreator = callback;
    }

    public setObjectBus(bus:IObjectBus)
    {
        this.objectBus = bus;
    }

    public loadData(data:TComposite):void
    {
        this.data = data;
        this.collapsedBtn.innerText = data.description;
        for(let name in data.fields){
            let field = this.fieldCreator(data.fields[name].type);
            field.setFormKey(this.formKey);
            field.loadData(data.fields[name]);
            this.fields[name] = field;
        }
        this.showErrors();
    }

    public getData():TComposite
    {
        return this.data;
    }
    
    public build():Promise<TComposite>
    {
        return new Promise<TComposite>((resolve:any, reject:any)=>{
            this.resolve = resolve;
            this.reject = reject;
            this.draw();
        });
    }

    public draw(): void
    {
        this.body.innerHTML = '';
        for(let name in this.fields){
            if(this.fields[name].serialize().composite){
                this.body.append((<IComposite>this.fields[name]).collapsedTemplate);
                continue;
            }
            this.body.append(this.fields[name].template);
        }
    }

    public showSaveButton()
    {
        this.saveButton.style.display = 'block';
    }

    public showBackButton()
    {
        this.backButton.style.display = 'block';
    }

    public showErrors():void
    {
        if(this.data.errors.length){
            this._collapsedTemplate.classList.remove('btn-info');
            this._collapsedTemplate.classList.add('btn-danger');
        }

        for(let name in this.fields){
            this.fields[name].showErrors();
        }
    }

    public clearErrors():void
    {

    }

    public serialize():TComposite
    {
        return this.data;
    }

    protected deepClone(obj:any)
    {
        if(Array.isArray(obj)){
            let result:any[] = [];
            for(let item of obj){
                if(typeof item === 'object'){
                    result.push(this.deepClone(item));
                }else{
                    result.push(item);
                }
            }
            return result;
        }else{
            let result:any = {};
            for(let key in obj){
                if(typeof obj[key] === 'object' && obj[key] !== null && key !== 'container'){
                    result[key] = this.deepClone(obj[key]);
                }else{
                    result[key] = obj[key];
                }
            }
            return result;
        }
    }

    public eventsListen()
    {
        for(let name in this.fields){
            this.fields[name].eventsListen();
        }
        this._collapsedTemplate.onclick = (e:Event)=>{
            e.preventDefault();
            this.objectBus.renderForm(this.deepClone(this.data), this.formKey)
                .then((updatedObj:TComposite)=>{
                    this.data.fields = updatedObj.fields;
                    this.objectBus.back(this.formKey);
                });
        };
        
        this.backButton.onclick = (e:Event)=>{
            e.preventDefault();
            this.objectBus.back(this.formKey);
        };
        
        this.saveButton.onclick = (e:Event)=>{
            e.preventDefault();
            this.resolve(this.data);
        };
    }

}