'use strict';

var Resource = require('./Resource.js');
var runner = require('../../../runner.js');
var constants = require('../../../constants.js');

function formatToCount(f) {
  if (f === constants.FORMATS.RGBA || f === constants.FORMATS.RGBA_INTEGER) {
    return 4;
  }
  if (f === constants.FORMATS.RG || f === constants.FORMATS.RG_INTEGER) {
    return 2;
  }
  return 1;
}
class Buffer3DResource extends Resource.Resource {
  /**
   * @param source - Source buffer
   * @param options - Options
   * @param {number} options.width - Width of the texture
   * @param {number} options.height - Height of the texture
   * @param {1|2|4|8} [options.unpackAlignment=4] - The alignment of the pixel rows.
   */
  constructor(source, options) {
    const { width, height, depth } = options || {};
    if (!width || !height || !depth) {
      throw new Error("BufferResource width or height invalid");
    }
    super(width, height);
    this.regionsToUpdate = [];
    this.depth = depth;
    this.pixelSize = options.pixelSize || 1;
    this.useFixedSize = options.useFixedSize ?? true;
    this.useSubRegions = options.useSubRegions ?? false;
    this.onResize3D = new runner.Runner("setRealSize3D");
    this.data = source;
  }
  bind(baseTexture) {
    this.onResize3D.add(baseTexture);
    this.onUpdate.add(baseTexture);
    this.onError.add(baseTexture);
    baseTexture.target = constants.TARGETS.TEXTURE_3D;
    if (this._width || this._height || this.depth) {
      this.onResize3D.emit(this._width, this._height, this.depth, this.pixelSize);
    }
  }
  /**
   * Upload the texture to the GPU.
   * @param renderer - Upload to the renderer
   * @param baseTexture - Reference to parent texture
   * @param glTexture - glTexture
   * @returns - true is success
   */
  upload(renderer, baseTexture, glTexture) {
    const gl = renderer.gl;
    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
    const { data, width, height, depth } = this;
    const { target } = baseTexture;
    const { internalFormat, type, format } = glTexture;
    glTexture.width = this.width;
    glTexture.height = this.height;
    glTexture.depth = this.depth;
    if (this.useFixedSize) {
      if (glTexture.dataLength === 0) {
        gl.texStorage3D(target, 1, internalFormat, width, height, depth);
        if (data) {
          gl.texSubImage3D(target, 0, 0, 0, 0, width, height, depth, format, type, data);
          glTexture.dataLength = data.length;
        } else {
          glTexture.dataLength = width * height * depth * formatToCount(format);
        }
      } else if (data && glTexture.dataLength !== data.length) {
        console.warn("Texture3D resize fail");
        return true;
      }
    }
    if (this.useSubRegions) {
      this.uploadSubs(renderer, baseTexture, glTexture);
    } else if (data) {
      if (glTexture.dataLength !== data.length) {
        glTexture.dataLength = data.length;
        gl.texImage3D(target, 0, internalFormat, width, height, depth, 0, format, type, data);
      } else {
        gl.texSubImage3D(target, 0, 0, 0, 0, width, height, depth, format, type, data);
      }
    }
    return true;
  }
  uploadSubs(renderer, baseTexture, glTexture) {
    const { gl } = renderer;
    const { pixelSize, depth, width, height } = this;
    const { target } = baseTexture;
    const { internalFormat, type, format } = glTexture;
    const sz = width * height * depth * formatToCount(format);
    if (glTexture.dataLength !== sz) {
      glTexture.dataLength = sz;
      gl.texImage3D(target, 0, internalFormat, width, height, depth, 0, format, type, null);
    }
    for (let i = 0; i < this.regionsToUpdate.length; i++) {
      const region = this.regionsToUpdate[i];
      if (!region.dirty) {
        continue;
      }
      region.dirty = false;
      if (!region.isEmpty) {
        gl.texSubImage3D(target, 0, region.layout.x / pixelSize, region.layout.y / pixelSize, region.layout.z / pixelSize, region.layout.width / pixelSize, region.layout.height / pixelSize, region.layout.depth / pixelSize, format, type, region.data);
        region.data = null;
      }
    }
    this.regionsToUpdate.length = 0;
  }
  style(renderer, baseTexture, _glTexture) {
    const { gl } = renderer;
    const target = baseTexture.target;
    const sm = baseTexture.scaleMode === constants.SCALE_MODES.LINEAR ? gl.LINEAR : gl.NEAREST;
    gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, sm);
    gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, sm);
    gl.texParameteri(target, gl.TEXTURE_WRAP_S, baseTexture.wrapMode);
    gl.texParameteri(target, gl.TEXTURE_WRAP_T, baseTexture.wrapMode);
    gl.texParameteri(target, gl.TEXTURE_WRAP_R, baseTexture.wrapMode);
    return true;
  }
  /** Destroy and don't use after this. */
  dispose() {
    this.data = null;
  }
  /**
   * Used to auto-detect the type of resource.
   * @param {*} source - The source object
   * @returns {boolean} `true` if buffer source
   */
  static test(source) {
    return source === null || source instanceof Int8Array || source instanceof Uint8Array || source instanceof Uint8ClampedArray || source instanceof Int16Array || source instanceof Uint16Array || source instanceof Int32Array || source instanceof Uint32Array || source instanceof Float32Array;
  }
}

exports.Buffer3DResource = Buffer3DResource;
exports.formatToCount = formatToCount;
//# sourceMappingURL=Buffer3DResource.js.map
