'use strict'

const NUM_BUFFERS = 2
let greyscaleBuffers = []
let bufferLocks = []
let outputBufferId

function resetBuffers () {
    for (let i = 0; i < NUM_BUFFERS; ++i) {
        const greyscaleBuffer = greyscaleBuffers[i]
        for (var j = 0; j < 640 * 480; ++j) {
            greyscaleBuffer[i] = 0
        }
    }
}
function resetBufferLocks () {
    for (let i = 0; i < NUM_BUFFERS; ++i) {
        bufferLocks[i] = false
    }
    outputBufferId = -1
}
function lockBuffer () {
    for (let i = 0; i < NUM_BUFFERS; ++i) {
        if (!bufferLocks[i]) {
            bufferLocks[i] = true
            return i
        }
    }
    throw new Error('Ran out of buffers')
}
function unlockBuffer (bufferId) {
    if (bufferLocks[bufferId]) {
        bufferLocks[bufferId] = false
    } else {
        throw new Error('Buffer was not locked during unlock')
    }
}
function setOutputBufferId (bufferId) {
    if (bufferLocks[bufferId]) {
        outputBufferId = bufferId
    } else {
        throw new Error('Buffer was not locked during set')
    }
}
function getOutputBufferId () {
    if (bufferLocks[outputBufferId]) {
        return outputBufferId
    } else if (outputBufferId === -1) {
        throw new Error('Output buffer was not set during get')
    } else {
        throw new Error('Output buffer was not locked during get')
    }
}


var buf = new ArrayBuffer(640 * 480 * 4)
var buf8 = new Uint8ClampedArray(buf)
var data = new Uint32Array(buf)
function presentFast (buffer, colourField) {
    const greyscaleBuffer = greyscaleBuffers[buffer]
    const pixels = canvasBuffer.data

    let colourIndex = 0
    let greyscale, r, g, b
    for (let index = 0; index < 640 * 480; ++index) {
        greyscale = greyscaleBuffer[index]
        data[index] = 0xff000000 +
            (Math.min(255, colourField[colourIndex++] * greyscale) << 16) +
            (Math.min(255, colourField[colourIndex++] * greyscale) << 8) +
            Math.min(255, colourField[colourIndex++] * greyscale)
    }

    pixels.set(buf8)
    ctx.putImageData(canvasBuffer, 0, 0)
}

function presentSlow (buffer, colourField1, colourField2) {
    const greyscaleBuffer = greyscaleBuffers[buffer]
    const pixels = canvasBuffer.data
    let writeIndex = 0
    let colourIndex = 0
    let readIndex, greyscale, clampedGreyscale
    for (readIndex = 0; readIndex < 640 * 480; ++readIndex) {
        greyscale = greyscaleBuffer[readIndex]
        clampedGreyscale = clamp(greyscale, 0, 1)
        pixels[writeIndex++] = lerp(colourField1[colourIndex], colourField2[colourIndex++], clampedGreyscale) * greyscale
        pixels[writeIndex++] = lerp(colourField1[colourIndex], colourField2[colourIndex++], clampedGreyscale) * greyscale
        pixels[writeIndex++] = lerp(colourField1[colourIndex], colourField2[colourIndex++], clampedGreyscale) * greyscale
        ++writeIndex
        ++colourIndex
    }

    ctx.putImageData(canvasBuffer, 0, 0)
}

function presentSlowMask (buffer, colourField1, colourField2) {
    const greyscaleBuffer = greyscaleBuffers[buffer]
    const pixels = canvasBuffer.data
    let writeIndex = 0
    let colourIndex = 0
    let readIndex, greyscale, clampedGreyscale
    for (readIndex = 0; readIndex < 640 * 480; ++readIndex) {
        greyscale = greyscaleBuffer[readIndex]
        clampedGreyscale = clamp(greyscale, 0, 1)
        if (greyscale > 0) {
            pixels[writeIndex++] = lerp(colourField1[colourIndex], colourField2[colourIndex++], clampedGreyscale) * greyscale
            pixels[writeIndex++] = lerp(colourField1[colourIndex], colourField2[colourIndex++], clampedGreyscale) * greyscale
            pixels[writeIndex++] = lerp(colourField1[colourIndex], colourField2[colourIndex++], clampedGreyscale) * greyscale
            ++writeIndex
            ++colourIndex
        } else {
            colourIndex += 4
            writeIndex += 4
        }
    }

    ctx.putImageData(canvasBuffer, 0, 0)
}

function presentSlowAdd (buffer, colourField1, colourField2) {
    const greyscaleBuffer = greyscaleBuffers[buffer]
    const pixels = canvasBuffer.data
    let writeIndex = 0
    let colourIndex = 0
    let readIndex, greyscale, clampedGreyscale
    for (readIndex = 0; readIndex < 640 * 480; ++readIndex) {
        greyscale = greyscaleBuffer[readIndex]
        clampedGreyscale = clamp(greyscale, 0, 1)
        pixels[writeIndex++] += lerp(colourField1[colourIndex], colourField2[colourIndex++], clampedGreyscale) * greyscale
        pixels[writeIndex++] += lerp(colourField1[colourIndex], colourField2[colourIndex++], clampedGreyscale) * greyscale
        pixels[writeIndex++] += lerp(colourField1[colourIndex], colourField2[colourIndex++], clampedGreyscale) * greyscale
        ++writeIndex
        ++colourIndex
    }

    ctx.putImageData(canvasBuffer, 0, 0)
}

function presentSlowAlpha (buffer, colourField1, colourField2) {
    const greyscaleBuffer = greyscaleBuffers[buffer]
    const pixels = canvasBuffer.data
    let writeIndex = 0
    let colourIndex = 0
    let readIndex, greyscale, clampedGreyscale
    for (readIndex = 0; readIndex < 640 * 480; ++readIndex) {
        greyscale = greyscaleBuffer[readIndex]
        clampedGreyscale = clamp(greyscale, 0, 1)
        pixels[writeIndex] = lerp(pixels[writeIndex], lerp(colourField1[colourIndex], colourField2[colourIndex++], clampedGreyscale) * greyscale, clampedGreyscale)
        ++writeIndex
        pixels[writeIndex] = lerp(pixels[writeIndex], lerp(colourField1[colourIndex], colourField2[colourIndex++], clampedGreyscale) * greyscale, clampedGreyscale)
        ++writeIndex
        pixels[writeIndex] = lerp(pixels[writeIndex], lerp(colourField1[colourIndex], colourField2[colourIndex++], clampedGreyscale) * greyscale, clampedGreyscale)
        ++writeIndex
        ++writeIndex
        ++colourIndex
    }

    ctx.putImageData(canvasBuffer, 0, 0)
}

function presentGreyscale (buffer) {
    const greyscaleBuffer = greyscaleBuffers[buffer]
    const pixels = canvasBuffer.data
    let readIndex = 0
    let writeIndex = 0
    for (var y = 0; y < 480; ++y) {
        const ys = Math.sin(1 - x * 0.00025)
        for (var x = 0; x < 640; ++x) {
            const greyscale = greyscaleBuffer[readIndex++]
            const r=Math.max(Math.min(greyscale*256, 255), 0)
            pixels[writeIndex++] = r
            pixels[writeIndex++] = r
            pixels[writeIndex++] = r
            ++writeIndex
        }
    }

    ctx.putImageData(canvasBuffer, 0, 0)
}

function presentImageData (imageData) {
    canvasBuffer.data.set(imageData)
    ctx.putImageData(canvasBuffer, 0, 0)
}