import bpy
import bmesh
import math
from mathutils import Vector, Matrix, Euler

def clear_scene():
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete()
    for mesh in bpy.data.meshes:
        bpy.data.meshes.remove(mesh)

def create_material(name, base_color, roughness=0.5):
    mat = bpy.data.materials.new(name=name)
    mat.use_nodes = True
    bsdf = mat.node_tree.nodes.get("Principled BSDF")
    if bsdf:
        try:
            bsdf.inputs["Base Color"].default_value = base_color
        except Exception:
            pass
        try:
            bsdf.inputs["Roughness"].default_value = float(roughness)
        except Exception:
            pass
        try:
            bsdf.inputs["Subsurface Weight"].default_value = 0.05
        except Exception:
            pass
    return mat

def add_sphere(name, location, radius, segments=16, rings=12):
    bpy.ops.mesh.primitive_uv_sphere_add(
        segments=segments, ring_count=rings,
        radius=radius, location=location
    )
    obj = bpy.context.active_object
    if obj:
        obj.name = name
    return obj

def add_cylinder(name, location, radius, depth, vertices=16):
    bpy.ops.mesh.primitive_cylinder_add(
        vertices=vertices, radius=radius, depth=depth, location=location
    )
    obj = bpy.context.active_object
    if obj:
        obj.name = name
    return obj

def add_cone(name, location, radius1, radius2, depth, vertices=16):
    bpy.ops.mesh.primitive_cone_add(
        vertices=vertices, radius1=radius1, radius2=radius2,
        depth=depth, location=location
    )
    obj = bpy.context.active_object
    if obj:
        obj.name = name
    return obj

def add_cube(name, location, size):
    bpy.ops.mesh.primitive_cube_add(size=size, location=location)
    obj = bpy.context.active_object
    if obj:
        obj.name = name
    return obj

def rotate_object(obj, rotation_euler):
    if obj:
        obj.rotation_euler = Euler(rotation_euler, 'XYZ')
        bpy.context.view_layer.objects.active = obj
        bpy.ops.object.transform_apply(rotation=True)

def scale_object(obj, scale):
    if obj:
        obj.scale = scale
        bpy.context.view_layer.objects.active = obj
        bpy.ops.object.transform_apply(scale=True)

def apply_material(obj, mat):
    if obj and obj.data and mat:
        if not obj.data.materials:
            obj.data.materials.append(mat)
        else:
            obj.data.materials[0] = mat

def join_all(objects):
    valid = [o for o in objects if o is not None]
    if not valid:
        return None
    bpy.ops.object.select_all(action='DESELECT')
    for obj in valid:
        obj.select_set(True)
    bpy.context.view_layer.objects.active = valid[0]
    bpy.ops.object.join()
    return bpy.context.active_object

def create_feather(name, location, length, width, mat):
    obj = add_cube(name, location, 1.0)
    if obj:
        scale_object(obj, (width, length, 0.01))
        apply_material(obj, mat)
    return obj

def create_wing(side, white_mat, black_mat):
    parts = []
    sign = 1 if side == "left" else -1
    base_x = sign * 0.12
    
    wing_base = add_sphere(f"wing_base_{side}", (base_x, -0.02, 0.52), 0.06, 12, 8)
    if wing_base:
        scale_object(wing_base, (1.2, 1.5, 0.4))
        apply_material(wing_base, white_mat)
        parts.append(wing_base)
    
    humerus = add_cylinder(f"humerus_{side}", (base_x + sign*0.12, -0.02, 0.5), 0.025, 0.18, 12)
    if humerus:
        rotate_object(humerus, (0, math.pi/2, sign * 0.2))
        apply_material(humerus, white_mat)
        parts.append(humerus)
    
    radius_ulna = add_cylinder(f"radius_{side}", (base_x + sign*0.28, -0.02, 0.48), 0.02, 0.22, 12)
    if radius_ulna:
        rotate_object(radius_ulna, (0, math.pi/2, sign * 0.3))
        apply_material(radius_ulna, white_mat)
        parts.append(radius_ulna)
    
    manus = add_cylinder(f"manus_{side}", (base_x + sign*0.48, -0.02, 0.44), 0.012, 0.18, 10)
    if manus:
        rotate_object(manus, (0, math.pi/2, sign * 0.4))
        apply_material(manus, white_mat)
        parts.append(manus)
    
    for i in range(6):
        x_off = sign * (0.18 + i * 0.06)
        y_off = -0.04 - i * 0.01
        f = create_feather(f"secondary_{side}_{i}", (base_x + x_off, y_off, 0.48), 0.14, 0.035, white_mat)
        if f:
            rotate_object(f, (0, 0, sign * 0.25))
            parts.append(f)
    
    for i in range(8):
        x_off = sign * (0.38 + i * 0.05)
        y_off = -0.06 - i * 0.015
        z_off = 0.46 - i * 0.015
        mat = black_mat
        f = create_feather(f"primary_{side}_{i}", (base_x + x_off, y_off, z_off), 0.18 - i*0.01, 0.03, mat)
        if f:
            rotate_object(f, (0.1, sign * 0.15, sign * (0.3 + i * 0.05)))
            parts.append(f)
    
    for i in range(5):
        x_off = sign * (0.12 + i * 0.04)
        f = create_feather(f"covert_{side}_{i}", (base_x + x_off, -0.01, 0.52), 0.08, 0.03, white_mat)
        if f:
            rotate_object(f, (0, 0, sign * 0.2))
            parts.append(f)
    
    scapular_cover = add_sphere(f"scapular_{side}", (base_x + sign*0.05, 0.0, 0.53), 0.05, 10, 8)
    if scapular_cover:
        scale_object(scapular_cover, (1.5, 1.2, 0.3))
        apply_material(scapular_cover, white_mat)
        parts.append(scapular_cover)
    
    return parts

def create_leg(side, red_mat, white_mat):
    parts = []
    sign = 1 if side == "left" else -1
    base_x = sign * 0.06
    
    thigh = add_sphere(f"thigh_{side}", (base_x, 0.0, 0.38), 0.045, 12, 8)
    if thigh:
        scale_object(thigh, (0.8, 1.0, 1.4))
        apply_material(thigh, white_mat)
        parts.append(thigh)
    
    thigh_feathers = add_sphere(f"thigh_feathers_{side}", (base_x, 0.0, 0.32), 0.035, 10, 6)
    if thigh_feathers:
        scale_object(thigh_feathers, (0.9, 1.0, 1.2))
        apply_material(thigh_feathers, white_mat)
        parts.append(thigh_feathers)
    
    tibiotarsus = add_cylinder(f"tibiotarsus_{side}", (base_x, 0.02, 0.18), 0.012, 0.22, 10)
    if tibiotarsus:
        rotate_object(tibiotarsus, (0.15, 0, 0))
        apply_material(tibiotarsus, red_mat)
        parts.append(tibiotarsus)
    
    knee = add_sphere(f"knee_{side}", (base_x, 0.035, 0.08), 0.018, 8, 6)
    if knee:
        apply_material(knee, red_mat)
        parts.append(knee)
    
    tarsometatarsus = add_cylinder(f"tarsometatarsus_{side}", (base_x, 0.03, -0.08), 0.01, 0.18, 10)
    if tarsometatarsus:
        rotate_object(tarsometatarsus, (-0.05, 0, 0))
        apply_material(tarsometatarsus, red_mat)
        parts.append(tarsometatarsus)
    
    ankle = add_sphere(f"ankle_{side}", (base_x, 0.025, -0.16), 0.015, 8, 6)
    if ankle:
        apply_material(ankle, red_mat)
        parts.append(ankle)
    
    for i, toe_angle in enumerate([-0.5, 0, 0.5]):
        toe = add_cylinder(f"toe_{side}_{i}", (base_x + sign*0.03*math.sin(toe_angle), 0.025 + 0.04, -0.17), 0.006, 0.06, 8)
        if toe:
            rotate_object(toe, (math.pi/2 - 0.2, toe_angle, 0))
            apply_material(toe, red_mat)
            parts.append(toe)
    
    hallux = add_cylinder(f"hallux_{side}", (base_x, 0.0, -0.17), 0.005, 0.03, 8)
    if hallux:
        rotate_object(hallux, (math.pi/2 + 0.8, 0, 0))
        apply_material(hallux, red_mat)
        parts.append(hallux)
    
    for i in range(4):
        angle = [-0.5, 0, 0.5, math.pi][i] if i < 3 else 0
        claw = add_cone(f"claw_{side}_{i}", (base_x, 0.08, -0.18), 0.005, 0.001, 0.015, 8)
        if claw:
            rotate_object(claw, (math.pi/2 - 0.2 if i < 3 else math.pi/2 + 0.8, angle if i < 3 else 0, 0))
            claw.location = (base_x + sign*0.03*math.sin(angle) if i < 3 else base_x, 
                            0.025 + 0.07 if i < 3 else -0.02, -0.18)
            apply_material(claw, red_mat)
            parts.append(claw)
    
    return parts

def create_tail(white_mat, black_mat):
    parts = []
    
    for i in range(8):
        x_off = (i - 3.5) * 0.025
        feather = create_feather(f"tail_feather_{i}", (x_off, -0.2, 0.48), 0.12, 0.03, white_mat)
        if feather:
            rotate_object(feather, (-0.3, x_off * 0.5, 0))
            parts.append(feather)
    
    uropygial = add_sphere("uropygial", (0, -0.15, 0.5), 0.035, 10, 8)
    if uropygial:
        scale_object(uropygial, (0.8, 1.2, 0.7))
        apply_material(uropygial, white_mat)
        parts.append(uropygial)
    
    return parts

def create_beak(red_mat):
    parts = []
    
    upper_beak = add_cone("upper_beak", (0, 0.38, 0.9), 0.018, 0.003, 0.22, 12)
    if upper_beak:
        rotate_object(upper_beak, (-math.pi/2 - 0.1, 0, 0))
        apply_material(upper_beak, red_mat)
        parts.append(upper_beak)
    
    upper_base = add_sphere("upper_beak_base", (0, 0.28, 0.89), 0.02, 10, 8)
    if upper_base:
        scale_object(upper_base, (0.9, 1.5, 0.7))
        apply_material(upper_base, red_mat)
        parts.append(upper_base)
    
    lower_beak = add_cone("lower_beak", (0, 0.36, 0.87), 0.015, 0.002, 0.18, 12)
    if lower_beak:
        rotate_object(lower_beak, (-math.pi/2 + 0.05, 0, 0))
        apply_material(lower_beak, red_mat)
        parts.append(lower_beak)
    
    culmen = add_cylinder("culmen", (0, 0.35, 0.91), 0.008, 0.16, 8)
    if culmen:
        rotate_object(culmen, (math.pi/2 - 0.1, 0, 0))
        apply_material(culmen, red_mat)
        parts.append(culmen)
    
    return parts

def create_head(white_mat, red_mat, eye_mat):
    parts = []
    
    cranium = add_sphere("cranium", (0, 0.22, 0.92), 0.045, 16, 12)
    if cranium:
        scale_object(cranium, (0.85, 1.1, 0.95))
        apply_material(cranium, white_mat)
        parts.append(cranium)
    
    forehead = add_sphere("forehead", (0, 0.25, 0.93), 0.035, 12, 10)
    if forehead:
        scale_object(forehead, (0.8, 0.9, 0.85))
        apply_material(forehead, white_mat)
        parts.append(forehead)
    
    for side in ["left", "right"]:
        sign = 1 if side == "left" else -1
        
        eye_socket = add_sphere(f"eye_socket_{side}", (sign * 0.025, 0.24, 0.93), 0.018, 10, 8)
        if eye_socket:
            apply_material(eye_socket, white_mat)
            parts.append(eye_socket)
        
        eyeball = add_sphere(f"eye_{side}", (sign * 0.032, 0.26, 0.932), 0.012, 12, 8)
        if eyeball:
            apply_material(eyeball, eye_mat)
            parts.append(eyeball)
        
        pupil = add_sphere(f"pupil_{side}", (sign * 0.038, 0.27, 0.934), 0.006, 8, 6)
        if pupil:
            apply_material(pupil, eye_mat)
            parts.append(pupil)
        
        eye_ring = add_cylinder(f"eye_ring_{side}", (sign * 0.033, 0.265, 0.933), 0.014, 0.003, 16)
        if eye_ring:
            rotate_object(eye_ring, (math.pi/2 - 0.2, sign * 0.3, 0))
            apply_material(eye_ring, red_mat)
            parts.append(eye_ring)
    
    nape = add_sphere("nape", (0, 0.18, 0.9), 0.04, 12, 10)
    if nape:
        scale_object(nape, (0.8, 0.7, 0.9))
        apply_material(nape, white_mat)
        parts.append(nape)
    
    crown = add_sphere("crown", (0, 0.2, 0.95), 0.03, 10, 8)
    if crown:
        scale_object(crown, (0.9, 0.8, 0.7))
        apply_material(crown, white_mat)
        parts.append(crown)
    
    return parts

def create_neck(white_mat):
    parts = []
    
    neck_segments = 12
    for i in range(neck_segments):
        t = i / (neck_segments - 1)
        y = 0.08 + t * 0.12
        z = 0.58 + t * 0.32
        curve = math.sin(t * math.pi * 0.8) * 0.04
        y += curve
        
        radius = 0.035 - t * 0.01
        
        segment = add_sphere(f"neck_segment_{i}", (0, y, z), radius, 12, 8)
        if segment:
            scale_object(segment, (0.85, 1.0, 1.1))
            apply_material(segment, white_mat)
            parts.append(segment)
    
    throat = add_cylinder("throat", (0, 0.12, 0.7), 0.025, 0.15, 12)
    if throat:
        rotate_object(throat, (-0.6, 0, 0))
        apply_material(throat, white_mat)
        parts.append(throat)
    
    return parts

def create_body(white_mat, black_mat):
    parts = []
    
    torso = add_sphere("torso", (0, 0, 0.5), 0.14, 24, 16)
    if torso:
        scale_object(torso, (0.75, 1.3, 0.9))
        apply_material(torso, white_mat)
        parts.append(torso)
    
    breast = add_sphere("breast", (0, 0.1, 0.52), 0.11, 20, 14)
    if breast:
        scale_object(breast, (0.7, 0.9, 0.85))
        apply_material(breast, white_mat)
        parts.append(breast)
    
    belly = add_sphere("belly", (0, 0.02, 0.42), 0.1, 18, 12)
    if belly:
        scale_object(belly, (0.65, 0.8, 0.75))
        apply_material(belly, white_mat)
        parts.append(belly)
    
    back = add_sphere("back", (0, -0.05, 0.52), 0.12, 18, 12)
    if back:
        scale_object(back, (0.8, 1.0, 0.6))
        apply_material(back, white_mat)
        parts.append(back)
    
    rump = add_sphere("rump", (0, -0.12, 0.5), 0.09, 16, 10)
    if rump:
        scale_object(rump, (0.75, 0.9, 0.7))
        apply_material(rump, white_mat)
        parts.append(rump)
    
    keel = add_sphere("keel", (0, 0.05, 0.4), 0.05, 12, 8)
    if keel:
        scale_object(keel, (0.4, 0.8, 1.2))
        apply_material(keel, white_mat)
        parts.append(keel)
    
    for side in ["left", "right"]:
        sign = 1 if side == "left" else -1
        flank = add_sphere(f"flank_{side}", (sign * 0.08, 0, 0.48), 0.07, 14, 10)
        if flank:
            scale_object(flank, (0.6, 1.1, 0.8))
            apply_material(flank, white_mat)
            parts.append(flank)
    
    for i in range(5):
        for side in ["left", "right"]:
            sign = 1 if side == "left" else -1
            x = sign * (0.06 + i * 0.015)
            y = -0.08 - i * 0.02
            f = create_feather(f"back_feather_{side}_{i}", (x, y, 0.54), 0.06, 0.025, black_mat if i > 2 else white_mat)
            if f:
                rotate_object(f, (-0.2, sign * 0.1, sign * 0.15))
                parts.append(f)
    
    return parts

def create_white_stork():
    clear_scene()
    
    white_mat = create_material("Stork_White_Plumage", (0.98, 0.96, 0.93, 1.0), 0.65)
    black_mat = create_material("Stork_Black_Plumage", (0.015, 0.015, 0.02, 1.0), 0.55)
    red_mat = create_material("Stork_Red_Bill_Legs", (0.82, 0.12, 0.08, 1.0), 0.3)
    eye_mat = create_material("Stork_Eye_Dark", (0.08, 0.06, 0.04, 1.0), 0.15)
    
    all_parts = []
    
    body_parts = create_body(white_mat, black_mat)
    all_parts.extend(body_parts)
    
    neck_parts = create_neck(white_mat)
    all_parts.extend(neck_parts)
    
    head_parts = create_head(white_mat, red_mat, eye_mat)
    all_parts.extend(head_parts)
    
    beak_parts = create_beak(red_mat)
    all_parts.extend(beak_parts)
    
    left_wing_parts = create_wing("left", white_mat, black_mat)
    all_parts.extend(left_wing_parts)
    
    right_wing_parts = create_wing("right", white_mat, black_mat)
    all_parts.extend(right_wing_parts)
    
    tail_parts = create_tail(white_mat, black_mat)
    all_parts.extend(tail_parts)
    
    left_leg_parts = create_leg("left", red_mat, white_mat)
    all_parts.extend(left_leg_parts)
    
    right_leg_parts = create_leg("right", red_mat, white_mat)
    all_parts.extend(right_leg_parts)
    
    stork = join_all(all_parts)
    
    if stork:
        stork.name = "Ciconia_ciconia"
        
        bpy.ops.object.select_all(action='DESELECT')
        stork.select_set(True)
        bpy.context.view_layer.objects.active = stork
        
        bpy.ops.object.shade_smooth()
        
        stork.location = (0, 0, 0.18)
    
    return stork

if __name__ == "__main__":
    create_white_stork()