"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const is_1 = __importDefault(require("./is"));
function isWritable(property) {
    return property.writable;
}
let writableProperties = Symbol("ionscript.Data.writableProperties");
function getWritableProperties(object) {
    let wprops = object.constructor[writableProperties];
    if (wprops == null) {
        // initialize this objects constructor since this is the first time.
        Object.defineProperties(object.constructor.prototype, Object.fromEntries(object.constructor.prototype.properties));
        // store the writable properties
        wprops = object.constructor[writableProperties]
            = new Map(Array.from(object.constructor.prototype.properties).filter(([name, property]) => property.writable));
    }
    return wprops;
}
class Data {
    constructor(properties) {
        if (properties == null) {
            throw new Error("properties arguments is required");
        }
        for (let [name, prop] of getWritableProperties(this)) {
            let value = properties[name];
            // only check at debug time?
            if (prop.type) {
                if (value === undefined) {
                    if (prop.value === undefined) {
                        throw new Error(`${name} property is required`);
                    }
                    value = prop.value;
                }
                else if (!is_1.default(value, prop.type)) {
                    throw new Error(`${name} property is not a valid ${prop.type.name}: ${value}`);
                }
            }
            this[name] = value;
        }
    }
    toJSON() {
        return { "": this.constructor.name, ...this };
    }
    static coerce(properties) {
        return new this(properties);
    }
}
exports.default = Data;
