"""
Image Primitives Library

Basic image operations: dimensions, pixels, resize, crop, paste.
"""
import numpy as np
import cv2


def prim_width(img):
    if isinstance(img, (list, tuple)):
        raise TypeError(f"image:width expects an image array, got {type(img).__name__} with {len(img)} elements")
    return img.shape[1]


def prim_height(img):
    if isinstance(img, (list, tuple)):
        import sys
        print(f"DEBUG image:height got list: {img[:3]}... (types: {[type(x).__name__ for x in img[:3]]})", file=sys.stderr)
        raise TypeError(f"image:height expects an image array, got {type(img).__name__} with {len(img)} elements: {img}")
    return img.shape[0]


def prim_make_image(w, h, color=None):
    """Create a new image filled with color (default black)."""
    if color is None:
        color = [0, 0, 0]
    img = np.zeros((h, w, 3), dtype=np.uint8)
    img[:] = color
    return img


def prim_copy(img):
    return img.copy()


def prim_pixel(img, x, y):
    """Get pixel color at (x, y) as [r, g, b]."""
    h, w = img.shape[:2]
    if 0 <= x < w and 0 <= y < h:
        return list(img[int(y), int(x)])
    return [0, 0, 0]


def prim_set_pixel(img, x, y, color):
    """Set pixel at (x, y) to color, returns modified image."""
    result = img.copy()
    h, w = result.shape[:2]
    if 0 <= x < w and 0 <= y < h:
        result[int(y), int(x)] = color
    return result


def prim_sample(img, x, y):
    """Bilinear sample at float coordinates, returns [r, g, b] as floats."""
    h, w = img.shape[:2]
    x = max(0, min(w - 1.001, x))
    y = max(0, min(h - 1.001, y))

    x0, y0 = int(x), int(y)
    x1, y1 = min(x0 + 1, w - 1), min(y0 + 1, h - 1)
    fx, fy = x - x0, y - y0

    c00 = img[y0, x0].astype(float)
    c10 = img[y0, x1].astype(float)
    c01 = img[y1, x0].astype(float)
    c11 = img[y1, x1].astype(float)

    top = c00 * (1 - fx) + c10 * fx
    bottom = c01 * (1 - fx) + c11 * fx
    return list(top * (1 - fy) + bottom * fy)


def prim_channel(img, c):
    """Extract single channel (0=R, 1=G, 2=B)."""
    return img[:, :, c]


def prim_merge_channels(r, g, b):
    """Merge three single-channel arrays into RGB image."""
    return np.stack([r, g, b], axis=2).astype(np.uint8)


def prim_resize(img, w, h, mode="linear"):
    """Resize image to w x h."""
    interp = cv2.INTER_LINEAR
    if mode == "nearest":
        interp = cv2.INTER_NEAREST
    elif mode == "cubic":
        interp = cv2.INTER_CUBIC
    elif mode == "area":
        interp = cv2.INTER_AREA
    return cv2.resize(img, (int(w), int(h)), interpolation=interp)


def prim_crop(img, x, y, w, h):
    """Crop rectangle from image."""
    x, y, w, h = int(x), int(y), int(w), int(h)
    ih, iw = img.shape[:2]
    x = max(0, min(x, iw - 1))
    y = max(0, min(y, ih - 1))
    w = min(w, iw - x)
    h = min(h, ih - y)
    return img[y:y+h, x:x+w].copy()


def prim_paste(dst, src, x, y):
    """Paste src onto dst at position (x, y)."""
    result = dst.copy()
    x, y = int(x), int(y)
    sh, sw = src.shape[:2]
    dh, dw = dst.shape[:2]

    # Clip to bounds
    sx1 = max(0, -x)
    sy1 = max(0, -y)
    dx1 = max(0, x)
    dy1 = max(0, y)
    sx2 = min(sw, dw - x)
    sy2 = min(sh, dh - y)

    if sx2 > sx1 and sy2 > sy1:
        result[dy1:dy1+(sy2-sy1), dx1:dx1+(sx2-sx1)] = src[sy1:sy2, sx1:sx2]

    return result


PRIMITIVES = {
    # Dimensions
    'width': prim_width,
    'height': prim_height,

    # Creation
    'make-image': prim_make_image,
    'copy': prim_copy,

    # Pixel access
    'pixel': prim_pixel,
    'set-pixel': prim_set_pixel,
    'sample': prim_sample,

    # Channels
    'channel': prim_channel,
    'merge-channels': prim_merge_channels,

    # Geometry
    'resize': prim_resize,
    'crop': prim_crop,
    'paste': prim_paste,
}
