"""
Drawing Primitives Library

Draw shapes, text, and characters on images.
"""
import numpy as np
import cv2
from PIL import Image, ImageDraw, ImageFont


# Default font (will be loaded lazily)
_default_font = None


def _get_default_font(size=16):
    """Get default font, creating if needed."""
    global _default_font
    if _default_font is None or _default_font.size != size:
        try:
            _default_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", size)
        except:
            _default_font = ImageFont.load_default()
    return _default_font


def prim_draw_char(img, char, x, y, font_size=16, color=None):
    """Draw a single character at (x, y)."""
    if color is None:
        color = [255, 255, 255]

    pil_img = Image.fromarray(img)
    draw = ImageDraw.Draw(pil_img)
    font = _get_default_font(font_size)
    draw.text((x, y), char, fill=tuple(color), font=font)
    return np.array(pil_img)


def prim_draw_text(img, text, x, y, font_size=16, color=None):
    """Draw text string at (x, y)."""
    if color is None:
        color = [255, 255, 255]

    pil_img = Image.fromarray(img)
    draw = ImageDraw.Draw(pil_img)
    font = _get_default_font(font_size)
    draw.text((x, y), text, fill=tuple(color), font=font)
    return np.array(pil_img)


def prim_fill_rect(img, x, y, w, h, color=None):
    """Fill a rectangle with color."""
    if color is None:
        color = [255, 255, 255]

    result = img.copy()
    x, y, w, h = int(x), int(y), int(w), int(h)
    result[y:y+h, x:x+w] = color
    return result


def prim_draw_rect(img, x, y, w, h, color=None, thickness=1):
    """Draw rectangle outline."""
    if color is None:
        color = [255, 255, 255]

    result = img.copy()
    cv2.rectangle(result, (int(x), int(y)), (int(x+w), int(y+h)),
                  tuple(color), thickness)
    return result


def prim_draw_line(img, x1, y1, x2, y2, color=None, thickness=1):
    """Draw a line from (x1, y1) to (x2, y2)."""
    if color is None:
        color = [255, 255, 255]

    result = img.copy()
    cv2.line(result, (int(x1), int(y1)), (int(x2), int(y2)),
             tuple(color), thickness)
    return result


def prim_draw_circle(img, cx, cy, radius, color=None, thickness=1, fill=False):
    """Draw a circle."""
    if color is None:
        color = [255, 255, 255]

    result = img.copy()
    t = -1 if fill else thickness
    cv2.circle(result, (int(cx), int(cy)), int(radius), tuple(color), t)
    return result


def prim_draw_ellipse(img, cx, cy, rx, ry, angle=0, color=None, thickness=1, fill=False):
    """Draw an ellipse."""
    if color is None:
        color = [255, 255, 255]

    result = img.copy()
    t = -1 if fill else thickness
    cv2.ellipse(result, (int(cx), int(cy)), (int(rx), int(ry)),
                angle, 0, 360, tuple(color), t)
    return result


def prim_draw_polygon(img, points, color=None, thickness=1, fill=False):
    """Draw a polygon from list of [x, y] points."""
    if color is None:
        color = [255, 255, 255]

    result = img.copy()
    pts = np.array(points, dtype=np.int32).reshape((-1, 1, 2))

    if fill:
        cv2.fillPoly(result, [pts], tuple(color))
    else:
        cv2.polylines(result, [pts], True, tuple(color), thickness)

    return result


PRIMITIVES = {
    # Text
    'draw-char': prim_draw_char,
    'draw-text': prim_draw_text,

    # Rectangles
    'fill-rect': prim_fill_rect,
    'draw-rect': prim_draw_rect,

    # Lines and shapes
    'draw-line': prim_draw_line,
    'draw-circle': prim_draw_circle,
    'draw-ellipse': prim_draw_ellipse,
    'draw-polygon': prim_draw_polygon,
}
