import { TypedArray } from '@math.gl/types';
import type { ExternalImage } from '@luma.gl/core';
import { Buffer, Texture, Framebuffer, FramebufferProps } from '@luma.gl/core';
import { GL, GLTextureTarget, GLTextureCubeMapTarget, GLTexelDataFormat, GLPixelType } from '@luma.gl/constants';
import { WEBGLFramebuffer } from "../resources/webgl-framebuffer.js";
import { WEBGLBuffer } from "../resources/webgl-buffer.js";
/**
 * Options for setting data into a texture
 */
export type WebGLSetTextureOptions = {
    dimension: '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d';
    height: number;
    width: number;
    depth: number;
    mipLevel?: number;
    glTarget: GLTextureTarget;
    glInternalFormat: GL;
    glFormat: GLTexelDataFormat;
    glType: GLPixelType;
    compressed?: boolean;
    byteOffset?: number;
    byteLength?: number;
};
/**
 * Options for copying an image or data into a texture
 *
 * @param {GLenum} format - internal format of image data.
 * @param {GLenum} type
 *  - format of array (autodetect from type) or
 *  - (WEBGL2) format of buffer or ArrayBufferView
 * @param {GLenum} dataFormat - format of image data.
 * @param {Number} offset - (WEBGL2) offset from start of buffer
 * @parameters - temporary settings to be applied, can be used to supply pixel store settings.
 */
export type WebGLCopyTextureOptions = {
    dimension: '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d';
    /** mip level to be updated */
    mipLevel?: number;
    /** width of the sub image to be updated */
    width: number;
    /** height of the sub image to be updated */
    height: number;
    /** depth of texture to be updated */
    depth?: number;
    /** xOffset from where texture to be updated */
    x?: number;
    /** yOffset from where texture to be updated */
    y?: number;
    /** yOffset from where texture to be updated */
    z?: number;
    glTarget: GLTextureTarget;
    glInternalFormat: GL;
    glFormat: GL;
    glType: GL;
    compressed?: boolean;
    byteOffset?: number;
    byteLength?: number;
};
/**
 * Initializes a texture memory space
 * Clear all the textures and mip levels of a two-dimensional or array texture at the same time.
 * On some implementations faster than repeatedly setting levels
 *
 * @note From WebGL 2 spec section 3.7.6:
 * @see https://registry.khronos.org/webgl/specs/latest/2.0/
 * - The image contents are set as if a buffer of sufficient size initialized to 0 would be passed to each level's texImage2D/3D
 * - texStorage2D should be considered a preferred alternative to texImage2D. It may have lower memory costs than texImage2D in some implementations.
 * - Once texStorage*D has been called, the texture is immutable and can only be updated with texSubImage*(), not texImage()
 */
export declare function initializeTextureStorage(gl: WebGL2RenderingContext, levels: number, options: WebGLSetTextureOptions): void;
/**
 * Copy a region of compressed data from a GPU memory buffer into this texture.
 */
export declare function copyExternalImageToMipLevel(gl: WebGL2RenderingContext, handle: WebGLTexture, image: ExternalImage, options: WebGLCopyTextureOptions & {
    flipY?: boolean;
}): void;
/**
 * Copy a region of data from a CPU memory buffer into this texture.
 */
export declare function copyCPUDataToMipLevel(gl: WebGL2RenderingContext, typedArray: TypedArray, options: WebGLCopyTextureOptions): void;
/**
 * Copy a region of compressed data from a GPU memory buffer into this texture.
 */
export declare function copyGPUBufferToMipLevel(gl: WebGL2RenderingContext, webglBuffer: WebGLBuffer, byteLength: number, options: WebGLCopyTextureOptions): void;
/** Convert a WebGPU style texture constant to a WebGL style texture constant */
export declare function getWebGLTextureTarget(dimension: '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d'): GLTextureTarget;
/**
 * In WebGL, cube maps specify faces by overriding target instead of using the depth parameter.
 * @note We still bind the texture using GL.TEXTURE_CUBE_MAP, but we need to use the face-specific target when setting mip levels.
 * @returns glTarget unchanged, if dimension !== 'cube'.
 */
export declare function getWebGLCubeFaceTarget(glTarget: GLTextureTarget, dimension: '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d', level: number): GLTextureTarget | GLTextureCubeMapTarget;
/**
 * Clear a texture mip level.
 * Wrapper for the messy WebGL texture API
 *
export function clearMipLevel(gl: WebGL2RenderingContext, options: WebGLSetTextureOptions): void {
  const {dimension, width, height, depth = 0, mipLevel = 0} = options;
  const {glInternalFormat, glFormat, glType, compressed} = options;
  const glTarget = getWebGLCubeFaceTarget(options.glTarget, dimension, depth);

  switch (dimension) {
    case '2d-array':
    case '3d':
      if (compressed) {
        // prettier-ignore
        gl.compressedTexImage3D(glTarget, mipLevel, glInternalFormat, width, height, depth, BORDER, null);
      } else {
        // prettier-ignore
        gl.texImage3D( glTarget, mipLevel, glInternalFormat, width, height, depth, BORDER, glFormat, glType, null);
      }
      break;

    case '2d':
    case 'cube':
      if (compressed) {
        // prettier-ignore
        gl.compressedTexImage2D(glTarget, mipLevel, glInternalFormat, width, height, BORDER, null);
      } else {
        // prettier-ignore
        gl.texImage2D(glTarget, mipLevel, glInternalFormat, width, height, BORDER, glFormat, glType, null);
      }
      break;

    default:
      throw new Error(dimension);
  }
}
  */
/**
 * Set a texture mip level to the contents of an external image.
 * Wrapper for the messy WebGL texture API
 * @note Corresponds to WebGPU device.queue.copyExternalImageToTexture()
 *
export function setMipLevelFromExternalImage(
  gl: WebGL2RenderingContext,
  image: ExternalImage,
  options: WebGLSetTextureOptions
): void {
  const {dimension, width, height, depth = 0, level = 0} = options;
  const {glInternalFormat, glType} = options;

  const glTarget = getWebGLCubeFaceTarget(options.glTarget, dimension, depth);

  // TODO - we can't change texture width (due to WebGPU limitations) -
  // and the width/heigh of an external image is implicit, so why do we need to extract it?
  // So what width height do we supply? The image size or the texture size?
  // const {width, height} = Texture.getExternalImageSize(image);

  switch (dimension) {
    case '2d-array':
    case '3d':
      // prettier-ignore
      gl.texImage3D(glTarget, level, glInternalFormat, width, height, depth, BORDER, glInternalFormat, glType, image);
      break;

    case '2d':
    case 'cube':
      // prettier-ignore
      gl.texImage2D(glTarget, level, glInternalFormat, width, height, BORDER, glInternalFormat, glType, image);
      break;

    default:
      throw new Error(dimension);
  }
}

/**
 * Set a texture mip level from CPU memory
 * Wrapper for the messy WebGL texture API
 * @note Not available (directly) in WebGPU
 *
export function setMipLevelFromTypedArray(
  gl: WebGL2RenderingContext,
  data: TypedArray,
  parameters: {},
  options: {
    dimension: '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d';
    height: number;
    width: number;
    depth?: number;
    level?: number;
    offset?: number;
    glTarget: GLTextureTarget;
    glInternalFormat: GL;
    glFormat: GL;
    glType: GL;
    compressed?: boolean;
  }
): void {
  const {dimension, width, height, depth = 0, level = 0, offset = 0} = options;
  const {glInternalFormat, glFormat, glType, compressed} = options;

  const glTarget = getWebGLCubeFaceTarget(options.glTarget, dimension, depth);

  withGLParameters(gl, parameters, () => {
    switch (dimension) {
      case '2d-array':
      case '3d':
        if (compressed) {
          // prettier-ignore
          gl.compressedTexImage3D(glTarget, level, glInternalFormat, width, height, depth, BORDER, data);
        } else {
          // prettier-ignore
          gl.texImage3D( glTarget, level, glInternalFormat, width, height, depth, BORDER, glFormat, glType, data);
        }
        break;

      case '2d':
        if (compressed) {
          // prettier-ignore
          gl.compressedTexImage2D(glTarget, level, glInternalFormat, width, height, BORDER, data);
        } else {
          // prettier-ignore
          gl.texImage2D( glTarget, level, glInternalFormat, width, height, BORDER, glFormat, glType, data, offset);
        }
        break;

      default:
        throw new Error(dimension);
    }
  });
}

/**
   * Set a texture level from CPU memory
   * @note Not available (directly) in WebGPU
  _setMipLevelFromTypedArray(
    depth: number,
    level: number,
    data: TextureLevelData,
    offset = 0,
    parameters
  ): void {
    withGLParameters(this.gl, parameters, () => {
      switch (this.props.dimension) {
        case '2d-array':
        case '3d':
          if (this.compressed) {
            // prettier-ignore
            this.device.gl.compressedTexImage3D(this.glTarget, level, this.glInternalFormat, data.width, data.height, depth, BORDER, data.data);
          } else {
            // prettier-ignore
            this.gl.texImage3D( this.glTarget, level, this.glInternalFormat, this.width, this.height, depth, BORDER, this.glFormat, this.glType, data.data);
          }
          break;

        case '2d':
          if (this.compressed) {
            // prettier-ignore
            this.device.gl.compressedTexImage2D(this.glTarget, level, this.glInternalFormat, data.width, data.height, BORDER, data.data);
          } else {
            // prettier-ignore
            this.device.gl.texImage2D( this.glTarget, level, this.glInternalFormat, this.width, this.height, BORDER, this.glFormat, this.glType, data.data, offset);
          }
          break;

        default:
          throw new Error(this.props.dimension);
      }
    });
  }

 * Set a texture level from a GPU buffer
 *
export function setMipLevelFromGPUBuffer(
  gl: WebGL2RenderingContext,
  buffer: Buffer,
  options: WebGLSetTextureOptions
): void {
  const {dimension, width, height, depth = 0, level = 0, byteOffset = 0} = options;
  const {glInternalFormat, glFormat, glType, compressed} = options;
  const glTarget = getWebGLCubeFaceTarget(options.glTarget, dimension, depth);

  const webglBuffer = buffer as WEBGLBuffer;
  const imageSize = buffer.byteLength;

  // In WebGL the source buffer is not a parameter. Instead it needs to be bound to a special bind point
  gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, webglBuffer.handle);

  switch (dimension) {
    case '2d-array':
    case '3d':
      if (compressed) {
        // prettier-ignore
        gl.compressedTexImage3D(glTarget, level, glInternalFormat, width, height, depth, BORDER, imageSize, byteOffset);
      } else {
        // prettier-ignore
        gl.texImage3D(glTarget, level, glInternalFormat, width, height, depth, BORDER, glFormat, glType, byteOffset);
      }
      break;

    case '2d':
      if (compressed) {
        // prettier-ignore
        gl.compressedTexImage2D(glTarget, level, glInternalFormat, width, height, BORDER, imageSize, byteOffset);
      } else {
        // prettier-ignore
        gl.texImage2D(glTarget, level, glInternalFormat, width, height, BORDER, glFormat, glType, byteOffset);
      }
      break;

    default:
      throw new Error(dimension);
  }

  gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, null);
}
*/
export type ReadPixelsToArrayOptions = {
    sourceX?: number;
    sourceY?: number;
    sourceFormat?: number;
    sourceAttachment?: number;
    target?: Uint8Array | Uint16Array | Float32Array;
    sourceWidth?: number;
    sourceHeight?: number;
    sourceDepth?: number;
    sourceType?: number;
};
export type ReadPixelsToBufferOptions = {
    sourceX?: number;
    sourceY?: number;
    sourceFormat?: number;
    target?: Buffer;
    targetByteOffset?: number;
    sourceWidth?: number;
    sourceHeight?: number;
    sourceType?: number;
};
/**
 * Copies data from a type  or a Texture object into ArrayBuffer object.
 * App can provide targetPixelArray or have it auto allocated by this method
 *  newly allocated by this method unless provided by app.
 * @deprecated Use CommandEncoder.copyTextureToBuffer and Buffer.read
 * @note Slow requires roundtrip to GPU
 *
 * @param source
 * @param options
 * @returns pixel array,
 */
export declare function readPixelsToArray(source: Framebuffer | Texture, options?: ReadPixelsToArrayOptions): Uint8Array | Uint16Array | Float32Array;
/**
 * Copies data from a Framebuffer or a Texture object into a Buffer object.
 * NOTE: doesn't wait for copy to be complete, it programs GPU to perform a DMA transffer.
 * @deprecated Use CommandEncoder
 * @param source
 * @param options
 */
export declare function readPixelsToBuffer(source: Framebuffer | Texture, options?: ReadPixelsToBufferOptions): WEBGLBuffer;
/**
 * Copy a rectangle from a Framebuffer or Texture object into a texture (at an offset)
 * @deprecated Use CommandEncoder
 */
export declare function copyToTexture(sourceTexture: Framebuffer | Texture, destinationTexture: Texture | GL, options?: {
    sourceX?: number;
    sourceY?: number;
    targetX?: number;
    targetY?: number;
    targetZ?: number;
    targetMipmaplevel?: number;
    targetInternalFormat?: number;
    width?: number;
    height?: number;
}): Texture;
/**
 * Wraps a given texture into a framebuffer object, that can be further used
 * to read data from the texture object.
 */
export declare function toFramebuffer(texture: Texture, props?: FramebufferProps): WEBGLFramebuffer;
//# sourceMappingURL=webgl-texture-utils.d.ts.map