class RobotEffect {
    constructor (audioContext) {
        this.audioContext = audioContext;

        this.input = this.audioContext.createGain();
        this.output = this.audioContext.createGain();

        // Ring modulator inspired by BBC Dalek voice
        // http://recherche.ircam.fr/pub/dafx11/Papers/66_e.pdf
        // https://github.com/bbc/webaudio.prototyping.bbc.co.uk

        // > There are four parallel signal paths, two which process the
        // > combination Vc + Vin / 2 and two which process Vc - Vin/2.
        // > Each branch consists of a non-linearity [diode]...
        const createDiodeNode = () => {
            const node = this.audioContext.createWaveShaper();

            // Piecewise function given by (2) in Parker paper
            const transform = (v, vb = 0.2, vl = 0.4, h = 0.65) => {
                if (v <= vb) return 0;
                if (v <= vl) return h * (Math.pow(v - vb, 2) / (2 * vl - 2 * vb));
                return h * v - h * vl + h * (Math.pow(v - vb, 2) / (2 * vl - 2 * vb));
            };

            // Create the waveshaper curve with the voltage transform above
            const bufferLength = 1024;
            const curve = new Float32Array(bufferLength);
            for (let i = 0; i < bufferLength; i++) {
                const voltage = 2 * (i / bufferLength) - 1;
                curve[i] = transform(voltage);
            }
            node.curve = curve;
            return node;
        };

        const oscillator = this.audioContext.createOscillator();
        oscillator.frequency.value = 50;
        oscillator.start(0);

        const vInGain = this.audioContext.createGain();
        vInGain.gain.value = 0.5;

        const vInInverter1 = this.audioContext.createGain();
        vInInverter1.gain.value = -1;

        const vInInverter2 = this.audioContext.createGain();
        vInInverter2.gain.value = -1;

        const vInDiode1 = createDiodeNode(this.audioContext);
        const vInDiode2 = createDiodeNode(this.audioContext);

        const vInInverter3 = this.audioContext.createGain();
        vInInverter3.gain.value = -1;

        const vcInverter1 = this.audioContext.createGain();
        vcInverter1.gain.value = -1;

        const vcDiode3 = createDiodeNode(this.audioContext);
        const vcDiode4 = createDiodeNode(this.audioContext);

        const compressor = this.audioContext.createDynamicsCompressor();
        compressor.threshold.value = -5;
        compressor.knee.value = 15;
        compressor.ratio.value = 12;
        compressor.attack.value = 0;
        compressor.release.value = 0.25;

        const biquadFilter = this.audioContext.createBiquadFilter();
        biquadFilter.type = 'highpass';
        biquadFilter.frequency.value = 1000;
        biquadFilter.gain.value = 1.25;

        this.input.connect(vcInverter1);
        this.input.connect(vcDiode4);

        vcInverter1.connect(vcDiode3);

        oscillator.connect(vInGain);
        vInGain.connect(vInInverter1);
        vInGain.connect(vcInverter1);
        vInGain.connect(vcDiode4);

        vInInverter1.connect(vInInverter2);
        vInInverter1.connect(vInDiode2);
        vInInverter2.connect(vInDiode1);

        vInDiode1.connect(vInInverter3);
        vInDiode2.connect(vInInverter3);

        vInInverter3.connect(compressor);
        vcDiode3.connect(compressor);
        vcDiode4.connect(compressor);

        this.input.connect(biquadFilter);
        biquadFilter.connect(compressor);

        compressor.connect(this.output);
    }
}

export default RobotEffect;