const MamiSoundInfo = function(name, isReadOnly, title, sources) {
    name = (name || '').toString();
    isReadOnly = !!isReadOnly;
    title = (title || ('Nameless Sound (' + name + ')')).toString();
    sources = sources || {};

    return {
        getName: () => name,
        isReadOnly: () => isReadOnly,
        getTitle: () => title,
        getSources: () => sources,
    };
};

const MamiSoundLibrary = function(soundMgr) {
    if(typeof soundMgr !== 'object' && typeof soundMgr.loadSource !== 'function')
        throw 'soundMgr must be an instance of MamiSoundManager';

    const sounds = new Map;

    const soundFormats = ['opus', 'caf', 'ogg', 'mp3', 'wav'];
    const registerSound = (soundInfo, readOnly) => {
        if(Array.isArray(soundInfo)) {
            for(const info of soundInfo)
                registerSound(info, readOnly);
            return;
        }

        if(typeof soundInfo !== 'object')
            throw 'soundInfo must be an object';
        if(typeof soundInfo.name !== 'string')
            throw 'soundInfo must contain a name field';
        if(typeof soundInfo.sources !== 'object')
            throw 'soundInfo must contain a sources field';
        if(sounds.has(soundInfo.name))
            throw 'a sound with that name has already been registered';

        let sources = {},
            sCount = 0;
        for(const fmt of soundFormats) {
            if(typeof soundInfo.sources[fmt] !== 'string')
                continue;
            sources[fmt] = soundInfo.sources[fmt];
            ++sCount;
        }

        if(sCount < 1)
            throw 'soundInfo does not contain any valid sources';

        soundInfo = new MamiSoundInfo(soundInfo.name, readOnly, soundInfo.title, sources);
        sounds.set(soundInfo.getName(), soundInfo);

        return soundInfo;
    };

    const getSound = name => {
        if(!sounds.has(name))
            throw 'No sound with this name has been registered.';
        return sounds.get(name);
    };

    const getSoundSources = name => getSound(name).getSources();
    const loadSoundSource = async name => await soundMgr.loadSource(getSoundSources(name));

    return {
        register: registerSound,
        unregister: name => {
            sounds.delete(name);
        },
        clear: () => {
            sounds.clear();
        },
        info: name => getSound(name),
        names: () => Array.from(sounds.keys()),
        has: name => sounds.has(name),
        get: getSound,
        getSources: getSoundSources,
        loadBuffer: async name => await soundMgr.loadBuffer(getSoundSources(name)),
        loadSource: loadSoundSource,
        play: async (name, volume, rate) => {
            if(typeof name !== 'string')
                return;

            const source = await loadSoundSource(name);

            if(typeof volume === 'number')
                source.setVolume(volume);

            if(typeof rate === 'number')
                source.setRate(rate);

            await source.play();
        },
    };
};
