import ValidatorFactory, {ValidatorConfig} from "./Validators/ValidatorFactory";
import DataType from "./DataType";
import BoolType from "./Types/BoolType";
import NumberType from "./Types/NumberType";
import ObjectType from "./Types/ObjectType";
import StringType from "./Types/StringType";
import ArrayType from "./Types/ArrayType";
import Input from "../Components/Input/Input";

export default class DataFactory {
    protected registeredConstructors: {[type:string]: (config:DataTypeConfig, input:Input<any>) => DataType<any>} = {};

    constructor(
        protected validatorFactory: ValidatorFactory
    ) {}

    public createDataNode(config: DataTypeConfig, input:Input<any>): DataType<any>|null {
        if (typeof config.type !== "string" ) throw new Error("Type is not a string");
        if (typeof this.registeredConstructors[config.type] === "function") {
            let dataType = this.registeredConstructors[config.type](config, input);

            for (let validatorConfig of config.validators){
                validatorConfig.idPrefix = input.idPrefix ?? "";
                const validator = this.validatorFactory.createValidator(validatorConfig);
                if (validator === null) continue;
                dataType.addValidator(validator);
            }

            return dataType;
        }
        return null;
    }

    public registerConstructor(type: string, constructor: (config:DataTypeConfig, input:Input<any>) => DataType<any>): this {
        this.registeredConstructors[type] = constructor;
        return this;
    }

    public setDefaults(): this{
        this.registerConstructor("bool", () => new BoolType());
        this.registerConstructor("int", () => new NumberType());
        this.registerConstructor("float", () => new NumberType());
        this.registerConstructor("object", () => new ObjectType());
        this.registerConstructor("string", () => new StringType());
        this.registerConstructor("array", (config, input) => new ArrayType(
            this.createDataNode(
                config.subType ?? {type: "bool", validators: []}, input
            ) ?? new BoolType()
        ));



        return this;
    }
}

export interface DataTypeConfig {
    type: string;
    validators: ValidatorConfig[];
    subType?: DataTypeConfig;
}