import { nanoid } from '../../nanoid.js';
import { IAmfArrayNodeTypes, IAmfObjectNodeTypes, IAmfScalarNodeTypes } from '../AmfTypes.js';
import { AmfNamespace } from '../definitions/Namespace.js';
/**
 * A class that manipulates the AMF's DataNode node.
 */
export class AmfDataNode {
    id = nanoid();
    /**
     * AMF domain types.
     * This is set in the constructor
     */
    types;
    /**
     * The name of this data node.
     */
    name;
    /**
     * Set when the object node type
     */
    properties;
    /**
     * Scalar type value. Always a string.
     */
    value;
    /**
     * Scalar type value data type.
     */
    dataType;
    /**
     * Set when the type is array.
     */
    members;
    customDomainProperties = [];
    static scalar(value, type) {
        const result = new AmfDataNode('scalar');
        result.value = value;
        result.dataType = type;
        return result;
    }
    constructor(init) {
        if (typeof init === 'string') {
            if (init === 'scalar') {
                this.types = IAmfScalarNodeTypes;
            }
            else if (init === 'array') {
                this.types = IAmfArrayNodeTypes;
            }
            else if (init === 'object') {
                this.types = IAmfObjectNodeTypes;
            }
            else {
                throw new Error(`Invalid data node init option: ${init}`);
            }
            return;
        }
        if (!init) {
            throw new Error(`Expected an initialization option.`);
        }
        const union = init;
        const { id, types, customDomainProperties } = union;
        this.id = id;
        this.types = types;
        this.customDomainProperties = customDomainProperties;
        if (types.includes(AmfNamespace.aml.vocabularies.data.Scalar)) {
            const typed = union;
            this.value = typed.value;
            this.dataType = typed.dataType;
        }
        else if (types.includes(AmfNamespace.aml.vocabularies.data.Object)) {
            const typed = union;
            const props = {};
            this.properties = props;
            Object.keys(typed.properties).forEach((key) => {
                const instance = new AmfDataNode(typed.properties[key]);
                props[key] = instance;
            });
        }
        else if (types.includes(AmfNamespace.aml.vocabularies.data.Array)) {
            const typed = union;
            const members = [];
            this.members = members;
            if (Array.isArray(typed.members)) {
                typed.members.forEach((member) => {
                    const instance = new AmfDataNode(member);
                    members.push(instance);
                });
            }
        }
    }
    toJSON() {
        const { id, customDomainProperties, types, name } = this;
        const result = {
            id,
            types,
            customDomainProperties,
        };
        if (name) {
            result.name = name;
        }
        if (types.includes(AmfNamespace.aml.vocabularies.data.Scalar)) {
            const typed = result;
            typed.dataType = this.dataType;
            typed.value = this.value;
        }
        else if (types.includes(AmfNamespace.aml.vocabularies.data.Object)) {
            const typed = result;
            typed.properties = {};
            const props = this.properties;
            if (props) {
                Object.keys(props).forEach((key) => {
                    typed.properties[key] = props[key].toJSON();
                });
            }
        }
        else if (types.includes(AmfNamespace.aml.vocabularies.data.Array)) {
            const typed = result;
            typed.members = [];
            if (this.members) {
                this.members.forEach((member) => {
                    typed.members.push(member.toJSON());
                });
            }
        }
        return result;
    }
    /**
     * Adds a member to the list of members.
     * It throws when this is not an array node.
     *
     * @param init The member to add.
     */
    addMember(init) {
        if (!this.types.includes(AmfNamespace.aml.vocabularies.data.Array)) {
            throw new Error(`Not an ArrayNode.`);
        }
        if (!this.members) {
            this.members = [];
        }
        this.members.push(init);
    }
    /**
     * Removes a member from the list of members.
     * It throws when this is not an array node.
     *
     * @param id The domain id of the member to remove.
     */
    removeMember(id) {
        if (!this.types.includes(AmfNamespace.aml.vocabularies.data.Array)) {
            throw new Error(`Not an ArrayNode.`);
        }
        if (!this.members) {
            return;
        }
        const index = this.members.findIndex((i) => i.id === id);
        if (index >= 0) {
            this.members.splice(index, 1);
        }
    }
    /**
     * Adds a property to the ObjectNode.
     * It throws when this is not an object node.
     *
     * @param name The name of the property.
     * @param value The instance of the DataNode to set.
     */
    addProperty(name, value) {
        if (!this.types.includes(AmfNamespace.aml.vocabularies.data.Object)) {
            throw new Error(`Not an ObjectNode.`);
        }
        if (!this.properties) {
            this.properties = {};
        }
        this.properties[name] = value;
    }
    /**
     * Deletes a property by name.
     *
     * @param name The name of the property to delete.
     */
    removeProperty(name) {
        if (!this.types.includes(AmfNamespace.aml.vocabularies.data.Object)) {
            throw new Error(`Not an ObjectNode.`);
        }
        if (!this.properties) {
            return;
        }
        // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
        delete this.properties[name];
    }
}
//# sourceMappingURL=AmfDataNode.js.map