#include settings.js
#include sound/sndlibrary.js

const MamiSoundPack = function(name, isReadOnly, title, events) {
    name = (name || '').toString();
    isReadOnly = !!isReadOnly;
    title = (title || '').toString();
    events = events || new Map;

    return {
        getName: function() {
            return name;
        },
        isReadOnly: function() {
            return isReadOnly;
        },
        getTitle: function() {
            return title;
        },
        setTitle: function(newTitle) {
            if(isReadOnly)
                throw 'Cannot edit read only sound pack.';
            title = (newTitle || '').toString();
        },
        getEventNames: function() {
            return Array.from(events.keys());
        },
        hasEventSound: function(eventName) {
            return events.has(eventName);
        },
        getEventSound: function(eventName) {
            if(!events.has(eventName))
                throw 'event not registered';
            return events.get(eventName);
        },
        setEventSound: function(eventName, soundName) {
            events.set(
                (eventName || '').toString(),
                (soundName || '').toString()
            );
        },
        removeEventSound: function(eventName) {
            events.delete(eventName);
        },
        clearEventSounds: function() {
            events.clear();
        },
        clone: function(newName, readOnly) {
            return new MamiSoundPack(newName, readOnly, 'Clone of ' + title, new Map(events));
        },
    };
};

const MamiSoundPackPlayer = function(soundMgr, sndLibrary) {
    const pub = {};
    let pack = null;

    pub.loadPack = function(packInfo) {
        if(typeof pack !== 'object' && typeof pack.getEventSound !== 'function')
            throw 'pack is not a valid soundpack';
        pack = packInfo;
    };

    pub.unloadPack = function() {
        pack = null;
        buffers.clear();
    };

    pub.hasPack = function() {
        return pack !== null;
    };

    const playSource = function(source, hasCallback, callback) {
        if(hasCallback)
            source.whenEnded(function() {
                callback(true);
            });
        source.play();
    };

    const handlePlayError = function(ex, hasCallback, callback) {
        console.error(ex);
        if(hasCallback)
            callback(false, ex);
    };

    pub.hasEvent = function(eventName) {
        return pack !== null && pack.hasEventSound(eventName);
    };

    pub.playEvent = function(eventName, callback) {
        if(pack === null)
            return;

        if(Array.isArray(eventName)) {
            const names = eventName;
            eventName = null;

            for(const name of names) {
                if(pack.hasEventSound(name)) {
                    eventName = name;
                    break;
                }
            }

            if(eventName === null)
                return;
        } else if(!pack.hasEventSound(eventName))
            return;

        const hasCallback = typeof callback === 'function';

        try {
            const soundInfo = sndLibrary.getSound(pack.getEventSound(eventName)),
                soundName = soundInfo.getName();

            if(soundMgr.isLoaded(soundName)) {
                playSource(soundMgr.get(soundName), hasCallback, callback);
            } else {
                soundMgr.load(soundName, soundInfo.getSources(), function(success, buffer) {
                    if(success)
                        playSource(buffer.createSource(), hasCallback, callback);
                    else
                        handlePlayError(buffer, hasCallback, callback);
                });
            }
        } catch(ex) {
            handlePlayError(ex, hasCallback, callback);
        }
    };

    return pub;
};

const MamiSoundPacks = function() {
    const packs = new Map;

    const addPack = function(packInfo) {
        if(packs.has(packInfo.getName()))
            throw 'a pack with that name has already been registered';
        packs.set(packInfo.getName(), packInfo);
    };

    const loadPack = function(packInfo, readOnly) {
        if(typeof packInfo !== 'object')
            throw 'packInfo must be an object';
        if(typeof packInfo.name !== 'string')
            throw 'packInfo must contain a name field';
        if(typeof packInfo.events !== 'object')
            throw 'packInfo must contain a events field';

        const events = new Map;
        for(const eventName in packInfo.events) {
            if(typeof packInfo.events[eventName] !== 'string')
                continue;
            events.set(eventName, packInfo.events[eventName]);
        }

        if(events.size < 1)
            throw 'packInfo does not contain any valid events';

        addPack(new MamiSoundPack(
            packInfo.name,
            readOnly,
            packInfo.title,
            events
        ));
    };

    return {
        addPack: addPack,
        loadPack: loadPack,
        loadPacks: function(packInfos, readOnly) {
            for(const packInfo of packInfos)
                loadPack(packInfo, readOnly);
        },
        removePack: function(name) {
            packs.delete(name);
        },
        clearPacks: function() {
            packs.clear();
        },
        forEachPack: function(body) {
            if(typeof body !== 'function')
                return;
            packs.forEach(body);
        },
        hasPack: function(name) {
            return packs.has(name);
        },
        getPack: function(name) {
            if(!packs.has(name))
                throw 'No pack with this name has been registered.';
            return packs.get(name);
        },
    };
};
