'use strict'

  modules['distort'] = {
    config: [
        ['strength', 'float', 1],
        ['magnetDistance', 'float', 0],
        ['distortDistance', 'float', 20],
        ['distortTime', 'float', 0],
        ['brightness', 'float', 0.5],
    ],

    init: () => {
        const octaveData = [
            [0.030, 4.5,12.34, 0.5],
            [0.001, 2.22,1.99, 0.3],
            [0.123, 5.7,11.17, 0.2],
         ]
         const noiseRng = new Math.seedrandom('noise')
         const noiseData = new Float32Array([...Array(1000).keys()].map(x => noiseRng() * 2 - 1))
         return {
             octaveData,
             noiseData
         }
    },
 
    render: async (frameTime, self, config) => {
        const {
            strength,
            magnetDistance,
            distortDistance,
            distortTime,
            brightness,
        } = { ...config }
        const distortBrightness = brightness
    
        if (strength > 0.1) {
            const srcBufferId = getOutputBufferId()
            const dstBufferId = lockBuffer()
            const srcBuffer = greyscaleBuffers[srcBufferId]
            const dstBuffer = greyscaleBuffers[dstBufferId]
    
            const magnetTime = frameTime * 1.57
            const magnetTimeAlpha = magnetTime % 1
            const magnetStrengthA = self.noiseData[Math.floor(frameTime) % self.noiseData.length]
            const magnetStrengthB = self.noiseData[(Math.floor(frameTime) + 1) % self.noiseData.length]
            const magnetStrength = (magnetStrengthA + (magnetStrengthB - magnetStrengthA) * magnetTimeAlpha) * 0.5 + 0.5
    
            let writeIndex = 0
            for (let y = 0; y < 480; ++y) {
                const brightness = 1 +
                    Math.sin(-distortTime*1.21 + y*0.012)*distortBrightness*0.75 + 
                    Math.sin(+distortTime*1.53 + y*0.003)*distortBrightness*0.25
    
                const readLine = 640 * y
                let distortValue = 0
                for (let i = 0; i < self.octaveData.length; ++i) {
                    const octave = self.octaveData[i]
                    distortValue += self.noiseData[Math.floor(y*octave[0] + (frameTime+octave[1])*octave[2]) % self.noiseData.length] * octave[3]
                }
                distortValue *= distortDistance
                distortValue += Math.pow(1-(y/480), magnetStrength * 10 + 2) * magnetDistance * magnetStrength
                distortValue *= strength
                distortValue = Math.floor(distortValue)
                if (distortValue > 0) {
                    let x = 0
                    const value = srcBuffer[readLine] * brightness
                    for (; x < distortValue; ++x) {
                        dstBuffer[writeIndex++] = value
                    }
                    let readIndex = 0
                    for (; x < 640; ++x) {
                        dstBuffer[writeIndex++] = srcBuffer[readLine + readIndex++] * brightness
                    }
                } else {
                    let readIndex = -distortValue
                    let x = 0
                    let value
                    for (; x < 640 + distortValue; ++x) {
                        value = srcBuffer[readLine + readIndex++] * brightness
                        dstBuffer[writeIndex++] = value
                    }
                    for (; x < 640; ++x) {
                        dstBuffer[writeIndex++] = value
                    }
                }
            }
    
            unlockBuffer(srcBufferId)
            setOutputBufferId(dstBufferId)
        }
    }
}
