import { BufferTransform } from '@luma.gl/engine';
import { readPixelsToArray } from '@luma.gl/webgl';
import { GL } from '@luma.gl/constants';
import { padBuffer, getAttributeTypeFromSize, getAttributeBufferLength, getFloat32VertexFormat, cycleBuffers } from '../lib/attribute/attribute-transition-utils';
import Attribute from '../lib/attribute/attribute';
import Transition from './transition';
export default class GPUSpringTransition {
  constructor({
    device,
    attribute,
    timeline
  }) {
    this.device = void 0;
    this.type = 'spring';
    this.attributeInTransition = void 0;
    this.settings = void 0;
    this.attribute = void 0;
    this.transition = void 0;
    this.currentStartIndices = void 0;
    this.currentLength = void 0;
    this.texture = void 0;
    this.framebuffer = void 0;
    this.transform = void 0;
    this.buffers = void 0;
    this.device = device;
    this.type = 'spring';
    this.transition = new Transition(timeline);
    this.attribute = attribute;
    this.attributeInTransition = new Attribute(device, { ...attribute.settings,
      normalized: false
    });
    this.currentStartIndices = attribute.startIndices;
    this.currentLength = 0;
    this.texture = getTexture(device);
    this.framebuffer = getFramebuffer(device, this.texture);
    const bufferOpts = {
      byteLength: 0,
      usage: 35050
    };
    this.buffers = [device.createBuffer(bufferOpts), device.createBuffer(bufferOpts), device.createBuffer(bufferOpts)];
    this.transform = getTransform(device, attribute, this.buffers);
  }

  get inProgress() {
    return this.transition.inProgress;
  }

  start(transitionSettings, numInstances) {
    const {
      buffers,
      attribute
    } = this;
    const padBufferOpts = {
      numInstances,
      attribute,
      fromLength: this.currentLength,
      fromStartIndices: this.currentStartIndices,
      getData: transitionSettings.enter
    };

    for (const [index, buffer] of buffers.entries()) {
      const paddedBuffer = padBuffer({
        buffer,
        ...padBufferOpts
      });

      if (buffer !== paddedBuffer) {
        buffer.destroy();
        buffers[index] = paddedBuffer;
        console.warn(`[GPUSpringTransition] Replaced buffer ${buffer.id} (${buffer.byteLength} bytes) → ` + `${paddedBuffer.id} (${paddedBuffer.byteLength} bytes)`);
      }
    }

    this.settings = transitionSettings;
    this.currentStartIndices = attribute.startIndices;
    this.currentLength = getAttributeBufferLength(attribute, numInstances);
    this.attributeInTransition.setData({
      buffer: buffers[1],
      value: attribute.value
    });
    this.transition.start({ ...transitionSettings,
      duration: Infinity
    });
    this.transform.model.setVertexCount(Math.floor(this.currentLength / attribute.size));
    this.transform.model.setAttributes({
      aTo: attribute.buffer
    });
  }

  update() {
    const {
      buffers,
      transform,
      framebuffer,
      transition
    } = this;
    const updated = transition.update();

    if (!updated) {
      return false;
    }

    const settings = this.settings;
    this.transform.model.setAttributes({
      aPrev: buffers[0],
      aCur: buffers[1]
    });
    this.transform.transformFeedback.setBuffers({
      vNext: buffers[2]
    });
    transform.model.setUniforms({
      stiffness: settings.stiffness,
      damping: settings.damping
    });
    transform.run({
      framebuffer,
      discard: false,
      parameters: {
        viewport: [0, 0, 1, 1]
      },
      clearColor: [0, 0, 0, 0]
    });
    cycleBuffers(buffers);
    this.attributeInTransition.setData({
      buffer: buffers[1],
      value: this.attribute.value
    });
    const isTransitioning = readPixelsToArray(framebuffer)[0] > 0;

    if (!isTransitioning) {
      transition.end();
    }

    return true;
  }

  cancel() {
    this.transition.cancel();
    this.transform.delete();

    for (const buffer of this.buffers) {
      buffer.delete();
    }

    this.buffers.length = 0;
    this.texture.delete();
    this.framebuffer.delete();
  }

}
const vs = `\
#version 300 es
#define SHADER_NAME spring-transition-vertex-shader

#define EPSILON 0.00001

uniform float stiffness;
uniform float damping;
in ATTRIBUTE_TYPE aPrev;
in ATTRIBUTE_TYPE aCur;
in ATTRIBUTE_TYPE aTo;
out ATTRIBUTE_TYPE vNext;
out float vIsTransitioningFlag;

ATTRIBUTE_TYPE getNextValue(ATTRIBUTE_TYPE cur, ATTRIBUTE_TYPE prev, ATTRIBUTE_TYPE dest) {
  ATTRIBUTE_TYPE velocity = cur - prev;
  ATTRIBUTE_TYPE delta = dest - cur;
  ATTRIBUTE_TYPE spring = delta * stiffness;
  ATTRIBUTE_TYPE damper = velocity * -1.0 * damping;
  return spring + damper + velocity + cur;
}

void main(void) {
  bool isTransitioning = length(aCur - aPrev) > EPSILON || length(aTo - aCur) > EPSILON;
  vIsTransitioningFlag = isTransitioning ? 1.0 : 0.0;

  vNext = getNextValue(aCur, aPrev, aTo);
  gl_Position = vec4(0, 0, 0, 1);
  gl_PointSize = 100.0;
}
`;
const fs = `\
#version 300 es
#define SHADER_NAME spring-transition-is-transitioning-fragment-shader

in float vIsTransitioningFlag;

out vec4 fragColor;

void main(void) {
  if (vIsTransitioningFlag == 0.0) {
    discard;
  }
  fragColor = vec4(1.0);
}`;

function getTransform(device, attribute, buffers) {
  const attributeType = getAttributeTypeFromSize(attribute.size);
  const format = getFloat32VertexFormat(attribute.size);
  return new BufferTransform(device, {
    vs,
    fs,
    attributes: {
      aPrev: buffers[0],
      aCur: buffers[1]
    },
    bufferLayout: [{
      name: 'aPrev',
      format
    }, {
      name: 'aCur',
      format
    }, {
      name: 'aTo',
      format
    }],
    feedbackBuffers: {
      vNext: buffers[2]
    },
    varyings: ['vNext'],
    defines: {
      ATTRIBUTE_TYPE: attributeType
    },
    parameters: {
      depthCompare: 'always',
      blendColorOperation: 'max',
      blendColorSrcFactor: 'one',
      blendColorDstFactor: 'one',
      blendAlphaOperation: 'max',
      blendAlphaSrcFactor: 'one',
      blendAlphaDstFactor: 'one'
    }
  });
}

function getTexture(device) {
  return device.createTexture({
    data: new Uint8Array(4),
    format: 'rgba8unorm',
    mipmaps: false,
    width: 1,
    height: 1
  });
}

function getFramebuffer(device, texture) {
  return device.createFramebuffer({
    id: 'spring-transition-is-transitioning-framebuffer',
    width: 1,
    height: 1,
    colorAttachments: [texture]
  });
}
//# sourceMappingURL=gpu-spring-transition.js.map