import {IFactory} from '../interfaces/components/IFactory';
import {IFactory as IAppFactory} from '../interfaces/IFactory';
import {IAbstractObject} from '../interfaces/components/IAbstractObject';
import {IComposite} from '../interfaces/components/IComposite';
import {EInputTypes} from '../types/EInputTypes';
import {IModal} from '../interfaces/components/IModal';
import {Modal} from './Modal';
import {Composite} from './Composite';
import {ObjectForm} from './ObjectForm';
import {IObjectForm} from '../interfaces/components/IObjectForm';
import {String} from './String';
import {Text} from './Text';
import {Html} from './Html';
import {Color} from './Color';
import {Bool} from './Bool';
import {Select} from './Select';
import {ObjectsArray} from './ObjectsArray';
import {ArrayItem} from './ArrayItem';
import {IFactory as IFileInputFactory} from '../interfaces/components/file-input/IFactory';
import {Factory as FileInputFactory} from './file-input/Factory';
import {IFactory as ITreeSelectFactory} from '../interfaces/components/tree-select/IFactory';
import {Factory as TreeSelectFactory} from '../components/tree-select/Factory';
import {Helpers} from '../../admin/Helpers';

type TObjectForms = {
    [key:string]:IObjectForm;
}

export class Factory implements IFactory {
    
    protected conf:any;
    
    protected appFactory:IAppFactory;

    protected modal:IModal = null;

    protected objectForms:TObjectForms = {};

    protected fileInputFactory:IFileInputFactory = null;

    protected treeSelectFactory:ITreeSelectFactory = null;

    public init(conf:any): void
    {
        this.conf = {
            modal: null,
            composite: null,
            objectForm: null,
            fileInput: {
                factory: null,
            },
            treeSelect: {
                factory: null,
            },
            string: null,
            text: null,
            html: null,
            color: null,
            bool: null,
            select: null,
            objectsArray: null,
        };
        Helpers.objectReplaceRecursive(this.conf, conf);
    }

    public setAppFactory(factory:IAppFactory)
    {
        this.appFactory = factory;
    }

    public getAppFactory():IAppFactory
    {
        return this.appFactory;
    }

    public getModal():IModal
    {
        if(this.modal === null){
            this.modal = this.conf.modal || new Modal();
            this.modal.setObjCreator((type:EInputTypes)=>{
                return this.createInputField(type);
            });
            document.querySelector('body').append(this.modal.template);
            this.modal.eventsListen();
        }
        return this.modal;
    }

    public createInputField(type:EInputTypes):IAbstractObject
    {
        let objectBus = this.appFactory.getBusFactory().getObjectBus();
        if(type == EInputTypes.string){
            let input = this.conf.string || new String();
            return input;
        }
        if(type == EInputTypes.text){
            let input = this.conf.text || new Text();
            return input;
        }
        if(type == EInputTypes.html){
            let input = this.conf.html || new Html();
            return input;
        }
        if(type == EInputTypes.color){
            let input = this.conf.color || new Color();
            return input;
        }
        if(type == EInputTypes.bool){
            let input = this.conf.bool || new Bool();
            return input;
        }
        if(type == EInputTypes.select){
            let input = this.conf.select || new Select();
            return input;
        }
        if(type == EInputTypes.array){
            let input = this.conf.objectsArray || new ObjectsArray();
            input.setObjectBus(objectBus);
            input.setItemCreator((type:string)=>{
                let item = new ArrayItem();
                let objectBus = this.appFactory.getBusFactory().getObjectBus();
                item.setObjectBus(objectBus);
                return item;
            });
            return input;
        }
        if(type == EInputTypes.file){
            let input = this.getFileInputFactory().createFileInput();
            return input;
        }
        if(type == EInputTypes.image){
            let input = this.getFileInputFactory().createImageInput();
            return input;
        }
        if(type == EInputTypes.treeSelect){
            let input = this.getTreeSelectFactory().createTreeSelect();
            return input;
        }
        let input = this.createComposite();
        return input;
    }

    public createComposite():IComposite
    {
        let objectBus = this.appFactory.getBusFactory().getObjectBus();
        let composite = this.conf.composite || new Composite();
        composite.setObjectBus(objectBus);
        composite.setFieldCreator((type:EInputTypes)=>{
            return this.createInputField(type);
        });
        return composite;
    }

    public getObjectForm(key:string):IObjectForm
    {
        if(!this.objectForms[key]){
            let objectForm = this.conf.objectForm || new ObjectForm();
            objectForm.setKey(key);
            objectForm.setCompositeCreator((type:string)=>{
                return this.createComposite();
            });
            this.objectForms[key] = objectForm;
        }
        return this.objectForms[key];
    }

    public getFileInputFactory():IFileInputFactory
    {
        if(this.fileInputFactory === null){
            this.fileInputFactory = this.conf.fileInput.factory || new FileInputFactory();
            this.fileInputFactory.setComponentsFactory(this);
            this.fileInputFactory.init(this.conf.fileInput);
        }
        return this.fileInputFactory;
    }

    public getTreeSelectFactory(): ITreeSelectFactory
    {
        if(this.treeSelectFactory === null){
            this.treeSelectFactory = this.conf.treeSelect.factory || new TreeSelectFactory();
            this.treeSelectFactory.setComponentsFactory(this);
            this.treeSelectFactory.init(this.conf.treeSelect);
        }
        return this.treeSelectFactory;
    }

}