
// Experimental

const flux = {};

flux.ModelFactory = class {

    async match(context) {
        const identifier = context.identifier;
        const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
        const stream = context.stream;
        if (stream && extension === 'bson') {
            return context.set('flux.bson');
        }
        return null;
    }

    async open(context) {
        let root = null;
        try {
            root = await context.read('bson');
        } catch (error) {
            const message = error && error.message ? error.message : error.toString();
            throw new flux.Error(`File format is not Flux BSON (${message.replace(/\.$/, '')}).`);
        }
        /* const metadata = */ context.metadata('flux-metadata.json');
        const backref = (obj, root) => {
            if (Array.isArray(obj)) {
                for (let i = 0; i < obj.length; i++) {
                    obj[i] = backref(obj[i], root);
                }
            } else if (obj === Object(obj)) {
                if (obj.tag === 'backref' && obj.ref) {
                    if (!root._backrefs[obj.ref - 1n]) {
                        throw new flux.Error(`Invalid backref '${obj.ref}'.`);
                    }
                    obj = root._backrefs[obj.ref - 1n];
                }
                for (const key of Object.keys(obj)) {
                    if (obj !== root || key !== '_backrefs') {
                        obj[key] = backref(obj[key], root);
                    }
                }
            }
            return obj;
        };
        const obj = backref(root, root);
        const model = obj.model;
        if (!model) {
            throw new flux.Error('File does not contain Flux model.');
        }
        throw new flux.Error("File contains unsupported Flux data.");
    }
};

flux.Model = class {

    constructor(/* root */) {
        this.format = 'Flux';
        this.modules = [];
    }
};

flux.Error = class extends Error {

    constructor(message) {
        super(message);
        this.name = 'Flux Error';
    }
};

export const ModelFactory = flux.ModelFactory;
