#!/usr/bin/env python3
"""
Hybrid approach: Take Gemini's physically-realistic patch (v1), 
sample its fabric color, erase the wrong logo, and draw the correct chevron logo.
Output: best of both worlds - Gemini's physical realism + exact correct logo.
"""

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
print(f"Gemini: {gW}x{gH}")

# Gemini patch center: (1201, 1096)
PC_X, PC_Y = 1201, 1096

# ============================================================
# 1. FIND THE EXACT PATCH BOUNDS IN GEMINI IMAGE
# ============================================================
# Search for the dark border around the patch
# The patch border is dark thread (< 60 brightness)
search = g_arr[PC_Y-200:PC_Y+200, PC_X-200:PC_X+200]
r, gb, b = search[:,:,0], search[:,:,1], search[:,:,2]
dark_mask = (r < 65) & (gb < 65) & (b < 65)
dy, dx = np.where(dark_mask)

if len(dx):
    # Patch bounds (search area coords)
    px_min, px_max = dx.min(), dx.max()
    py_min, py_max = dy.min(), dy.max()
    # Convert to global coords
    patch_left   = px_min + (PC_X - 200)
    patch_right  = px_max + (PC_X - 200)
    patch_top    = py_min + (PC_Y - 200)
    patch_bottom = py_max + (PC_Y - 200)
    print(f"Patch bounds: ({patch_left}, {patch_top}) to ({patch_right}, {patch_bottom})")
    print(f"Patch size: {patch_right-patch_left}x{patch_bottom-patch_top}")
else:
    # Fallback
    patch_left, patch_top = PC_X - 150, PC_Y - 120
    patch_right, patch_bottom = PC_X + 150, PC_Y + 120
    print("Using fallback patch bounds")

pw = patch_right - patch_left
ph = patch_bottom - patch_top

# ============================================================
# 2. EXTRACT PATCH, SAMPLE FABRIC COLOR (non-logo, non-border areas)
# ============================================================
patch_crop = g_arr[patch_top:patch_bottom, patch_left:patch_right]
lum_patch = patch_crop[:,:,0]*0.299 + patch_crop[:,:,1]*0.587 + patch_crop[:,:,2]*0.114

# Fabric pixels = medium-bright (not border/logo dark, not extremely bright)
fabric_mask = (lum_patch > 100) & (lum_patch < 230)
if fabric_mask.sum() > 0:
    fab_r = patch_crop[:,:,0][fabric_mask].mean()
    fab_g = patch_crop[:,:,1][fabric_mask].mean()
    fab_b = patch_crop[:,:,2][fabric_mask].mean()
else:
    fab_r, fab_g, fab_b = 210, 204, 192

print(f"Sampled fabric color: ({fab_r:.0f}, {fab_g:.0f}, {fab_b:.0f})")

# ============================================================
# 3. FIND THE LOGO AREA (dark pixels that are the WRONG logo)
# ============================================================
# Dark pixels in the interior of the patch (not border)
border_margin = int(min(pw, ph) * 0.12)  # border is ~12% of patch size
interior = patch_crop[border_margin:-border_margin, border_margin:-border_margin]
interior_lum = interior[:,:,0]*0.299 + interior[:,:,1]*0.587 + interior[:,:,2]*0.114

logo_area_dark = interior_lum < 80  # the embroidered logo pixels
dark_coords = np.where(logo_area_dark)
if len(dark_coords[0]):
    logo_center_y = dark_coords[0].mean() + border_margin
    logo_center_x = dark_coords[1].mean() + border_margin
    logo_span_y = dark_coords[0].max() - dark_coords[0].min()
    logo_span_x = dark_coords[1].max() - dark_coords[1].min()
    print(f"Logo area in patch: center=({logo_center_x:.0f}, {logo_center_y:.0f}), span={logo_span_x}x{logo_span_y}")
else:
    logo_center_x = pw // 2
    logo_center_y = ph // 2
    logo_span_x = pw // 2
    logo_span_y = ph // 2
    print("Logo area: fallback center")

# ============================================================
# 4. ERASE WRONG LOGO — paint fabric color over it
# ============================================================
result = np.array(gemini, dtype=np.float32)

# The logo area (with margin around dark pixels)
erase_margin = 15  # px in Gemini coords
ey1 = max(0, int(logo_center_y - logo_span_y/2 - erase_margin) + patch_top)
ey2 = min(gH, int(logo_center_y + logo_span_y/2 + erase_margin) + patch_top)
ex1 = max(0, int(logo_center_x - logo_span_x/2 - erase_margin) + patch_left)
ex2 = min(gW, int(logo_center_x + logo_span_x/2 + erase_margin) + patch_left)
print(f"Erase zone: ({ex1},{ey1}) to ({ex2},{ey2})")

# Paint over with fabric color (with slight texture to match)
np.random.seed(13)
erase_h = ey2 - ey1
erase_w = ex2 - ex1

# Create fabric fill matching the sampled color with some texture
fill = np.zeros((erase_h, erase_w, 3), dtype=np.float32)
yi_e = np.arange(erase_h)[:, None].astype(np.float32)
xi_e = np.arange(erase_w)[None, :].astype(np.float32)
tex = (np.cos(xi_e * np.pi / 2.0) * 2.5 + np.cos(yi_e * np.pi / 2.0) * 2.0 + 
       np.random.normal(0, 3, (erase_h, erase_w)))[:,:,np.newaxis]
fill[:,:,0] = fab_r
fill[:,:,1] = fab_g
fill[:,:,2] = fab_b
fill = np.clip(fill + tex, 0, 255)

result[ey1:ey2, ex1:ex2] = fill

# Gaussian blend at edges of the erase zone
result_img = Image.fromarray(result.astype(np.uint8), "RGB")
erase_mask = np.zeros((gH, gW), dtype=np.float32)
erase_mask[ey1:ey2, ex1:ex2] = 1.0
erase_mask_img = Image.fromarray((erase_mask * 255).astype(np.uint8)).filter(ImageFilter.GaussianBlur(3))
erase_mask_soft = np.array(erase_mask_img) / 255.0

original_arr = np.array(gemini, dtype=np.float32)
blend = original_arr * (1 - erase_mask_soft[:,:,np.newaxis]) + result * erase_mask_soft[:,:,np.newaxis]
result = blend

# ============================================================
# 5. DRAW CORRECT LOGO ON TOP
# ============================================================
# Determine size: fit logo into the logo area we found, minus margin
logo_target_w = max(30, int(logo_span_x * 0.85))
logo_target_h = max(25, int(logo_span_y * 0.85))
print(f"Logo target size: {logo_target_w}x{logo_target_h}")

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

ld = np.array(logo_copy, dtype=np.float32)
logo_lum = ld[:,:,0]*0.299 + ld[:,:,1]*0.587 + ld[:,:,2]*0.114
logo_px = (logo_lum < 120) & (ld[:,:,3] > 30)

# Satin stitch embroidery
yi_l = np.arange(lh)[:, None].astype(np.float32)
xi_l = np.arange(lw)[None, :].astype(np.float32)
stdir = yi_l * 0.6 + xi_l
tphase = (stdir % 2.0) / 2.0
tval = np.clip(8 + np.sin(tphase * np.pi) * 34 + np.random.normal(0, 1.5, (lh, lw)), 0, 48)

emb = np.zeros((lh, lw, 4), dtype=np.float32)
emb[:,:,0] = logo_px * tval * 0.90
emb[:,:,1] = logo_px * tval * 0.80
emb[:,:,2] = logo_px * tval * 0.62
emb[:,:,3] = logo_px * 245

emb_img = Image.fromarray(np.clip(emb, 0, 255).astype(np.uint8), "RGBA")
emb_img = emb_img.filter(ImageFilter.GaussianBlur(0.4))

# Place logo centered on the logo area
logo_paste_x = int(logo_center_x - lw/2) + patch_left
logo_paste_y = int(logo_center_y - lh/2) + patch_top
print(f"Paste logo at: ({logo_paste_x}, {logo_paste_y})")

# Apply embroidery to result
result_img2 = 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, (logo_paste_x, logo_paste_y), emb_img)
result_img2 = Image.alpha_composite(result_img2, emb_layer)

# ============================================================
# 6. SCALE DOWN TO ORIGINAL SIZE and SAVE
# ============================================================
# Scale Gemini (1920x2228) back to original (1117x2048)
result_rgb = result_img2.convert("RGB")
result_final = result_rgb.resize((1117, 2048), Image.LANCZOS)
result_final.save("tatiana-patch-v2.jpg", quality=96)
print("✓ Saved tatiana-patch-v2.jpg")

# Also save full-res hybrid
result_rgb.save("tatiana-hybrid-fullres.jpg", quality=95)
print("✓ Saved tatiana-hybrid-fullres.jpg")

# Crop for review (scaled to ~300px area around patch)
# In original coords: patch is around flag location at (750, 710)
ctx = result_final.crop((580, 590, 920, 920))
ctx.save("tatiana-patch-v2-context.jpg", quality=95)

# Zoom into logo area (in original coords)
# Scale from gemini: logo was at (logo_paste_x, logo_paste_y) in gemini
orig_logo_x = int(logo_paste_x / (gW / 1117))
orig_logo_y = int(logo_paste_y / (gH / 2048))
print(f"In original coords: logo at ~({orig_logo_x}, {orig_logo_y})")
zp = result_final.crop((orig_logo_x - 80, orig_logo_y - 60, orig_logo_x + 130, orig_logo_y + 120))
zp_big = zp.resize((zp.width * 3, zp.height * 3), Image.LANCZOS)
zp_big.save("tatiana-patch-v2-zoom.jpg", quality=95)
print("✓ Context + Zoom saved.")
