/**
 * @module ol/webgl/TileGeometry
 */

import MixedGeometryBatch from '../render/webgl/MixedGeometryBatch.js';
import {
  create as createTransform,
  translate as translateTransform,
} from '../transform.js';
import {ARRAY_BUFFER, STATIC_DRAW} from '../webgl.js';
import BaseTileRepresentation from './BaseTileRepresentation.js';
import WebGLArrayBuffer from './Buffer.js';

/**
 * @typedef {import("../VectorRenderTile.js").default} TileType
 */

/**
 * @extends {BaseTileRepresentation<TileType>}
 */
class TileGeometry extends BaseTileRepresentation {
  /**
   * @param {import("./BaseTileRepresentation.js").TileRepresentationOptions<TileType>} options The tile texture options.
   * @param {import("../render/webgl/VectorStyleRenderer.js").default} styleRenderer Vector style renderer
   */
  constructor(options, styleRenderer) {
    super(options);

    /**
     * @private
     */
    this.batch_ = new MixedGeometryBatch();

    /**
     * @private
     */
    this.styleRenderer_ = styleRenderer;

    /**
     * @type {import("../render/webgl/VectorStyleRenderer.js").WebGLBuffers}
     */
    this.buffers = null;

    /**
     * Each geometry tile also has a mask which consisted of a quad (two triangles); this mask is intended to
     * be rendered to an offscreen buffer, and be used to correctly mask tiles according to their zoom level
     * during rendering; these coordinates are expressed in the same coordinate system as the tile geometries
     */
    this.maskVertices = new WebGLArrayBuffer(ARRAY_BUFFER, STATIC_DRAW);

    this.setTile(options.tile);
  }

  /**
   * @private
   */
  generateMaskBuffer_() {
    const extent = this.tile.getSourceTiles()[0].extent;
    const originX = extent[0];
    const originY = extent[1];
    const width = extent[2] - originX;
    const height = extent[3] - originY;
    this.maskVertices.fromArray([0, 0, width, 0, width, height, 0, height]);
    this.helper.flushBufferData(this.maskVertices);
  }

  /**
   * @override
   */
  uploadTile() {
    this.generateMaskBuffer_();

    this.batch_.clear();
    const sourceTiles = this.tile.getSourceTiles();
    const features = sourceTiles.reduce(
      (accumulator, sourceTile) => accumulator.concat(sourceTile.getFeatures()),
      [],
    );
    this.batch_.addFeatures(features);

    const tileOriginX = sourceTiles[0].extent[0];
    const tileOriginY = sourceTiles[0].extent[1];
    const transform = translateTransform(
      createTransform(),
      -tileOriginX,
      -tileOriginY,
    );

    this.styleRenderer_
      .generateBuffers(this.batch_, transform)
      .then((buffers) => {
        this.buffers = buffers;
        this.setReady();
      });
  }

  /**
   * @override
   */
  disposeInternal() {
    if (this.buffers) {
      /**
       * @param {Array<WebGLArrayBuffer>} typeBuffers Buffers
       */
      const disposeBuffersOfType = (typeBuffers) => {
        for (const buffer of typeBuffers) {
          if (buffer) {
            this.helper.deleteBuffer(buffer);
          }
        }
      };
      this.buffers.pointBuffers &&
        disposeBuffersOfType(this.buffers.pointBuffers);
      this.buffers.lineStringBuffers &&
        disposeBuffersOfType(this.buffers.lineStringBuffers);
      this.buffers.polygonBuffers &&
        disposeBuffersOfType(this.buffers.polygonBuffers);
    }
    super.disposeInternal();
  }
}

export default TileGeometry;