#!/usr/bin/env python3
"""
Hybrid v2: Better erase + clean crisp logo rendering on Gemini's realistic patch.
Key fix: use clean black logo pixels (no satin distortion), proper erase with gaussian blend.
"""

from PIL import Image, ImageDraw, ImageFilter
import numpy as np

np.random.seed(42)

gemini = Image.open("tatiana-patch-v1.png").convert("RGB")
logo_raw = Image.open("ff-logo-mark.png").convert("RGBA")

g_arr = np.array(gemini, dtype=np.float32)
gW, gH = gemini.size

# Gemini patch bounds (from v1 analysis)
P_L, P_T, P_R, P_B = 1001, 912, 1400, 1295
pw = P_R - P_L
ph = P_B - P_T

print(f"Patch: {pw}x{ph} at ({P_L},{P_T})")

# Sample fabric color from patch interior
BM = 55  # border margin
interior = g_arr[P_T+BM:P_B-BM, P_L+BM:P_R-BM]
lum_int = interior[:,:,0]*0.299 + interior[:,:,1]*0.587 + interior[:,:,2]*0.114

# Fabric pixels = medium brightness (not the dark logo, not very dark)
fab_mask = lum_int > 130  # brighter than the logo
fab_r = interior[:,:,0][fab_mask].mean() if fab_mask.sum() > 0 else 210
fab_g = interior[:,:,1][fab_mask].mean() if fab_mask.sum() > 0 else 204
fab_b = interior[:,:,2][fab_mask].mean() if fab_mask.sum() > 0 else 192
print(f"Fabric color: ({fab_r:.0f}, {fab_g:.0f}, {fab_b:.0f})")

# Find logo dark area center (to know where to erase)
logo_mask = lum_int < 80
ly, lx = np.where(logo_mask)
logo_cy = ly.mean() + P_T + BM if len(ly) else gH//2
logo_cx = lx.mean() + P_L + BM if len(lx) else gW//2
logo_span_y = (ly.max() - ly.min()) if len(ly) else 200
logo_span_x = (lx.max() - lx.min()) if len(lx) else 200
print(f"Old logo center: ({logo_cx:.0f}, {logo_cy:.0f}), span: {logo_span_x}x{logo_span_y}")

# ============================================================
# 2. CREATE FABRIC FILL with matching texture
# ============================================================
result = g_arr.copy()

# Erase zone: the interior of the patch (excluding border)
ey1, ey2 = P_T + BM, P_B - BM
ex1, ex2 = P_L + BM, P_R - BM
erase_h, erase_w = ey2 - ey1, ex2 - ex1

# Sample local lighting in erase zone from surroundings (patch fabric area)
yi_e = np.arange(erase_h)[:, None].astype(np.float32)
xi_e = np.arange(erase_w)[None, :].astype(np.float32)

# Canvas weave texture
canvas_tex = (np.cos(xi_e * np.pi / 2.2) * 3.0 + 
              np.cos(yi_e * np.pi / 2.2) * 2.5 + 
              np.random.normal(0, 3.5, (erase_h, erase_w)))

fill = np.zeros((erase_h, erase_w, 3), dtype=np.float32)
fill[:,:,0] = np.clip(fab_r + canvas_tex, fab_r - 15, fab_r + 12)
fill[:,:,1] = np.clip(fab_g + canvas_tex * 0.92, fab_g - 14, fab_g + 10)
fill[:,:,2] = np.clip(fab_b + canvas_tex * 0.85, fab_b - 12, fab_b + 8)

# Blend fill into result using soft mask (Gaussian feathering at edges)
solid_mask = np.ones((erase_h, erase_w), dtype=np.float32)
mask_img = Image.fromarray((solid_mask * 255).astype(np.uint8), "L")
mask_img = mask_img.filter(ImageFilter.GaussianBlur(5))
blend_w = np.array(mask_img, dtype=np.float32) / 255.0

result[ey1:ey2, ex1:ex2, :] = (result[ey1:ey2, ex1:ex2, :] * (1 - blend_w[:,:,np.newaxis]) + 
                                 fill * blend_w[:,:,np.newaxis])

print(f"Erased zone: ({ex1},{ey1}) to ({ex2},{ey2})")

# ============================================================
# 3. PREPARE LOGO — clean, accurate, no distortion
# ============================================================
# The interior of the patch is (erase_w x erase_h) in Gemini coords
# We want the logo to be about 55% of the interior size, centered
logo_target_w = int(erase_w * 0.55)
logo_target_h = int(erase_h * 0.55)
# Keep aspect ratio of logo (449:521 ≈ 0.86:1)
logo_aspect = 449 / 521
if logo_target_w / logo_target_h > logo_aspect:
    logo_target_w = int(logo_target_h * logo_aspect)
else:
    logo_target_h = int(logo_target_w / logo_aspect)

logo_copy = logo_raw.copy()
logo_copy.thumbnail((logo_target_w, logo_target_h), Image.LANCZOS)
lw, lh = logo_copy.size
print(f"Logo: {lw}x{lh} in interior {erase_w}x{erase_h}")

# Create clean embroidery: the logo pixels become dark thread
ld = np.array(logo_copy, dtype=np.float32)
lum_logo = ld[:,:,0]*0.299 + ld[:,:,1]*0.587 + ld[:,:,2]*0.114

# Threshold: logo mark pixels
logo_px_strong = lum_logo < 80   # definitely logo
logo_px_mid = (lum_logo >= 80) & (lum_logo < 160)  # antialiased edge

# Simple crisp embroidery: dark thread (not satin-stitch grained — too much distortion)
thread_color = 22  # dark charcoal
emb_arr = np.zeros((lh, lw, 4), dtype=np.float32)
emb_arr[:,:,0] = np.where(logo_px_strong, thread_color * 0.92,
                  np.where(logo_px_mid, thread_color * (1 - (lum_logo - 80)/80), 0))
emb_arr[:,:,1] = np.where(logo_px_strong, thread_color * 0.82,
                  np.where(logo_px_mid, thread_color * 0.82 * (1 - (lum_logo - 80)/80), 0))
emb_arr[:,:,2] = np.where(logo_px_strong, thread_color * 0.62,
                  np.where(logo_px_mid, thread_color * 0.62 * (1 - (lum_logo - 80)/80), 0))
emb_arr[:,:,3] = np.where(logo_px_strong, 252,
                  np.where(logo_px_mid, 252 * (1 - (lum_logo - 80)/80), 0))

emb_img = Image.fromarray(np.clip(emb_arr, 0, 255).astype(np.uint8), "RGBA")
# Very slight blur to soften thread edges (thread isn't printed, it has slight fiber texture)
emb_img = emb_img.filter(ImageFilter.GaussianBlur(0.5))

# Place logo centered in the interior
lx_pos = ex1 + (erase_w - lw) // 2
ly_pos = ey1 + (erase_h - lh) // 2
print(f"Place logo at: ({lx_pos}, {ly_pos})")

# Apply to result
result_img = Image.fromarray(np.clip(result, 0, 255).astype(np.uint8), "RGB").convert("RGBA")
emb_layer = Image.new("RGBA", (gW, gH), (0,0,0,0))
emb_layer.paste(emb_img, (lx_pos, ly_pos), emb_img)
result_final_full = Image.alpha_composite(result_img, emb_layer).convert("RGB")

# ============================================================
# 4. SCALE TO ORIGINAL SIZE AND SAVE
# ============================================================
result_scaled = result_final_full.resize((1117, 2048), Image.LANCZOS)
result_scaled.save("tatiana-patch-v2.jpg", quality=96)
print("✓ Saved tatiana-patch-v2.jpg")

# Save context (original scale)
ctx = result_scaled.crop((560, 575, 940, 940))
ctx.save("tatiana-patch-v2-context.jpg", quality=95)

# Zoom into the logo area
orig_lx = int(lx_pos / (gW / 1117))
orig_ly = int(ly_pos / (gH / 2048))
orig_lw = int(lw / (gW / 1117))
orig_lh = int(lh / (gH / 2048))
print(f"In original coords: logo at ({orig_lx}, {orig_ly}), size ~{orig_lw}x{orig_lh}")

zoom_margin = 60
zp = result_scaled.crop((orig_lx - zoom_margin, orig_ly - zoom_margin,
                          orig_lx + orig_lw + zoom_margin, orig_ly + orig_lh + zoom_margin))
zp_big = zp.resize((zp.width * 3, zp.height * 3), Image.LANCZOS)
zp_big.save("tatiana-patch-v2-zoom.jpg", quality=95)

# Also save a full-res version
result_final_full.save("tatiana-hybrid-v2-fullres.jpg", quality=96)
print("✓ All saved.")
