import ComponentFactory, {ComponentConfig} from "../ComponentFactory";
import LayoutItem from "./LayoutItem";
import {addHeaderToFactory} from "./Implementations/Header";
import {addMessageDisplayToFactory} from "./Implementations/MessageDisplay";
import Component from "../Component";
import {addHtmlToFactory} from "./Implementations/Html";
import {addSubmitButtonToFactory} from "./Implementations/SubmitButton";
import {addTextToFactory} from "./Implementations/Text";

export default class LayoutItemFactory {
    protected registeredConstructors: {[type:string]: (config:LayoutItemConfig, params: Partial<LayoutItem>) => LayoutItem|null|undefined} = {};

    constructor() {}

    public createLayoutItem(config: LayoutItemConfig, params: Partial<Component>): LayoutItem|null {
        if (typeof config.impl.type !== "string" ) throw new Error("Type is not a string");
        if (!(typeof this.registeredConstructors[config.impl.type] === "function")) {
            console.warn(`LayoutItem ${config.impl.type} is not registered`);
            return null;
        }
        
        const layoutItemParam: Partial<LayoutItem> = params as Partial<LayoutItem>;

        const layoutItem = this.registeredConstructors[config.impl.type](config, layoutItemParam);
        if (layoutItem == null) {
            console.warn(`LayoutItem ${config.impl.type} could not be created`);
            return null;
        }
        return layoutItem;
    }

    public registerConstructor(type: string, constructor: (config:LayoutItemConfig, params: Partial<LayoutItem>) => LayoutItem|null|undefined): this {
        this.registeredConstructors[type] = constructor;
        return this;
    }

    public setDefaults(): this{
        addHeaderToFactory(this);
        addMessageDisplayToFactory(this);
        addHtmlToFactory(this);
        addSubmitButtonToFactory(this);
        addTextToFactory(this);
        return this;
    }

    public registerOnComponentFactory(factory:ComponentFactory): void{
        factory.registerConstructor("LayoutItem", (config, params) => {
            if (componentConfigIsLayoutItem(config)){
                return this.createLayoutItem(config, params);
            }
        });
    }
}

export function componentConfigIsLayoutItem(config: ComponentConfig): config is LayoutItemConfig{
    return config.type === "LayoutItem";
}

export interface LayoutItemConfig extends ComponentConfig{
    impl: LayoutItemImplConfig;
}

export interface LayoutItemImplConfig{
    type: string;
}