import { Rect, Point, Polygon } from './image-types';

export class ImageGeometry {
  /**
   * Find rectangular regions based on edge strength
   */
  static findRectangularRegions(edges: Float32Array, width: number, height: number, threshold: number = 50): Polygon[] {
    const regions: Polygon[] = [];
    const visited = new Uint8Array(width * height);

    for (let y = 0; y < height; y++) {
      for (let x = 0; x < width; x++) {
        if (edges[y * width + x] > threshold && !visited[y * width + x]) {
          const region = this.expandRegion(edges, visited, x, y, width, height, threshold);
          if (region) {
            const aspectRatio = region.bbox.width / region.bbox.height;
            // Card aspect ratio is roughly 0.6-0.8 (standard) or 1.2-1.6 (flipped)
            const isValidRatio = (aspectRatio > 0.5 && aspectRatio < 0.9) || (aspectRatio > 1.1 && aspectRatio < 1.8);
            const isLargeEnough = region.bbox.width > 40 && region.bbox.height > 60;

            if (isValidRatio && isLargeEnough) {
              regions.push(region);
            }
          }
        }
      }
    }
    return regions;
  }

  private static expandRegion(edges: Float32Array, visited: Uint8Array, startX: number, startY: number, width: number, height: number, threshold: number): Polygon | null {
    let minX = startX, maxX = startX;
    let minY = startY, maxY = startY;
    const points: Point[] = [];

    const stack = [[startX, startY]];
    visited[startY * width + startX] = 1;

    while (stack.length > 0) {
      const [cx, cy] = stack.pop()!;

      minX = Math.min(minX, cx);
      maxX = Math.max(maxX, cx);
      minY = Math.min(minY, cy);
      maxY = Math.max(maxY, cy);
      points.push({ x: cx, y: cy });

      const neighbors = [
        [cx + 1, cy], [cx - 1, cy], [cx, cy + 1], [cx, cy - 1]
      ];

      for (const [nx, ny] of neighbors) {
        if (nx >= 0 && nx < width && ny >= 0 && ny < height &&
            !visited[ny * width + nx] && edges[ny * width + nx] > threshold) {
          visited[ny * width + nx] = 1;
          stack.push([nx, ny]);
        }
      }
    }

    const w = maxX - minX;
    const h = maxY - minY;

    if (w < 50 || h < 80) return null;

    return {
      points,
      bbox: { x: minX, y: minY, width: w, height: h }
    };
  }

  /**
   * Find the 4 corners of a point set that most closely resemble a rectangle
   */
  static findCorners(points: Point[]): Point[] {
    if (points.length < 3) return [];

    // Monotone Chain algorithm for Convex Hull
    const sorted = [...points].sort((a, b) => a.x !== b.x ? a.x - b.x : a.y - b.y);
    
    const crossProduct = (a: Point, b: Point, c: Point) => 
      (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);

    const buildHull = (pts: Point[]) => {
      const hull: Point[] = [];
      for (const p of pts) {
        while (hull.length >= 2 && crossProduct(hull[hull.length - 2], hull[hull.length - 1], p) <= 0) {
          hull.pop();
        }
        hull.push(p);
      }
      return hull;
    };

    const lower = buildHull(sorted);
    const upper = buildHull(sorted.reverse());
    
    lower.pop();
    upper.pop();
    const hull = lower.concat(upper);

    if (hull.length < 4) return [];

    // Find 4 points on hull that maximize the rectangle area
    // Simplification: find points furthest apart along axes and their mid-points
    let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
    let pMinX: Point | null = null, pMaxX: Point | null = null, pMinY: Point | null = null, pMaxY: Point | null = null;

    for (const p of hull) {
      if (p.x < minX) { minX = p.x; pMinX = p; }
      if (p.x > maxX) { maxX = p.x; pMaxX = p; }
      if (p.y < minY) { minY = p.y; pMinY = p; }
      if (p.y > maxY) { maxY = p.y; pMaxY = p; }
    }

    const extremePoints = [pMinX, pMaxX, pMinY, pMaxY].filter((p): p is Point => p !== null);
    
    if (extremePoints.length >= 4) {
      return extremePoints.slice(0, 4);
    }

    // Fallback to previous simple logic if hull is too small or degenerate
    return points.slice(0, 4);
  }

  static warpPerspective(sourceCanvas: HTMLCanvasElement, srcCorners: Point[], destWidth: number, destHeight: number): HTMLCanvasElement {
    const destCanvas = document.createElement('canvas');
    destCanvas.width = destWidth;
    destCanvas.height = destHeight;
    const destCtx = destCanvas.getContext('2d');
    if (!destCtx) return destCanvas;

    const srcCtx = sourceCanvas.getContext('2d');
    if (!srcCtx) return destCanvas;

    const srcData = srcCtx.getImageData(0, 0, sourceCanvas.width, sourceCanvas.height).data;
    const destData = destCtx.createImageData(destWidth, destHeight);

    for (let y = 0; y < destHeight; y++) {
      for (let x = 0; x < destWidth; x++) {
        const u = x / destWidth;
        const v = y / destHeight;

        const srcX = (1 - u) * ((1 - v) * srcCorners[0].x + v * srcCorners[3].x) +
                     u * ((1 - v) * srcCorners[1].x + v * srcCorners[2].x);
        const srcY = (1 - u) * ((1 - v) * srcCorners[0].y + v * srcCorners[3].y) +
                     u * ((1 - v) * srcCorners[1].y + v * srcCorners[2].y);

        const sx = Math.floor(srcX);
        const sy = Math.floor(srcY);

        if (sx >= 0 && sx < sourceCanvas.width && sy >= 0 && sy < sourceCanvas.height) {
          const srcIdx = (sy * sourceCanvas.width + sx) * 4;
          const destIdx = (y * destWidth + x) * 4;
          destData.data[destIdx] = srcData[srcIdx];
          destData.data[destIdx + 1] = srcData[srcIdx + 1];
          destData.data[destIdx + 2] = srcData[srcIdx + 2];
          destData.data[destIdx + 3] = srcData[srcIdx + 3];
        }
      }
    }

    destCtx.putImageData(destData, 0, 0);
    return destCanvas;
  }
}
