/**
 * @file 图像处理工具类
 * @description 提供图像预处理功能，用于提高OCR识别率
 * @module ImageProcessor
 * @version 1.4.0
 */

import imageCompression from "browser-image-compression"
import { Point, Rect, ImageProcessingOptions } from './types';
import { CanvasPool } from './canvas-pool';
import { EdgeDetector } from './edge-detector';

/**
 * 图像处理器配置选项
 */
export interface ImageProcessorOptions {
  brightness?: number // 亮度调整，范围 -100 到 100
  contrast?: number // 对比度调整，范围 -100 到 100
  grayscale?: boolean // 是否转换为灰度图
  invert?: boolean // 是否反转颜色
  blur?: number // 模糊程度 (0-10)
  sharpen?: boolean // 是否锐化
  /** 是否使用 Canvas 对象池（减少内存分配） */
  usePool?: boolean;
}

/**
 * 图像压缩选项
 */
export interface ImageCompressionOptions {
  maxSizeMB?: number // 图片最大大小，MB
  maxWidthOrHeight?: number // 图片最大宽度或高度
  useWebWorker?: boolean // 是否使用Web Worker处理
  maxIteration?: number // 最大压缩迭代次数
  quality?: number // 输出质量 (0-1)
  fileType?: string // 输出文件类型 ('image/jpeg', 'image/png' 等)
}

/**
 * 图像处理工具类
 *
 * 提供各种图像处理功能，用于优化识别效果
 */
export class ImageProcessor {
  /**
   * 将ImageData转换为Canvas元素
   *
   * @param {ImageData} imageData - 要转换的图像数据
   * @returns {HTMLCanvasElement} 包含图像的Canvas元素
   */
  static imageDataToCanvas(imageData: ImageData, usePool: boolean = true): HTMLCanvasElement {
    let canvas: HTMLCanvasElement;
    let context: CanvasRenderingContext2D;

    if (usePool) {
      ({ canvas, context } = CanvasPool.getInstance().acquire(imageData.width, imageData.height));
    } else {
      canvas = document.createElement("canvas");
      canvas.width = imageData.width;
      canvas.height = imageData.height;
      context = canvas.getContext("2d")!;
    }

    context.putImageData(imageData, 0, 0);

    if (usePool) {
      // 立即释放回池中，用户保留 canvas 引用即可
      CanvasPool.getInstance().release(canvas);
    }

    return canvas;
  }

  /**
   * 将Canvas转换为ImageData
   *
   * @param {HTMLCanvasElement} canvas - 要转换的Canvas元素
   * @returns {ImageData|null} Canvas的图像数据，如果获取失败则返回null
   */
  static canvasToImageData(canvas: HTMLCanvasElement): ImageData | null {
    const ctx = canvas.getContext("2d")
    return ctx ? ctx.getImageData(0, 0, canvas.width, canvas.height) : null
  }

  /**
   * 调整图像亮度和对比度
   *
   * @param imageData 原始图像数据
   * @param brightness 亮度调整值 (-100到100)
   * @param contrast 对比度调整值 (-100到100)
   * @returns 处理后的图像数据
   */
  static adjustBrightnessContrast(
    imageData: ImageData,
    brightness: number = 0,
    contrast: number = 0
  ): ImageData {
    // 将亮度和对比度范围限制在 -100 到 100 之间
    brightness = Math.max(-100, Math.min(100, brightness))
    contrast = Math.max(-100, Math.min(100, contrast))

    // 将范围转换为适合计算的值
    const factor = (259 * (contrast + 255)) / (255 * (259 - contrast))
    const briAdjust = (brightness / 100) * 255

    const data = imageData.data
    const length = data.length

    for (let i = 0; i < length; i += 4) {
      // 分别处理 RGB 三个通道
      for (let j = 0; j < 3; j++) {
        // 应用亮度和对比度调整公式
        const newValue = factor * (data[i + j] + briAdjust - 128) + 128
        data[i + j] = Math.max(0, Math.min(255, newValue))
      }
      // Alpha 通道保持不变
    }

    return imageData
  }

  /**
   * 将图像转换为灰度图（返回新 ImageData，不修改原图）
   *
   * @param imageData 原始图像数据
   * @returns 灰度图像数据（新对象）
   */
  static toGrayscale(imageData: ImageData): ImageData {
    const srcData = imageData.data
    const length = srcData.length
    // 创建新数组，避免修改原图
    const destData = new Uint8ClampedArray(srcData)

    for (let i = 0; i < length; i += 4) {
      // 使用加权平均法将 RGB 转换为灰度值
      const gray = srcData[i] * 0.3 + srcData[i + 1] * 0.59 + srcData[i + 2] * 0.11
      destData[i] = destData[i + 1] = destData[i + 2] = gray
      // Alpha 通道保持不变
      destData[i + 3] = srcData[i + 3]
    }

    return new ImageData(destData, imageData.width, imageData.height)
  }

  /**
   * 锐化图像
   *
   * @param imageData 原始图像数据
   * @param amount 锐化程度，默认为2
   * @returns 锐化后的图像数据
   */
  static sharpen(imageData: ImageData, amount: number = 2): ImageData {
    if (!imageData || !imageData.data) return imageData

    const width = imageData.width
    const height = imageData.height
    const data = imageData.data

    const outputData = new Uint8ClampedArray(data.length)

    // 锐化卷积核
    const kernel = [
      0,
      -amount,
      0,
      -amount,
      1 + 4 * amount,
      -amount,
      0,
      -amount,
      0,
    ]

    // 应用卷积
    for (let y = 1; y < height - 1; y++) {
      for (let x = 1; x < width - 1; x++) {
        const pos = (y * width + x) * 4

        // 对每个通道应用卷积
        for (let c = 0; c < 3; c++) {
          let val = 0
          for (let ky = -1; ky <= 1; ky++) {
            for (let kx = -1; kx <= 1; kx++) {
              const kernelPos = (ky + 1) * 3 + (kx + 1)
              const dataPos = ((y + ky) * width + (x + kx)) * 4 + c
              val += data[dataPos] * kernel[kernelPos]
            }
          }
          outputData[pos + c] = Math.max(0, Math.min(255, val))
        }
        outputData[pos + 3] = data[pos + 3] // 保持透明度不变
      }
    }

    // 处理边缘像素（仅遍历四条边，而非全图 O(width×height) → O(width+height)）
    // 上边 + 下边
    for (let x = 0; x < width; x++) {
      const topPos = x * 4
      const bottomPos = ((height - 1) * width + x) * 4
      outputData[topPos] = data[topPos]; outputData[topPos + 1] = data[topPos + 1]; outputData[topPos + 2] = data[topPos + 2]; outputData[topPos + 3] = data[topPos + 3]
      outputData[bottomPos] = data[bottomPos]; outputData[bottomPos + 1] = data[bottomPos + 1]; outputData[bottomPos + 2] = data[bottomPos + 2]; outputData[bottomPos + 3] = data[bottomPos + 3]
    }
    // 左边 + 右边（排除四角，它们已在上下一行处理）
    for (let y = 1; y < height - 1; y++) {
      const leftPos = y * width * 4
      const rightPos = (y * width + width - 1) * 4
      outputData[leftPos] = data[leftPos]; outputData[leftPos + 1] = data[leftPos + 1]; outputData[leftPos + 2] = data[leftPos + 2]; outputData[leftPos + 3] = data[leftPos + 3]
      outputData[rightPos] = data[rightPos]; outputData[rightPos + 1] = data[rightPos + 1]; outputData[rightPos + 2] = data[rightPos + 2]; outputData[rightPos + 3] = data[rightPos + 3]
    }

    // 创建新的ImageData对象
    return new ImageData(outputData, width, height)
  }

  /**
   * 对图像应用阈值操作，增强对比度（二值化）
   *
   * @param imageData 原始图像数据
   * @param threshold 阈值 (0-255)
   * @returns 处理后的图像数据（新对象，不修改原图）
   */
  static threshold(imageData: ImageData, threshold: number = 128): ImageData {
    // 先转换为灰度图（返回新 ImageData，不修改原图）
    const grayscaleImage = this.toGrayscale(imageData)
    const srcData = grayscaleImage.data
    const length = srcData.length
    // 创建新数组存储二值化结果
    const destData = new Uint8ClampedArray(length)

    for (let i = 0; i < length; i += 4) {
      // 二值化处理
      const value = srcData[i] < threshold ? 0 : 255
      destData[i] = destData[i + 1] = destData[i + 2] = value
      destData[i + 3] = srcData[i + 3] // 保持透明度
    }

    return new ImageData(destData, grayscaleImage.width, grayscaleImage.height)
  }

  /**
   * 将图像转换为黑白图像（二值化，使用OTSU自动阈值）
   *
   * @param imageData 原始图像数据
   * @returns 二值化后的图像数据（新对象，不修改原图）
   */
  static toBinaryImage(imageData: ImageData): ImageData {
    // 先转换为灰度图（返回新 ImageData，不修改原图）
    const grayscaleImage = this.toGrayscale(imageData)

    // 使用OTSU算法自动确定阈值
    const threshold = this.getOtsuThreshold(grayscaleImage)

    // 直接对灰度图进行二值化，避免再次调用 toGrayscale
    const srcData = grayscaleImage.data
    const length = srcData.length
    const destData = new Uint8ClampedArray(length)

    for (let i = 0; i < length; i += 4) {
      const value = srcData[i] < threshold ? 0 : 255
      destData[i] = destData[i + 1] = destData[i + 2] = value
      destData[i + 3] = srcData[i + 3] // 保持透明度
    }

    return new ImageData(destData, grayscaleImage.width, grayscaleImage.height)
  }

  /**
   * 使用OTSU算法计算最佳阈值
   *
   * @param imageData 灰度图像数据
   * @returns 最佳阈值
   */
  private static getOtsuThreshold(imageData: ImageData): number {
    const data = imageData.data
    // 使用 Uint8Array 替代 Array<number>，避免 boxing 开销，提升直方图统计性能
    const histogram = new Uint32Array(256)

    // 统计灰度直方图（每4字节取R通道，即灰度值）
    for (let i = 0; i < data.length; i += 4) {
      histogram[data[i]]++
    }

    const total = imageData.width * imageData.height
    let sum = 0

    // 计算总灰度值和
    for (let i = 0; i < 256; i++) {
      sum += i * histogram[i]
    }

    let sumB = 0
    let wB = 0
    let wF = 0
    let maxVariance = 0
    let threshold = 0

    // 遍历所有可能的阈值，找到最大类间方差
    for (let t = 0; t < 256; t++) {
      wB += histogram[t] // 背景权重
      if (wB === 0) continue

      wF = total - wB // 前景权重
      if (wF === 0) break

      sumB += t * histogram[t]

      const mB = sumB / wB // 背景平均灰度
      const mF = (sum - sumB) / wF // 前景平均灰度

      // 计算类间方差
      const variance = wB * wF * (mB - mF) * (mB - mF)

      if (variance > maxVariance) {
        maxVariance = variance
        threshold = t
      }
    }

    return threshold
  }

  /**
   * 批量应用图像处理
   *
   * @param imageData 原始图像数据
   * @param options 处理选项
   * @returns 处理后的图像数据
   */
  static batchProcess(
    imageData: ImageData,
    options: ImageProcessorOptions
  ): ImageData {
    let processedImage = new ImageData(
      new Uint8ClampedArray(imageData.data),
      imageData.width,
      imageData.height
    )

    // 应用亮度和对比度调整
    if (options.brightness !== undefined || options.contrast !== undefined) {
      processedImage = this.adjustBrightnessContrast(
        processedImage,
        options.brightness || 0,
        options.contrast || 0
      )
    }

    // 应用灰度转换
    if (options.grayscale) {
      processedImage = this.toGrayscale(processedImage)
    }

    // 应用锐化
    if (options.sharpen) {
      processedImage = this.sharpen(processedImage)
    }

    // 应用颜色反转
    if (options.invert) {
      const data = processedImage.data
      for (let i = 0; i < data.length; i += 4) {
        // 反转RGB值
        data[i] = 255 - data[i]
        data[i + 1] = 255 - data[i + 1]
        data[i + 2] = 255 - data[i + 2]
        // Alpha通道保持不变
      }
    }

    return processedImage
  }

  /**
   * 压缩图片文件
   *
   * @param file 图片文件
   * @param options 压缩选项
   * @returns Promise<File> 压缩后的文件
   */
  static async compressImage(
    file: File,
    options?: ImageCompressionOptions
  ): Promise<File> {
    const defaultOptions = {
      maxSizeMB: 1,
      maxWidthOrHeight: 1920,
      useWebWorker: true,
      quality: 0.8,
      fileType: file.type || "image/jpeg",
    }

    const compressOptions = { ...defaultOptions, ...options }

    try {
      return await imageCompression(file, compressOptions)
    } catch (error) {
      console.error("图片压缩失败:", error)
      return file // 如果压缩失败，返回原始文件
    }
  }

  /**
   * 从图片文件创建ImageData
   *
   * @param file 图片文件
   * @returns Promise<ImageData>
   */
  static async createImageDataFromFile(file: File): Promise<ImageData> {
    return new Promise((resolve, reject) => {
      try {
        const img = new Image()
        const url = URL.createObjectURL(file)

        img.onload = () => {
          try {
            // 使用 Canvas 池获取 canvas
            const { canvas, context } = CanvasPool.getInstance().acquire(img.width, img.height);

            // 绘制图片到canvas
            context.drawImage(img, 0, 0);

            // 获取图像数据
            const imageData = context.getImageData(0, 0, canvas.width, canvas.height);

            // 释放回池
            CanvasPool.getInstance().release(canvas);

            // 释放资源
            URL.revokeObjectURL(url);

            resolve(imageData);
          } catch (e) {
            URL.revokeObjectURL(url);
            reject(e);
          }
        };

        img.onerror = () => {
          URL.revokeObjectURL(url);
          reject(new Error("图片加载失败"));
        };

        img.src = url;
      } catch (error) {
        reject(error);
      }
    });
  }

  /**
   * 将ImageData转换为File对象
   *
   * @param imageData ImageData对象
   * @param fileName 输出文件名
   * @param fileType 输出文件类型
   * @param quality 图片质量 (0-1)
   * @returns Promise<File>
   */
  static async imageDataToFile(
    imageData: ImageData,
    fileName: string = "image.jpg",
    fileType: string = "image/jpeg",
    quality: number = 0.8
  ): Promise<File> {
    return new Promise((resolve, reject) => {
      try {
        // 使用 Canvas 池
        const { canvas, context } = CanvasPool.getInstance().acquire(imageData.width, imageData.height);

        context.putImageData(imageData, 0, 0);

        canvas.toBlob(
          (blob) => {
            // 释放回池
            CanvasPool.getInstance().release(canvas);

            if (!blob) {
              reject(new Error("无法创建图片Blob"));
              return;
            }
            const file = new File([blob], fileName, { type: fileType });
            resolve(file)
          },
          fileType,
          quality
        )
      } catch (error) {
        reject(error)
      }
    })
  }

  /**
   * 将图像调整到指定大小
   * @param image 输入图像
   * @param maxWidth 最大宽度
   * @param maxHeight 最大高度
   * @param keepAspectRatio 是否保持宽高比
   * @returns 调整后的图像
   */
  public static resizeImage(
    image: ImageData | HTMLImageElement | HTMLCanvasElement,
    maxWidth: number,
    maxHeight: number,
    keepAspectRatio: boolean = true
  ): ImageData {
    // 创建canvas元素
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    
    if (!ctx) {
      throw new Error('无法创建Canvas上下文');
    }
    
    // 获取图像尺寸
    let width: number;
    let height: number;
    
    if (image instanceof ImageData) {
      width = image.width;
      height = image.height;
    } else {
      width = image.width;
      height = image.height;
    }
    
    // 计算调整后的尺寸
    let newWidth = width;
    let newHeight = height;
    
    if (keepAspectRatio) {
      if (width > height) {
        if (width > maxWidth) {
          newHeight = Math.round(height * (maxWidth / width));
          newWidth = maxWidth;
        }
      } else {
        if (height > maxHeight) {
          newWidth = Math.round(width * (maxHeight / height));
          newHeight = maxHeight;
        }
      }
    } else {
      newWidth = Math.min(width, maxWidth);
      newHeight = Math.min(height, maxHeight);
    }
    
    // 设置canvas尺寸
    canvas.width = newWidth;
    canvas.height = newHeight;
    
    // 绘制调整后的图像
    if (image instanceof ImageData) {
      // 创建临时canvas存储ImageData
      const tempCanvas = document.createElement('canvas');
      const tempCtx = tempCanvas.getContext('2d');
      
    if (!tempCtx) {
        throw new Error('无法创建临时Canvas上下文');
    }

      tempCanvas.width = image.width;
      tempCanvas.height = image.height;
      tempCtx.putImageData(image, 0, 0);
      
      // 绘制调整后的图像
      ctx.drawImage(tempCanvas, 0, 0, width, height, 0, 0, newWidth, newHeight);
    } else {
      ctx.drawImage(image, 0, 0, width, height, 0, 0, newWidth, newHeight);
    }

    // 返回调整后的ImageData
    return ctx.getImageData(0, 0, newWidth, newHeight);
  }
  
  /**
   * @deprecated 请使用 EdgeDetector.detectEdges()
   */
  static detectEdges(imageData: ImageData, threshold: number = 30): ImageData {
    return EdgeDetector.detectEdges(imageData, threshold);
  }
  
  /**
   * @deprecated 请使用 EdgeDetector.cannyEdgeDetection()
   */
  static cannyEdgeDetection(
    imageData: ImageData, 
    lowThreshold: number = 20, 
    highThreshold: number = 50
  ): ImageData {
    return EdgeDetector.cannyEdgeDetection(imageData, lowThreshold, highThreshold);
  }
}
