import Ajv from "ajv";
function createSchemaFromType(type) {
    if (type === 'object') {
        return { type: 'object', properties: {} };
    }
    else if (type === 'array') {
        return { type: 'array', items: {} };
    }
    else {
        return { type };
    }
}
export class Schema {
    schema;
    properties = {};
    _validator;
    constructor(schema) {
        this.schema = schema || { type: 'object', properties: {} };
        this.load();
    }
    get validator() {
        if (!this._validator) {
            const ajv = new Ajv({ allErrors: true, strict: false });
            this._validator = ajv.compile(this.schema);
        }
        return this._validator;
    }
    load() {
        if (this.schema.properties) {
            const properties = this.schema.properties;
            Object.keys(properties).forEach(name => {
                this.loadProperty(name, properties[name]);
            });
        }
    }
    hasProperties() {
        return Object.keys(this.properties).length > 0;
    }
    get title() {
        return this.schema.title || this.schema.name;
    }
    get description() {
        return this.schema.description;
    }
    loadProperty(name, propertySchema) {
        let property;
        if (propertySchema.type === 'array') {
            property = new ArrayPropertySchema(this, name, propertySchema);
        }
        else {
            property = new PropertySchema(this, name, propertySchema);
        }
        this.properties[name] = property;
        return property;
    }
    get type() {
        return this.schema.type;
    }
    validate(value) {
        if (!this.validator(value)) {
            return this.validator.errors || [];
        }
        else {
            return null;
        }
    }
    addProperty(name, typeOrSchema, isRequired = false) {
        if (this.schema.type !== 'object') {
            this.schema.type = 'object';
        }
        if (!this.schema.properties) {
            this.schema.properties = {};
        }
        const propertySchema = typeof typeOrSchema === 'string' ? createSchemaFromType(typeOrSchema) : typeOrSchema;
        const property = this.loadProperty(name, propertySchema);
        this.schema.properties[property.name] = property.schema;
        if (isRequired) {
            property.isRequired = true;
        }
        return property;
    }
    removeProperty(name) {
        if (this.schema.properties) {
            delete this.schema.properties[name];
            if (Array.isArray(this.schema.required)) {
                this.schema.required = this.schema.required.filter(x => x !== name);
            }
        }
        delete this.properties[name];
    }
    getProperty(name) {
        return this.properties[name];
    }
    get editor() {
        return this.schema.editor;
    }
}
export class PropertySchema extends Schema {
    parent;
    name;
    constructor(parent, name, schema) {
        super(schema);
        this.parent = parent;
        this.name = name;
        if (schema.type === 'array') {
            throw new Error("Array property must be instantiated using ArrayPropertySchema");
        }
        if (schema.type === 'object' && !schema.properties) {
            schema.properties = {};
        }
    }
    get isMulti() {
        return false;
    }
    get isReadOnly() {
        return !!this.schema.readOnly;
    }
    set isReadOnly(value) {
        this.schema.readOnly = value;
    }
    get defaultValue() {
        return this.schema.default;
    }
    set defaultValue(value) {
        this.schema.default = value;
    }
    get enum() {
        return this.schema.enum;
    }
    set enum(values) {
        this.schema.enum = values;
    }
    get isRequired() {
        const required = this.parent.schema.required;
        return required ? required.includes(this.name) : false;
    }
    set isRequired(value) {
        let required = Array.isArray(this.parent.schema.required) ? this.parent.schema.required : [];
        if (value) {
            required = required.concat(this.name);
        }
        else {
            required = required.filter(x => x !== this.name);
        }
        this.parent.schema.required = required;
    }
    get type() {
        return this.schema.type;
    }
    set type(value) {
        if (this.schema.type !== value) {
            this.schema.type = value;
            if (value !== 'object') { // remove sub properties
                this.properties = {};
                this.schema.properties = undefined;
            }
        }
    }
    remove() {
        this.parent.removeProperty(this.name);
    }
    get isBoolean() {
        return this.type === 'boolean';
    }
    get isString() {
        return this.type === 'string';
    }
    get isNumber() {
        return this.type === 'number' || this.type === 'integer';
    }
    get isObject() {
        return this.type === 'object';
    }
}
function getArrayElementType(schema) {
    if (schema.type !== 'array') {
        throw new Error('Expecting an array schema');
    }
    if (!schema.items) {
        schema.items = {};
    }
    else if (Array.isArray(schema.items)) {
        throw new Error('Tuple arrays are not supported');
    }
    return schema.items;
}
export class ArrayPropertySchema extends PropertySchema {
    arraySchema;
    constructor(parent, name, schema) {
        super(parent, name, getArrayElementType(schema));
        this.arraySchema = schema;
    }
    get isMulti() {
        return true;
    }
}
//# sourceMappingURL=schema.js.map