/**
 * image-processor.ts - modern PNG/GIF reader replacement for tng.js
 * Copyright (c) 2025, Contributors (MIT License).
 * Replaces the legacy vendor/tng.js with modern npm dependencies
 */

import { PNG } from 'pngjs';
import * as fs from 'fs';

interface ImageOptions {
  colors?: any;
  width?: number;
  height?: number;
  scale?: number;
  ascii?: boolean;
  speed?: number;
  filename?: string;
}

interface CellMap {
  [row: number]: any[];
  length: number;
}

interface ImageResult {
  cellmap: CellMap;
  frames?: any[] | undefined;
  play?(callback: (bmp: any, cellmap: CellMap) => void): any;
  pause?(): any;
  stop?(): any;
  renderElement?(cellmap: CellMap, element: any): void;
}

class ModernImageProcessor {
  private options: ImageOptions;
  private colors: any;
  private png: PNG | null = null;

  constructor(file: string | Buffer, options: ImageOptions = {}) {
    this.options = options;
    this.colors = options.colors || require('./colors');

    this.loadImage(file);
  }

  private loadImage(file: string | Buffer): void {
    let buf: Buffer;

    if (Buffer.isBuffer(file)) {
      buf = file;
    } else {
      buf = fs.readFileSync(file);
    }

    // Detect format and load image
    if (buf.readUInt32BE(0) === 0x89504e47) {
      // PNG format
      this.png = PNG.sync.read(buf);
    } else if (buf.slice(0, 3).toString('ascii') === 'GIF') {
      throw new Error('GIF format not yet supported in modern implementation');
    } else if (buf.readUInt16BE(0) === 0xffd8) {
      throw new Error('JPEG format not yet supported in modern implementation');
    } else {
      throw new Error('Unsupported image format');
    }
  }

  createCellmap(): CellMap {
    if (!this.png) {
      throw new Error('No image loaded');
    }

    const bmp = this.pngToBitmap();
    return this.bitmapToCellmap(bmp);
  }

  private pngToBitmap(): number[][][] {
    if (!this.png) {
      throw new Error('No PNG loaded');
    }

    const { width, height, data } = this.png;
    const bmp: number[][][] = [];

    for (let y = 0; y < height; y++) {
      const row: number[][] = [];
      for (let x = 0; x < width; x++) {
        const idx = (width * y + x) << 2;
        const r = data[idx] ?? 0;
        const g = data[idx + 1] ?? 0;
        const b = data[idx + 2] ?? 0;
        const a = data[idx + 3] ?? 255;
        row.push([r, g, b, a]);
      }
      bmp.push(row);
    }

    return bmp;
  }

  private bitmapToCellmap(bmp: number[][][]): CellMap {
    const cellmap: any[] = [];
    const scale = this.options.scale || 0.2;
    const height = bmp.length;
    const width = bmp[0]?.length ?? 0;

    let cmwidth = this.options.width || 0;
    let cmheight = this.options.height || 0;

    let actualScale = scale;
    if (cmwidth) {
      actualScale = cmwidth / width;
    } else if (cmheight) {
      actualScale = cmheight / height;
    }

    if (!cmheight) {
      cmheight = Math.round(height * actualScale);
    }

    if (!cmwidth) {
      cmwidth = Math.round(width * actualScale);
    }

    const ys = height / cmheight;
    const xs = width / cmwidth;

    for (let y = 0; y < bmp.length; y += ys) {
      const line: any[] = [];
      const yy = Math.round(y);
      if (!bmp[yy]) break;

      for (let x = 0; x < bmp[yy].length; x += xs) {
        const xx = Math.round(x);
        if (!bmp[yy][xx]) break;

        // Convert RGBA to terminal cell representation
        const pixel = bmp[yy][xx];
        const cell = this.pixelToCell(pixel);
        line.push(cell);
      }
      cellmap.push(line);
    }

    // Add length property for compatibility
    Object.defineProperty(cellmap, 'length', {
      value: cellmap.length,
      writable: true,
      enumerable: false,
      configurable: true,
    });

    return cellmap as CellMap;
  }

  private pixelToCell(pixel: number[]): any {
    // Convert RGBA pixel to terminal cell representation
    // This is a simplified version - the original tng.js had complex color matching
    const [r, g, b, a] = pixel;

    if ((a ?? 255) < 128) {
      // Transparent pixel
      return { char: ' ', fg: 'default', bg: 'default' };
    }

    // Use the colors module to find the best matching color
    if (this.colors?.match) {
      const color = this.colors.match(r ?? 0, g ?? 0, b ?? 0);
      return {
        char: '█', // Full block character
        fg: color,
        bg: 'default',
      };
    }

    // Fallback: basic grayscale
    const gray = Math.round(((r ?? 0) + (g ?? 0) + (b ?? 0)) / 3);
    const char = gray > 128 ? '█' : gray > 64 ? '▓' : gray > 32 ? '▒' : '░';

    return {
      char: char,
      fg: 'white',
      bg: 'default',
    };
  }
}

// Synchronous wrapper to match tng.js API
function processImage(
  file: string | Buffer,
  options: ImageOptions = {}
): ImageResult {
  const processor = new ModernImageProcessor(file, options);

  const result: ImageResult = {
    cellmap: processor.createCellmap(),
    // GIF support would go here
  };

  return result;
}

export = processImage;
