"""
Filters Primitives Library

Image filters: blur, sharpen, edges, convolution.
"""
import numpy as np
import cv2


def prim_blur(img, radius):
    """Gaussian blur with given radius."""
    radius = max(1, int(radius))
    ksize = radius * 2 + 1
    return cv2.GaussianBlur(img, (ksize, ksize), 0)


def prim_box_blur(img, radius):
    """Box blur with given radius."""
    radius = max(1, int(radius))
    ksize = radius * 2 + 1
    return cv2.blur(img, (ksize, ksize))


def prim_median_blur(img, radius):
    """Median blur (good for noise removal)."""
    radius = max(1, int(radius))
    ksize = radius * 2 + 1
    return cv2.medianBlur(img, ksize)


def prim_bilateral(img, d=9, sigma_color=75, sigma_space=75):
    """Bilateral filter (edge-preserving blur)."""
    return cv2.bilateralFilter(img, d, sigma_color, sigma_space)


def prim_sharpen(img, amount=1.0):
    """Sharpen image using unsharp mask."""
    blurred = cv2.GaussianBlur(img, (0, 0), 3)
    return cv2.addWeighted(img, 1.0 + amount, blurred, -amount, 0)


def prim_edges(img, low=50, high=150):
    """Canny edge detection."""
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    edges = cv2.Canny(gray, low, high)
    return cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)


def prim_sobel(img, ksize=3):
    """Sobel edge detection."""
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=ksize)
    sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=ksize)
    mag = np.sqrt(sobelx**2 + sobely**2)
    mag = np.clip(mag, 0, 255).astype(np.uint8)
    return cv2.cvtColor(mag, cv2.COLOR_GRAY2RGB)


def prim_laplacian(img, ksize=3):
    """Laplacian edge detection."""
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    lap = cv2.Laplacian(gray, cv2.CV_64F, ksize=ksize)
    lap = np.abs(lap)
    lap = np.clip(lap, 0, 255).astype(np.uint8)
    return cv2.cvtColor(lap, cv2.COLOR_GRAY2RGB)


def prim_emboss(img):
    """Emboss effect."""
    kernel = np.array([[-2, -1, 0],
                       [-1,  1, 1],
                       [ 0,  1, 2]])
    result = cv2.filter2D(img, -1, kernel)
    return np.clip(result + 128, 0, 255).astype(np.uint8)


def prim_dilate(img, size=1):
    """Morphological dilation."""
    kernel = np.ones((size * 2 + 1, size * 2 + 1), np.uint8)
    return cv2.dilate(img, kernel)


def prim_erode(img, size=1):
    """Morphological erosion."""
    kernel = np.ones((size * 2 + 1, size * 2 + 1), np.uint8)
    return cv2.erode(img, kernel)


def prim_convolve(img, kernel):
    """Apply custom convolution kernel."""
    kernel = np.array(kernel, dtype=np.float32)
    return cv2.filter2D(img, -1, kernel)


PRIMITIVES = {
    # Blur
    'blur': prim_blur,
    'box-blur': prim_box_blur,
    'median-blur': prim_median_blur,
    'bilateral': prim_bilateral,

    # Sharpen
    'sharpen': prim_sharpen,

    # Edges
    'edges': prim_edges,
    'sobel': prim_sobel,
    'laplacian': prim_laplacian,

    # Effects
    'emboss': prim_emboss,

    # Morphology
    'dilate': prim_dilate,
    'erode': prim_erode,

    # Custom
    'convolve': prim_convolve,
}
