import bpy
import math
from mathutils import Vector

# ==============================================================================
# PROCEDURAL WHITE STORK (Ciconia ciconia) - Blender 4.2 Compatible
# ==============================================================================

def safe_socket_set(node, name, value):
    """Safely set socket value with type checking"""
    try:
        sock = node.inputs.get(name)
        if sock is not None:
            sock.default_value = value
    except Exception:
        pass

def create_mat(name, color, rough=0.5):
    """Create material with defensive handling"""
    mat = bpy.data.materials.new(name=name)
    mat.use_nodes = True
    bsdf = mat.node_tree.nodes.get('Principled BSDF')
    if bsdf:
        safe_socket_set(bsdf, 'Base Color', color)
        safe_socket_set(bsdf, 'Roughness', rough)
        try:
            safe_socket_set(bsdf, 'Subsurface Weight', 0.02)
        except Exception:
            pass
    return mat

# Materials
MAT_WHITE = create_mat("Stork_White", (1.0, 0.98, 0.95, 1.0), 0.65)
MAT_BLACK = create_mat("Stork_Black", (0.015, 0.015, 0.02, 1.0), 0.45)
MAT_RED = create_mat("Stork_Red", (0.88, 0.16, 0.08, 1.0), 0.28)
MAT_EYE = create_mat("Stork_Eye", (0.005, 0.005, 0.01, 1.0), 0.02)
MAT_EYE_RING = create_mat("Stork_EyeRing", (0.1, 0.1, 0.1, 1.0), 0.5)

parts = []

def desel():
    bpy.ops.object.select_all(action='DESELECT')

def apply_scale(obj):
    if obj:
        desel()
        obj.select_set(True)
        bpy.context.view_layer.objects.active = obj
        bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)

def apply_rot_scale(obj):
    if obj:
        desel()
        obj.select_set(True)
        bpy.context.view_layer.objects.active = obj
        bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)

def sphere(name, loc, r, seg=16, ring=12, mat=MAT_WHITE, scale=None):
    bpy.ops.mesh.primitive_uv_sphere_add(segments=seg, ring_count=ring, radius=r, location=loc)
    obj = bpy.context.active_object
    if obj:
        obj.name = name
        if scale:
            obj.scale = scale
            apply_scale(obj)
        obj.data.materials.append(mat)
        parts.append(obj.name)
    return obj

def cyl(name, loc, r, d, rot=(0,0,0), verts=12, mat=MAT_RED):
    bpy.ops.mesh.primitive_cylinder_add(vertices=verts, radius=r, depth=d, location=loc, rotation=rot)
    obj = bpy.context.active_object
    if obj:
        obj.name = name
        obj.data.materials.append(mat)
        parts.append(obj.name)
    return obj

def cone(name, loc, r1, r2, d, rot=(0,0,0), verts=16, mat=MAT_RED):
    bpy.ops.mesh.primitive_cone_add(vertices=verts, radius1=r1, radius2=r2, depth=d, location=loc, rotation=rot)
    obj = bpy.context.active_object
    if obj:
        obj.name = name
        obj.data.materials.append(mat)
        parts.append(obj.name)
    return obj

def cube(name, loc, scale, rot=(0,0,0), mat=MAT_WHITE):
    bpy.ops.mesh.primitive_cube_add(size=1, location=loc, rotation=rot)
    obj = bpy.context.active_object
    if obj:
        obj.name = name
        obj.scale = scale
        apply_rot_scale(obj)
        obj.data.materials.append(mat)
        parts.append(obj.name)
    return obj

# ==============================================================================
# BODY
# ==============================================================================

# Main torso
sphere("Body_Main", (0, 0, 0.52), 0.085, 32, 20, MAT_WHITE, (1.0, 2.1, 0.88))

# Breast
sphere("Body_Breast", (0, 0.1, 0.54), 0.065, 24, 16, MAT_WHITE, (0.92, 1.35, 0.95))

# Upper back
sphere("Body_Back", (0, -0.05, 0.55), 0.07, 24, 16, MAT_WHITE, (0.95, 1.5, 0.75))

# Rump
sphere("Body_Rump", (0, -0.14, 0.50), 0.055, 20, 14, MAT_WHITE, (0.85, 1.5, 0.7))

# Belly
sphere("Body_Belly", (0, 0.02, 0.46), 0.06, 20, 14, MAT_WHITE, (0.9, 1.4, 0.8))

# ==============================================================================
# NECK (S-curved series of segments)
# ==============================================================================

neck_pts = []
n_segs = 12
for i in range(n_segs):
    t = i / (n_segs - 1)
    y = 0.15 + 0.12 * math.sin(t * math.pi * 0.6)
    z = 0.58 + t * 0.38
    r = 0.028 * (1.0 - 0.35 * t)
    neck_pts.append((y, z, r))

for i, (y, z, r) in enumerate(neck_pts):
    sphere(f"Neck_{i}", (0, y, z), r, 14, 10, MAT_WHITE, (1.0, 1.25, 1.1))

# Throat pouch area
sphere("Neck_Throat", (0, 0.2, 0.72), 0.018, 12, 8, MAT_WHITE, (1.0, 1.4, 1.0))

# ==============================================================================
# HEAD
# ==============================================================================

head_y = 0.22
head_z = 0.98

# Main head
sphere("Head_Main", (0, head_y, head_z), 0.032, 20, 14, MAT_WHITE, (0.88, 1.15, 0.95))

# Crown
sphere("Head_Crown", (0, head_y - 0.015, head_z + 0.018), 0.024, 16, 12, MAT_WHITE, (0.85, 0.95, 0.7))

# Nape
sphere("Head_Nape", (0, head_y - 0.03, head_z - 0.01), 0.022, 14, 10, MAT_WHITE, (0.9, 1.1, 0.85))

# Cheeks
for side, xs in [("L", 0.022), ("R", -0.022)]:
    sphere(f"Head_Cheek_{side}", (xs, head_y + 0.01, head_z - 0.005), 0.018, 12, 8, MAT_WHITE, (0.8, 1.0, 0.9))

# Forehead
sphere("Head_Forehead", (0, head_y + 0.025, head_z + 0.01), 0.02, 14, 10, MAT_WHITE, (0.9, 1.0, 0.8))

# ==============================================================================
# BEAK (long, conical, red-orange)
# ==============================================================================

beak_len = 0.19
beak_y = head_y + 0.04 + beak_len / 2

# Upper mandible
cone("Beak_Upper", (0, beak_y, head_z + 0.003), 0.006, 0.017, beak_len, (math.pi/2, 0, 0), 20, MAT_RED)

# Upper mandible ridge
cube("Beak_Ridge", (0, beak_y - 0.02, head_z + 0.016), (0.006, 0.08, 0.008), (0,0,0), MAT_RED)

# Lower mandible
cone("Beak_Lower", (0, beak_y - 0.01, head_z - 0.015), 0.005, 0.014, beak_len * 0.92, (math.pi/2, 0, 0), 18, MAT_RED)

# Beak base transition
sphere("Beak_Base", (0, head_y + 0.045, head_z - 0.002), 0.016, 12, 8, MAT_RED, (0.7, 1.0, 0.9))

# Nostril openings (dark slits)
for side, xs in [("L", 0.008), ("R", -0.008)]:
    cube(f"Nostril_{side}", (xs, head_y + 0.08, head_z + 0.008), (0.003, 0.012, 0.003), (0, 0, 0), MAT_BLACK)

# ==============================================================================
# EYES
# ==============================================================================

eye_x = 0.027
eye_y = head_y + 0.02
eye_z = head_z + 0.008

for side, xs in [("L", eye_x), ("R", -eye_x)]:
    # Eyeball
    sphere(f"Eye_{side}", (xs, eye_y, eye_z), 0.009, 14, 10, MAT_EYE)
    # Eye ring (bare skin around eye - characteristic of storks)
    sphere(f"EyeRing_{side}", (xs, eye_y, eye_z), 0.013, 12, 8, MAT_EYE_RING, (1.0, 1.0, 0.6))

# ==============================================================================
# WINGS (white with black flight feathers)
# ==============================================================================

def build_wing(side, xsign):
    wx = xsign * 0.075
    
    # Shoulder/scapulars
    sphere(f"Wing_{side}_Shoulder", (wx, -0.02, 0.54), 0.038, 16, 12, MAT_WHITE, (0.75, 1.6, 0.4))
    
    # Lesser coverts
    cube(f"Wing_{side}_LesserCov", (xsign * 0.13, -0.03, 0.53), (0.06, 0.1, 0.012), (0.05, xsign*0.08, xsign*0.1), MAT_WHITE)
    
    # Median coverts
    cube(f"Wing_{side}_MedianCov", (xsign * 0.2, -0.05, 0.515), (0.08, 0.1, 0.01), (0.08, xsign*0.12, xsign*0.12), MAT_WHITE)
    
    # Greater coverts
    cube(f"Wing_{side}_GreaterCov", (xsign * 0.28, -0.07, 0.50), (0.09, 0.09, 0.009), (0.1, xsign*0.15, xsign*0.15), MAT_WHITE)
    
    # Alula (thumb feathers)
    cube(f"Wing_{side}_Alula", (xsign * 0.32, 0.01, 0.51), (0.025, 0.04, 0.006), (0.15, xsign*0.25, xsign*0.3), MAT_BLACK)
    
    # Primary flight feathers (BLACK - key feature)
    for i in range(8):
        px = xsign * (0.34 + i * 0.035)
        py = -0.08 - i * 0.018
        pz = 0.485 - i * 0.012
        length = 0.11 - i * 0.008
        width = 0.028 - i * 0.002
        cube(f"Wing_{side}_Primary_{i}", (px, py, pz), 
             (width, length, 0.005), 
             (0.12 + i*0.02, xsign*(0.2 + i*0.04), xsign*(0.18 + i*0.03)), MAT_BLACK)
    
    # Secondary flight feathers (BLACK)
    for i in range(6):
        sx = xsign * (0.18 + i * 0.028)
        sy = -0.12 - i * 0.012
        sz = 0.47 - i * 0.005
        cube(f"Wing_{side}_Secondary_{i}", (sx, sy, sz),
             (0.024, 0.09 - i*0.005, 0.004),
             (0.15, xsign*0.12, xsign*0.1), MAT_BLACK)
    
    # Tertials (innermost flight feathers, white)
    for i in range(3):
        tx = xsign * (0.1 + i * 0.025)
        ty = -0.13 - i * 0.01
        tz = 0.485
        cube(f"Wing_{side}_Tertial_{i}", (tx, ty, tz),
             (0.022, 0.07, 0.004),
             (0.12, xsign*0.08, xsign*0.05), MAT_WHITE)
    
    # Wing lining (underwing coverts)
    cube(f"Wing_{side}_Lining", (xsign * 0.18, -0.02, 0.48), (0.12, 0.14, 0.008), (0, xsign*0.1, xsign*0.08), MAT_WHITE)

build_wing("L", 1)
build_wing("R", -1)

# ==============================================================================
# TAIL
# ==============================================================================

tail_y = -0.2
tail_z = 0.46

# Tail coverts (white)
sphere("Tail_Coverts", (0, tail_y + 0.03, tail_z + 0.02), 0.04, 16, 12, MAT_WHITE, (0.8, 1.3, 0.5))

# Upper tail coverts
for i in range(3):
    offset = (i - 1) * 0.02
    cube(f"Tail_UpperCov_{i}", (offset, tail_y - 0.02, tail_z + 0.015), 
         (0.018, 0.06, 0.005), (-0.15, offset*0.3, 0), MAT_WHITE)

# Rectrices (tail feathers - white with black tips)
for i in range(6):
    offset = (i - 2.5) * 0.018
    
    # White base
    cube(f"Tail_Rectrix_W_{i}", (offset, tail_y - 0.07, tail_z - 0.01),
         (0.014, 0.07, 0.004), (-0.22, offset*0.4, 0), MAT_WHITE)
    
    # Black tip
    cube(f"Tail_Rectrix_B_{i}", (offset * 1.1, tail_y - 0.13, tail_z - 0.035),
         (0.012, 0.025, 0.003), (-0.28, offset*0.5, 0), MAT_BLACK)

# Under tail coverts
sphere("Tail_UnderCov", (0, tail_y + 0.01, tail_z - 0.02), 0.03, 14, 10, MAT_WHITE, (0.85, 1.2, 0.6))

# ==============================================================================
# LEGS
# ==============================================================================

def build_leg(side, xsign):
    lx = xsign * 0.04
    
    # Feathered thigh (tibial feathers)
    sphere(f"Leg_{side}_Thigh", (lx, 0.01, 0.44), 0.025, 14, 10, MAT_WHITE, (0.8, 1.2, 1.0))
    
    # Tibiotarsus (upper visible leg) - RED
    cyl(f"Leg_{side}_Tibia", (lx + xsign*0.01, 0.02, 0.32), 0.012, 0.2, (0.08, xsign*0.06, 0), 14, MAT_RED)
    
    # Knee joint
    sphere(f"Leg_{side}_Knee", (lx + xsign*0.012, 0.025, 0.22), 0.014, 10, 8, MAT_RED)
    
    # Tarsometatarsus (lower leg) - RED
    cyl(f"Leg_{side}_Tarsus", (lx + xsign*0.015, 0.02, 0.11), 0.009, 0.2, (0.03, xsign*0.02, 0), 12, MAT_RED)
    
    # Ankle/intertarsal joint
    sphere(f"Leg_{side}_Ankle", (lx + xsign*0.018, 0.022, 0.015), 0.012, 10, 8, MAT_RED)
    
    # Foot base
    fx = lx + xsign * 0.018
    fy = 0.022
    fz = 0.0
    
    sphere(f"Leg_{side}_FootBase", (fx, fy, fz + 0.005), 0.01, 8, 6, MAT_RED)
    
    # Toes (anisodactyl arrangement: 3 forward, 1 back)
    # Forward toes
    toe_angles = [-0.3, 0, 0.3]
    toe_lens = [0.055, 0.065, 0.055]
    for i, (ang, tlen) in enumerate(zip(toe_angles, toe_lens)):
        ty = fy + math.cos(ang) * tlen / 2 + 0.02
        tx = fx + math.sin(ang) * tlen / 2
        cyl(f"Leg_{side}_Toe_F{i}", (tx, ty, fz), 0.004, tlen, (math.pi/2 - 0.15, 0, ang), 8, MAT_RED)
        # Toe tip/claw
        sphere(f"Leg_{side}_Claw_F{i}", (fx + math.sin(ang)*tlen*0.9, fy + math.cos(ang)*tlen*0.9 + 0.02, fz - 0.003), 
               0.005, 6, 4, MAT_BLACK, (0.6, 0.8, 0.5))
    
    # Rear toe (hallux)
    cyl(f"Leg_{side}_Toe_R", (fx, fy - 0.02, fz), 0.003, 0.03, (-math.pi/2 + 0.25, 0, 0), 8, MAT_RED)
    sphere(f"Leg_{side}_Claw_R", (fx, fy - 0.04, fz - 0.005), 0.004, 6, 4, MAT_BLACK, (0.5, 0.7, 0.5))
    
    # Webbing between forward toes (partial, characteristic)
    cube(f"Leg_{side}_Web_01", (fx - 0.008, fy + 0.025, fz - 0.002), (0.008, 0.02, 0.002), (0.1, 0, -0.15), MAT_RED)
    cube(f"Leg_{side}_Web_12", (fx + 0.008, fy + 0.025, fz - 0.002), (0.008, 0.02, 0.002), (0.1, 0, 0.15), MAT_RED)
    
    # Scales on tarsus (subtle texture)
    for j in range(4):
        sz = 0.05 + j * 0.04
        sphere(f"Leg_{side}_Scale_{j}", (lx + xsign*0.016, 0.032, sz), 0.007, 6, 4, MAT_RED, (1.2, 0.5, 0.8))

build_leg("L", 1)
build_leg("R", -1)

# ==============================================================================
# ADDITIONAL ANATOMICAL DETAILS
# ==============================================================================

# Cloaca/vent region
sphere("Vent", (0, -0.08, 0.42), 0.02, 10, 8, MAT_WHITE, (0.9, 1.0, 0.7))

# Keel/sternum projection
sphere("Sternum", (0, 0.06, 0.42), 0.03, 12, 8, MAT_WHITE, (0.6, 1.4, 0.8))

# Back feather texture
for i in range(4):
    bx = (i - 1.5) * 0.025
    cube(f"Back_Feather_{i}", (bx, -0.08, 0.56), (0.018, 0.05, 0.006), (0.1, bx*0.3, 0), MAT_WHITE)

# Mantle feathers
for i in range(3):
    mx = (i - 1) * 0.03
    cube(f"Mantle_{i}", (mx, 0.0, 0.58), (0.022, 0.04, 0.008), (0, mx*0.2, 0), MAT_WHITE)

# Scapular feathers (between wing and body)
for side, xs in [("L", 1), ("R", -1)]:
    for i in range(3):
        cube(f"Scapular_{side}_{i}", (xs * (0.06 + i*0.015), -0.04 - i*0.02, 0.535 - i*0.01),
             (0.025, 0.05, 0.006), (0.08, xs*0.1, xs*0.05), MAT_WHITE)

# ==============================================================================
# JOIN ALL PARTS
# ==============================================================================

desel()

valid = []
for name in parts:
    obj = bpy.data.objects.get(name)
    if obj:
        obj.select_set(True)
        valid.append(obj)

if valid:
    bpy.context.view_layer.objects.active = valid[0]
    bpy.ops.object.join()
    
    stork = bpy.context.active_object
    if stork:
        stork.name = "White_Stork_Ciconia_ciconia"
        
        # Smooth shading
        desel()
        stork.select_set(True)
        bpy.context.view_layer.objects.active = stork
        bpy.ops.object.shade_smooth()
        
        # Subdivision for organic smoothness
        try:
            mod = stork.modifiers.new(name="Subsurf", type='SUBSURF')
            mod.levels = 1
            mod.render_levels = 2
        except Exception:
            pass
        
        # Center origin using valid enum value
        bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN')
        
        # Position stork at origin, standing on ground plane
        stork.location = (0, 0, 0)
        
        # Ensure proper orientation (head forward = +Y)
        desel()
        stork.select_set(True)
        bpy.context.view_layer.objects.active = stork

print("White Stork (Ciconia ciconia) generated successfully.")