import Component from "./Components/Component";
import ComponentFactory, {ComponentConfig} from "./Components/ComponentFactory";
import Form from "./Components/Structure/Form/Form";
import DataFactory from "./Data/DataFactory";
import ValidatorFactory from "./Data/Validators/ValidatorFactory";
import ConditionFactory from "./Conditions/ConditionFactory";
import StructureFactory from "./Components/Structure/StructureFactory";
import LayoutItemFactory from "./Components/LayoutItem/LayoutItemFactory";
import InputFactory from "./Components/Input/InputFactory";
import "./FormBuilder.css";
import EventHandler from "./Utility/EventHandler";
import {ValidEvent} from "./Data/DataNode";


export default class FormBuilder {
    public registry: ComponentRegistry = {};
    public form: Form;
    public componentFactory: ComponentFactory;
    public dataFactory: DataFactory;
    public validatorFactory: ValidatorFactory;
    public conditionFactory: ConditionFactory;
    public structureFactory: StructureFactory;
    public layoutItemFactory: LayoutItemFactory;
    public inputFactory: InputFactory;

    public onCommit: EventHandler<null> = new EventHandler();
    public onClean: EventHandler<null> = new EventHandler();
    public onChange: EventHandler<null> = new EventHandler();
    public onValidChange: EventHandler<ValidEvent> = new EventHandler();
    public onComponentsCreated: EventHandler<null> = new EventHandler();

    constructor(config: ComponentConfig) {
        this.conditionFactory = new ConditionFactory(this).setDefaults();
        this.componentFactory = new ComponentFactory(this, this.conditionFactory);
        this.validatorFactory = new ValidatorFactory(this.conditionFactory).setDefaults();
        this.dataFactory = new DataFactory(this.validatorFactory).setDefaults();

        this.structureFactory = new StructureFactory(this.componentFactory).setDefaults();
        this.layoutItemFactory = new LayoutItemFactory().setDefaults();
        this.inputFactory = new InputFactory(this.dataFactory, this).setDefaults();

        this.structureFactory.registerOnComponentFactory(this.componentFactory);
        this.layoutItemFactory.registerOnComponentFactory(this.componentFactory);
        this.inputFactory.registerOnComponentFactory(this.componentFactory);

        const form: Component|null = this.componentFactory.createComponent(config);
        if (!(form instanceof Form)) throw new Error("invalid config of form");
        this.form = form;

        form.getDataNode().onChange.add(() => this.onChange.emit(null));
        form.getDataNode().onValidChange.add(event => this.onValidChange.emit(event));

        this.onComponentsCreated.emit(null);
        this.onComponentsCreated.clear();
    }

    public manualComponentsCreatedEvent(): this{
        this.onComponentsCreated.emit(null);
        this.onComponentsCreated.clear();
        return this;
    }

    public commit(){
        this.onCommit.emit(null);
    }

    public clean(){
        this.onClean.emit(null);
    }

    public runValidate(): this{
        this.form.getDataNode().validate(true);
        return this;
    }

    public get isValid(): boolean{
        return this.form.getDataNode().isValid;
    }

    public placeHtml(container: HTMLElement): this{
        this.form.placeHtml(container);
        return this;
    }

    public loadData(data: any): this{
        this.form.getDataNode().value = data;
        return this;
    }

    public getData(withDefaults: boolean = false): object | undefined{
        return withDefaults ? this.form.getDataNode().valueOrDefault : this.form.getDataNode().value;
    }
}

interface ComponentRegistry {
    [id: string]: Component;
}