'use strict'

modules['spikeBall'] = {
    config: [
        ['model1', 'model', ''],
        ['model2', 'model', ''],
        ['extendMin', 'float', 1],
        ['extendMax', 'float', 2],
        ['brightness1', 'float', 1],
        ['brightness2', 'float', 1],
        ['bump1', 'float', 0],
        ['bump2', 'float', 0],
        ['scale', 'float3', ['1', 1, 1]],
        ['translation', 'float3', ['0', 0, 0]],
        ['xRotation', 'angle', 0],
        ['yRotation', 'angle', 0],
        ['zRotation', 'angle', 0],
        ['animation', 'float', 0],
        ['powA', 'float', 0.10],
        ['powB', 'float', 10.0],
        ['dScale', 'float', 1.0],
        ['attractorStrength1', 'float', 1.0],
        ['attractorStrength2', 'float', 0.0],
    ],

    init: () => {
      return {
          attractorSet1: [
            [1, 0, 0],
            [-1, 0, 0],
            [0, 1, 0],
            [0, -1, 0],
            [0, 0, 1],
            [0, 0, -1],
          ].map(x => m4.normalize(x)),
          attractorSet2: [
            [-1,-1,-1],
            [-1,-1,+1],
            [-1,+1,-1],
            [-1,+1,+1],
            [+1,-1,-1],
            [+1,-1,+1],
            [+1,+1,-1],
            [+1,+1,+1],
          ].map(x => m4.normalize(x)),
        }
    },

    render: async (time, self, config) => {
        const {
            model1,
            model2,
            extendMin,
            extendMax,
            brightness1,
            brightness2,
            bump1,
            bump2,
            scale,
            translation,
            xRotation,
            yRotation,
            zRotation,
            animation,
            powA,
            powB,
            dScale,
            attractorStrength1,
            attractorStrength2,
        } = { ...config }

      const buffer = greyscaleBuffers[getOutputBufferId()]

      let attractorMat = m4.xRotation(animation * .5)
      attractorMat = m4.yRotate(attractorMat, animation * .3)
      attractorMat = m4.zRotate(attractorMat, animation * .2)
      const attractorMap = (source, strength) => {
          return source.map(attractor => {
              const transformedAttractor = m4.transformVector(attractorMat, [...attractor, 0])
              return [transformedAttractor[0], transformedAttractor[1], transformedAttractor[2], strength]
          })
      }
      const attractors = [
          ...attractorMap(self.attractorSet1, attractorStrength1),
          ...attractorMap(self.attractorSet2, attractorStrength2),
      ]

      const model1c = JSON.parse(JSON.stringify(model1))
      const model2c = JSON.parse(JSON.stringify(model2))
      const warp = (v, bump) => {
          const nv = m4.normalize(v)
          const d = attractors.reduce((p, attractor) => {
              return Math.max(p, (1 - clamp((m4.lengthVector(m4.subtractVectors(nv, attractor))**powA)*dScale, 0, 1)**powB) * attractor[3])
          }, 0)
          const s = lerp(extendMin, extendMax, d) + bump
          return m4.multiplyVector(nv, s)
      }
      model1c.verts = model1c.verts.map(v => warp(v, bump1))
      model2c.verts = model2c.verts.map(v => warp(v, bump2))
      model1c.objects[0].subObjects[0].brightness = brightness1
      model2c.objects[0].subObjects[0].brightness = brightness2

      const xRotationMat = m4.xRotation(xRotation)
      const yRotationMat = m4.yRotation(yRotation)
      const zRotationMat = m4.zRotation(zRotation)
      const scaleMat = m4.scaling(scale[0], scale[1], scale[2])
      const translationMat = m4.translation(translation[0], translation[1], translation[2])
      const modelMat = m4.multiply(translationMat, m4.multiply(scaleMat, m4.multiply(m4.multiply(zRotationMat, yRotationMat), xRotationMat)))

      renderer.drawModel(buffer, modelMat, model1c)
      const state = renderer.saveRenderState()
      renderer.setDepthBias(-.1)
      renderer.drawModel(buffer, modelMat, model2c)
      renderer.loadRenderState(state)
    }
}
