"""
Color Operations Primitives Library

Vectorized color adjustments: brightness, contrast, saturation, invert, HSV.
These operate on entire images for fast processing.
"""
import numpy as np
import cv2


def prim_adjust(img, brightness=0, contrast=1):
    """Adjust brightness and contrast. Brightness: -255 to 255, Contrast: 0 to 3+."""
    result = (img.astype(np.float32) - 128) * contrast + 128 + brightness
    return np.clip(result, 0, 255).astype(np.uint8)


def prim_mix_gray(img, amount):
    """Mix image with its grayscale version. 0=original, 1=grayscale."""
    gray = 0.299 * img[:, :, 0] + 0.587 * img[:, :, 1] + 0.114 * img[:, :, 2]
    gray_rgb = np.stack([gray, gray, gray], axis=-1)
    result = img.astype(np.float32) * (1 - amount) + gray_rgb * amount
    return np.clip(result, 0, 255).astype(np.uint8)


def prim_invert_img(img):
    """Invert all pixel values."""
    return (255 - img).astype(np.uint8)


def prim_shift_hsv(img, h=0, s=1, v=1):
    """Shift HSV: h=degrees offset, s/v=multipliers."""
    hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV).astype(np.float32)
    hsv[:, :, 0] = (hsv[:, :, 0] + h / 2) % 180
    hsv[:, :, 1] = np.clip(hsv[:, :, 1] * s, 0, 255)
    hsv[:, :, 2] = np.clip(hsv[:, :, 2] * v, 0, 255)
    return cv2.cvtColor(hsv.astype(np.uint8), cv2.COLOR_HSV2RGB)


def prim_add_noise(img, amount):
    """Add gaussian noise to image."""
    noise = np.random.normal(0, amount, img.shape)
    result = img.astype(np.float32) + noise
    return np.clip(result, 0, 255).astype(np.uint8)


def prim_quantize(img, levels):
    """Reduce to N color levels per channel."""
    levels = max(2, int(levels))
    factor = 256 / levels
    result = (img // factor) * factor + factor // 2
    return np.clip(result, 0, 255).astype(np.uint8)


def prim_sepia(img, intensity=1.0):
    """Apply sepia tone effect."""
    sepia_matrix = np.array([
        [0.393, 0.769, 0.189],
        [0.349, 0.686, 0.168],
        [0.272, 0.534, 0.131]
    ])
    sepia = np.dot(img, sepia_matrix.T)
    result = img.astype(np.float32) * (1 - intensity) + sepia * intensity
    return np.clip(result, 0, 255).astype(np.uint8)


def prim_grayscale(img):
    """Convert to grayscale (still RGB output)."""
    gray = 0.299 * img[:, :, 0] + 0.587 * img[:, :, 1] + 0.114 * img[:, :, 2]
    return np.stack([gray, gray, gray], axis=-1).astype(np.uint8)


PRIMITIVES = {
    # Brightness/Contrast
    'adjust': prim_adjust,

    # Saturation
    'mix-gray': prim_mix_gray,
    'grayscale': prim_grayscale,

    # HSV manipulation
    'shift-hsv': prim_shift_hsv,

    # Inversion
    'invert-img': prim_invert_img,

    # Effects
    'add-noise': prim_add_noise,
    'quantize': prim_quantize,
    'sepia': prim_sepia,
}
