'use strict'

// https://easings.net/#
const tweeners = {
    'hold': x => 0,
    'linear': x => x,
    'easeInSine': x => 1 - Math.cos((x * Math.PI) / 2),
    'easeOutSine': x => Math.sin((x * Math.PI) / 2),
    'easeInOutSine': x => -(Math.cos(Math.PI * x) - 1) / 2,
    'easeOutBounce': x => {
        const n1 = 7.5625
        const d1 = 2.75
        if (x < 1 / d1) {
            return n1 * x * x
        } else if (x < 2 / d1) {
            return n1 * (x -= 1.5 / d1) * x + 0.75
        } else if (x < 2.5 / d1) {
            return n1 * (x -= 2.25 / d1) * x + 0.9375
        } else {
            return n1 * (x -= 2.625 / d1) * x + 0.984375
        }
    }
}

function keyValidate_boolean (keyData) {
    return typeof keyData === 'boolean'
}
function keyLerp_boolean (a, b, alpha) {
    return a
}

function keyValidate_int (keyData) {
    return typeof keyData === 'number'
}
function keyLerp_int (a, b, alpha) {
    return Math.floor(a + (b - a) * alpha)
}

function keyValidate_float (keyData) {
    return typeof keyData === 'number'
}
function keyLerp_float (a, b, alpha) {
    return a + (b - a) * alpha
}

function keyValidate_float2 (keyData) {
    if (typeof keyData === 'object' &&
    keyData.length === 2 &&
        typeof keyData[0] === 'number' &&
        typeof keyData[1] === 'number') {
      return true
    } else {
      return false
    }
}
function keyLerp_float2 (a, b, alpha) {
    return [
        a[0] + (b[0] - a[0]) * alpha,
        a[1] + (b[1] - a[1]) * alpha,
    ]
}

function keyValidate_float3 (keyData) {
    if (typeof keyData === 'object' &&
    keyData.length === 3 &&
        typeof keyData[0] === 'number' &&
        typeof keyData[1] === 'number' &&
        typeof keyData[2] === 'number') {
      return true
    } else {
      return false
    }
}
function keyLerp_float3 (a, b, alpha) {
    return [
        a[0] + (b[0] - a[0]) * alpha,
        a[1] + (b[1] - a[1]) * alpha,
        a[2] + (b[2] - a[2]) * alpha,
    ]
}

function keyValidate_float4 (keyData) {
    if (typeof keyData === 'object' &&
    keyData.length === 4 &&
        typeof keyData[0] === 'number' &&
        typeof keyData[1] === 'number' &&
        typeof keyData[2] === 'number' &&
        typeof keyData[3] === 'number') {
      return true
    } else {
      return false
    }
}
function keyLerp_float4 (a, b, alpha) {
    return [
        a[0] + (b[0] - a[0]) * alpha,
        a[1] + (b[1] - a[1]) * alpha,
        a[2] + (b[2] - a[2]) * alpha,
        a[3] + (b[3] - a[3]) * alpha,
    ]
}

function keyValidate_string (keyData) {
    return typeof keyData === 'string'
}
function keyLerp_string (a, b, alpha) {
    return a
}

const allKeyFuncs = {
    boolean: [keyValidate_boolean, keyLerp_boolean, (x) => !!x],
    int: [keyValidate_int, keyLerp_int, (x) => Math.floor(x)],
    float: [keyValidate_float, keyLerp_float, (x) => x],
    float2: [keyValidate_float2, keyLerp_float2, (x) => x],
    float3: [keyValidate_float3, keyLerp_float3, (x) => x],
    float4: [keyValidate_float4, keyLerp_float4, (x) => x],
    string: [keyValidate_string, keyLerp_string, (x) => x],
    angle: [keyValidate_float, keyLerp_float, (x) => degToRad(x)],
    model: [keyValidate_string, keyLerp_string, (x) => getModel(x)],
    texture: [keyValidate_string, keyLerp_string, (x) => getTexture(x)],
}

function calculateConfig (time, configs, syncs, config) {
    const results = {}
    config.forEach(([paramName, keyType, defaultValue]) => {
        results[paramName] = readAtTime(time, configs, syncs, paramName, keyType, defaultValue)
    })
    return results
}

function readAtTime (time, configs, syncs, paramName, keyType, defaultValue) {
    const keyFuncs = allKeyFuncs[keyType]

    let result = undefined
    const keyData = configs[paramName]
    if (keyData !== undefined) {
        if (keyFuncs[0](keyData)) {
            result = keyData
        } else {
            var lastKey = keyData[0]
            if (time <= lastKey[0]) {
                result = lastKey[1]
            } else {
                for (var i in keyData) {
                    const nextKey = keyData[i];
                    if (nextKey[0] > time) {
                        const alpha = (time - lastKey[0]) / (nextKey[0] - lastKey[0])
                        let tweener = lastKey[2] ? lastKey[2] : 'linear'
                        if (tweeners[tweener] === undefined) {
                        console.warn(`Unknown tweener type '${tweener}'`)
                        tweener = 'linear'
                        }
                        const tweenedAlpha = tweeners[tweener](alpha)
                        result = keyFuncs[1](lastKey[1], nextKey[1], tweenedAlpha)
                        break
                    }
                    lastKey = nextKey
                }
                if (result === undefined) {
                    result = lastKey[1]
                }
            }
        }
    } else {
        result = defaultValue
    }
    result = keyFuncs[2](result)

    const syncData = syncs[paramName]
    if (syncData) {
        if (keyType === 'float') {
            syncData.forEach(sync => {
                const a = 1
                const b = sync[1]
                const alpha = readInstrumentSync(sync[0])
                result = result * (a + (b-1)*alpha)
            })
        }  else if (keyType === 'int') {
            syncData.forEach(sync => {
                const a = 1
                const b = sync[1]
                const alpha = readInstrumentSync(sync[0])
                result = Math.floor(result * (a + (b-1)*alpha))
            })
        }
    } 

    return result
}

