#!/usr/bin/env python3
"""
FF Logo Patch Compositor
Detects bright arm/shoulder area in astronaut images and composites
the exact FF logo PNG as a patch. No AI — deterministic, always correct.
"""
import sys, io, base64, json, numpy as np
from PIL import Image, ImageDraw, ImageFilter, ImageOps

LOGO_PATH = "/root/.openclaw/workspace/work/pitches/future-forward/generator/ff-logo-mark.png"

def find_patch_placement(img_arr, min_brightness=0.45, min_area=20):
    """Find the best uniform bright region on the suit for patch placement."""
    brightness = img_arr[:,:,:3].mean(axis=2) / 255.0
    h, w = brightness.shape
    
    best = []
    step = 15
    for y in range(int(h*0.20), int(h*0.65), step):
        for x in range(int(w*0.40), int(w*0.95), step):
            region = brightness[y:y+min_area, x:x+min_area]
            if region.shape[0] < min_area or region.shape[1] < min_area:
                continue
            mean = region.mean()
            std = region.std()
            # Bright (suit) and uniform (fabric, not hardware)
            if mean > min_brightness and std < 0.10:
                best.append((mean, std, x, y))
    
    if not best:
        return None
    # Sort by brightness descending, pick top cluster
    best.sort(reverse=True)
    return best[0][2], best[0][3]  # x, y

def build_patch(logo_path, patch_w, patch_h, suit_brightness=0.80, warm_tint=1.02):
    """Build an off-white patch with the exact FF logo in embroidery style."""
    logo = Image.open(logo_path).convert("RGBA")
    
    # Off-white fabric background matching suit tone
    base_val = int(245 * suit_brightness)
    patch = Image.new("RGBA", (patch_w, patch_h), (base_val, int(base_val*0.98), int(base_val*0.95), 255))
    
    # Add subtle fabric texture
    np_p = np.array(patch, dtype=np.float32)
    rng = np.random.RandomState(42)
    np_p[:,:,:3] += rng.normal(0, 2.5, np_p[:,:,:3].shape)
    np_p[:,:,:3] = np.clip(np_p[:,:,:3], 0, 255)
    patch = Image.fromarray(np_p.astype(np.uint8), 'RGBA')
    
    # Scale logo to fit patch (60% of width, centered)
    lw = int(patch_w * 0.60)
    lh = int(logo.height * (lw / logo.width))
    logo_r = logo.resize((lw, lh), Image.LANCZOS)
    
    # Convert: white → transparent, black → warm dark thread
    la = np.array(logo_r, dtype=np.int32)
    total = la[:,:,0] + la[:,:,1] + la[:,:,2]
    white = total > 570
    la_out = la.copy().astype(np.uint8)
    la_out[white, 3] = 0
    # Thread color: warm near-black matching suit shadow
    thread_val = int(30 * suit_brightness)
    la_out[~white, :3] = [thread_val, int(thread_val*0.92), int(thread_val*0.85)]
    la_out[~white, 3] = 235
    logo_final = Image.fromarray(la_out)
    
    # Center on patch
    lx = (patch_w - lw) // 2
    ly = (patch_h - lh) // 2
    patch.paste(logo_final, (lx, ly), logo_final)
    
    # Stitched border
    draw = ImageDraw.Draw(patch)
    border_val = max(0, base_val - 60)
    draw.rectangle([3,3,patch_w-4,patch_h-4], outline=(border_val, int(border_val*0.92), int(border_val*0.85), 185), width=2)
    draw.rectangle([6,6,patch_w-7,patch_h-7], outline=(border_val, int(border_val*0.92), int(border_val*0.85), 110), width=1)
    
    return patch.filter(ImageFilter.SMOOTH)

def composite_patch(image_bytes):
    """Main entry: takes image bytes, returns composited image bytes."""
    img = Image.open(io.BytesIO(image_bytes)).convert("RGBA")
    aw, ah = img.size
    img_arr = np.array(img)
    
    # Find placement
    placement = find_patch_placement(img_arr)
    if not placement:
        # Fallback: upper-right chest area
        placement = (int(aw * 0.60), int(ah * 0.30))
    
    px, py = placement
    
    # Sample local suit brightness for color matching
    sample = img_arr[py:py+25, px:px+25, :3].astype(np.float32)
    local_bright = min(1.0, (sample.mean() / 255.0) * 1.15)
    
    # Patch size: ~8% of image width (realistic patch size)
    patch_w = max(80, int(aw * 0.082))
    patch_h = int(patch_w * 0.87)
    
    patch = build_patch(LOGO_PATH, patch_w, patch_h, suit_brightness=local_bright)
    
    # Slight rotation to follow suit surface
    patch = patch.rotate(-5, expand=True, resample=Image.BICUBIC)
    
    # Feather edges softly (6px)
    pa = np.array(patch, dtype=np.float32)
    f = 6
    ramp = np.linspace(0, 1, f)
    pa[:f,:,3] *= ramp[:,None]
    pa[-f:,:,3] *= ramp[::-1,None]
    pa[:,:f,3] = np.minimum(pa[:,:f,3], pa[:,:f,3] * ramp[None,:])
    pa[:,-f:,3] = np.minimum(pa[:,-f:,3], pa[:,-f:,3] * ramp[::-1][None,:])
    patch = Image.fromarray(pa.astype(np.uint8), 'RGBA')
    
    result = img.copy()
    result.paste(patch, (px, py), patch)
    
    out = io.BytesIO()
    result.convert("RGB").save(out, format="JPEG", quality=95)
    return out.getvalue()

if __name__ == "__main__":
    if len(sys.argv) == 3:
        # CLI test: python patch-compositor.py input.png output.jpg
        with open(sys.argv[1], 'rb') as f:
            result = composite_patch(f.read())
        with open(sys.argv[2], 'wb') as f:
            f.write(result)
        print(f"Saved to {sys.argv[2]}", file=sys.stderr)
    else:
        # Server mode: read raw image bytes from stdin, write result to stdout
        import os
        sys.stdin = sys.stdin.detach() if hasattr(sys.stdin, 'detach') else sys.stdin
        sys.stdout = sys.stdout.detach() if hasattr(sys.stdout, 'detach') else sys.stdout
        image_bytes = sys.stdin.read()
        result = composite_patch(image_bytes)
        sys.stdout.write(result)
