#include utility.js
#include audio/source.js

const MamiAudioContext = function() {
    let volume;
    let isMuted = false;

    const ctxArgs = { latencyHint: 'playback' };
    let ctx, mainGain;

    const onReset = [];

    const reset = () => {
        const audObj = window.AudioContext || window.webkitAudioContext;
        try {
            ctx = new audObj(ctxArgs);
        } catch(ex) {
            ctx = new audObj;
        }

        mainGain = ctx.createGain();

        if(volume === undefined) {
            volume = mainGain.gain.defaultValue;
            isMuted = false;
        } else
            mainGain.gain.value = isMuted ? 0 : volume;

        mainGain.connect(ctx.destination);

        for(const handler of onReset)
            handler();
    };

    reset();

    return {
        getAudioContext: () => ctx,
        getAudioContextGain: () => mainGain,

        isReady: () => ctx !== undefined,
        reset: reset,
        onReset: handler => {
            if(!onReset.includes(handler))
                onReset.push(handler);
        },

        getVolume: () => volume,
        setVolume: vol => {
            volume = vol;
            if(!isMuted && mainGain !== undefined)
                mainGain.gain.value = volume;
        },
        isMuted: () => isMuted,
        setMuted: mute => {
            isMuted = mute;
            if(mainGain !== undefined)
                mainGain.gain.value = isMuted ? 0 : volume;
        },

        createBuffer: async url => {
            if(ctx === undefined)
                return undefined;

            const result = await $x.get(url, { type: 'arraybuffer' });
            return await ctx.decodeAudioData(result.body());
        },

        createSource: (buffer, reverse) => {
            if(ctx === undefined || buffer === undefined)
                return new MamiAudioSourceDummy;

            if(reverse) {
                const reverse = new AudioBuffer({
                    length: buffer.length,
                    numberOfChannels: buffer.numberOfChannels,
                    sampleRate: buffer.sampleRate,
                });

                const data = new Float32Array(buffer.length);
                for(let i = 0; i < reverse.numberOfChannels; ++i) {
                    buffer.copyFromChannel(data, i, 0);
                    data.reverse();
                    reverse.copyToChannel(data, i, 0);
                }

                buffer = reverse;
            }

            const gain = ctx.createGain();
            gain.connect(mainGain);

            const source = ctx.createBufferSource();
            source.buffer = buffer;
            source.connect(gain);

            return new MamiAudioSource(source, gain);
        },
    };
};
