import bpy
import math
from mathutils import Vector

def clear_scene():
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete(use_global=False)

def make_material(name, rgba, 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 = rgba
        except Exception:
            pass
        try:
            bsdf.inputs['Roughness'].default_value = float(roughness)
        except Exception:
            pass
        try:
            bsdf.inputs['Subsurface Weight'].default_value = 0.1
        except Exception:
            try:
                bsdf.inputs['Subsurface'].default_value = 0.1
            except Exception:
                pass
    return mat

def assign_material(obj, material):
    if obj is None or obj.data is None:
        return
    if len(obj.data.materials) == 0:
        obj.data.materials.append(material)
    else:
        obj.data.materials[0] = material

def add_uv_sphere(name, location, radius=1.0, scale=None, 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 is None:
        return None
    obj.name = name
    if scale:
        obj.scale = scale
        bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
    return bpy.data.objects.get(name)

def add_cylinder(name, location, radius, depth, vertices=12, rotation=(0, 0, 0)):
    bpy.ops.mesh.primitive_cylinder_add(
        vertices=vertices,
        radius=radius,
        depth=depth,
        location=location,
        rotation=rotation
    )
    obj = bpy.context.active_object
    if obj is None:
        return None
    obj.name = name
    return bpy.data.objects.get(name)

def add_cone(name, location, radius1, radius2, depth, vertices=12, rotation=(0, 0, 0)):
    bpy.ops.mesh.primitive_cone_add(
        vertices=vertices,
        radius1=radius1,
        radius2=radius2,
        depth=depth,
        location=location,
        rotation=rotation
    )
    obj = bpy.context.active_object
    if obj is None:
        return None
    obj.name = name
    return bpy.data.objects.get(name)

def select_only(obj):
    bpy.ops.object.select_all(action='DESELECT')
    if obj:
        obj.select_set(True)
        bpy.context.view_layer.objects.active = obj

def join_all(objects, result_name):
    valid = [o for o in objects if o is not None and o.type == 'MESH']
    if not valid:
        return None
    bpy.ops.object.select_all(action='DESELECT')
    for o in valid:
        o.select_set(True)
    bpy.context.view_layer.objects.active = valid[0]
    bpy.ops.object.join()
    result = bpy.context.active_object
    if result:
        result.name = result_name
    return bpy.data.objects.get(result_name)

class PelicanBuilder:
    def __init__(self):
        self.parts = []
        self.materials = {}
        self._init_materials()
    
    def _init_materials(self):
        self.materials['body'] = make_material("PelicanBody", (0.35, 0.28, 0.22, 1.0), 0.75)
        self.materials['head'] = make_material("PelicanHead", (0.95, 0.90, 0.75, 1.0), 0.65)
        self.materials['neck'] = make_material("PelicanNeck", (0.45, 0.25, 0.15, 1.0), 0.7)
        self.materials['bill'] = make_material("PelicanBill", (0.55, 0.45, 0.40, 1.0), 0.45)
        self.materials['pouch'] = make_material("PelicanPouch", (0.50, 0.38, 0.35, 1.0), 0.55)
        self.materials['wing'] = make_material("PelicanWing", (0.20, 0.15, 0.12, 1.0), 0.8)
        self.materials['covert'] = make_material("PelicanCovert", (0.40, 0.32, 0.25, 1.0), 0.75)
        self.materials['leg'] = make_material("PelicanLeg", (0.15, 0.13, 0.12, 1.0), 0.5)
        self.materials['eye'] = make_material("PelicanEye", (0.08, 0.06, 0.05, 1.0), 0.2)
        self.materials['eyering'] = make_material("PelicanEyeRing", (0.6, 0.65, 0.7, 1.0), 0.6)
        self.materials['tail'] = make_material("PelicanTail", (0.18, 0.14, 0.10, 1.0), 0.8)
    
    def _add_part(self, obj, material_key):
        if obj:
            assign_material(obj, self.materials.get(material_key, self.materials['body']))
            self.parts.append(obj)
        return obj
    
    def build_body(self):
        body = add_uv_sphere("Body", (0, 0, 0), radius=1.0, scale=(0.40, 0.22, 0.20), segments=24, rings=16)
        self._add_part(body, 'body')
        belly = add_uv_sphere("Belly", (0.05, 0, -0.08), radius=1.0, scale=(0.25, 0.18, 0.14), segments=16, rings=12)
        self._add_part(belly, 'body')
        back = add_uv_sphere("Back", (-0.05, 0, 0.08), radius=1.0, scale=(0.30, 0.18, 0.12), segments=16, rings=12)
        self._add_part(back, 'body')
    
    def build_neck(self):
        neck_profile = [
            (0.32, 0.10, 0.065), (0.38, 0.18, 0.060), (0.42, 0.27, 0.055),
            (0.40, 0.35, 0.052), (0.38, 0.42, 0.050), (0.40, 0.50, 0.048), (0.44, 0.57, 0.046),
        ]
        for i, (x, z, r) in enumerate(neck_profile):
            part = add_uv_sphere(f"Neck{i}", (x, 0, z), radius=r, segments=12, rings=10)
            self._add_part(part, 'neck')
    
    def build_head(self):
        head = add_uv_sphere("Head", (0.50, 0, 0.64), radius=1.0, scale=(0.10, 0.065, 0.065), segments=16, rings=12)
        self._add_part(head, 'head')
        crown = add_uv_sphere("Crown", (0.48, 0, 0.70), radius=0.04, segments=10, rings=8)
        self._add_part(crown, 'head')
        for i in range(4):
            nape = add_uv_sphere(f"Nape{i}", (0.42 - i*0.025, 0, 0.66 + i*0.01), radius=1.0, scale=(0.02, 0.012, 0.018), segments=8, rings=6)
            self._add_part(nape, 'neck')
    
    def build_bill(self):
        bill_base = add_uv_sphere("BillBase", (0.58, 0, 0.64), radius=1.0, scale=(0.05, 0.04, 0.028), segments=12, rings=10)
        self._add_part(bill_base, 'bill')
        upper_segments = [
            ((0.68, 0, 0.63), (0.07, 0.035, 0.022)),
            ((0.80, 0, 0.62), (0.08, 0.030, 0.018)),
            ((0.92, 0, 0.61), (0.07, 0.025, 0.015)),
            ((1.02, 0, 0.605), (0.05, 0.018, 0.012)),
        ]
        for i, (loc, scale) in enumerate(upper_segments):
            seg = add_uv_sphere(f"UpperBill{i}", loc, radius=1.0, scale=scale, segments=12, rings=10)
            self._add_part(seg, 'bill')
        hook = add_uv_sphere("BillHook", (1.08, 0, 0.595), radius=1.0, scale=(0.02, 0.012, 0.015), segments=10, rings=8)
        self._add_part(hook, 'bill')
        for side in [-1, 1]:
            nostril = add_uv_sphere(f"Nostril{'L' if side<0 else 'R'}", (0.62, side * 0.02, 0.65), radius=0.008, segments=6, rings=4)
            self._add_part(nostril, 'bill')
        lower_base = add_uv_sphere("LowerBillBase", (0.58, 0, 0.58), radius=1.0, scale=(0.04, 0.035, 0.025), segments=10, rings=8)
        self._add_part(lower_base, 'pouch')
        pouch_main = add_uv_sphere("PouchMain", (0.72, 0, 0.52), radius=1.0, scale=(0.14, 0.06, 0.08), segments=18, rings=14)
        self._add_part(pouch_main, 'pouch')
        pouch_ext = add_uv_sphere("PouchExt", (0.85, 0, 0.54), radius=1.0, scale=(0.10, 0.045, 0.05), segments=14, rings=10)
        self._add_part(pouch_ext, 'pouch')
        for side in [-1, 1]:
            ramus = add_uv_sphere(f"Ramus{'L' if side<0 else 'R'}", (0.78, side * 0.035, 0.58), radius=1.0, scale=(0.15, 0.008, 0.015), segments=10, rings=8)
            self._add_part(ramus, 'bill')
        throat = add_uv_sphere("PouchThroat", (0.52, 0, 0.50), radius=1.0, scale=(0.05, 0.045, 0.06), segments=12, rings=10)
        self._add_part(throat, 'pouch')
    
    def build_eyes(self):
        for side in [-1, 1]:
            suffix = 'L' if side < 0 else 'R'
            y = side * 0.055
            ring = add_uv_sphere(f"OrbitalRing{suffix}", (0.52, y, 0.66), radius=1.0, scale=(0.022, 0.018, 0.020), segments=10, rings=8)
            self._add_part(ring, 'eyering')
            eye = add_uv_sphere(f"Eye{suffix}", (0.525, y * 1.05, 0.66), radius=0.014, segments=10, rings=8)
            self._add_part(eye, 'eye')
            pupil = add_uv_sphere(f"Pupil{suffix}", (0.528, y * 1.12, 0.66), radius=0.006, segments=6, rings=4)
            self._add_part(pupil, 'eye')
    
    def build_wings(self):
        for side in [-1, 1]:
            suffix = 'L' if side < 0 else 'R'
            y_base = side * 0.19
            scap = add_uv_sphere(f"Scapular{suffix}", (0.15, y_base * 0.6, 0.08), radius=1.0, scale=(0.15, 0.08, 0.06), segments=14, rings=10)
            self._add_part(scap, 'covert')
            for i in range(3):
                covert = add_uv_sphere(f"Covert{suffix}{i}", (0.0 - i*0.08, y_base * (0.65 + i*0.05), 0.06 - i*0.01), radius=1.0, scale=(0.08, 0.05, 0.025), segments=10, rings=8)
                self._add_part(covert, 'covert')
            for i in range(6):
                primary = add_uv_sphere(f"Primary{suffix}{i}", (-0.18 - i*0.04, y_base * (0.6 + i*0.02), 0.01 - i*0.005), radius=1.0, scale=(0.05, 0.015, 0.008), segments=8, rings=6)
                self._add_part(primary, 'wing')
            for i in range(5):
                secondary = add_uv_sphere(f"Secondary{suffix}{i}", (-0.05 - i*0.05, y_base * (0.75 + i*0.02), 0.02), radius=1.0, scale=(0.045, 0.018, 0.006), segments=8, rings=6)
                self._add_part(secondary, 'wing')
            tip = add_uv_sphere(f"WingTip{suffix}", (-0.38, y_base * 0.55, -0.02), radius=1.0, scale=(0.06, 0.03, 0.012), segments=10, rings=8)
            self._add_part(tip, 'wing')
    
    def build_tail(self):
        base = add_uv_sphere("TailBase", (-0.38, 0, 0.02), radius=1.0, scale=(0.08, 0.08, 0.04), segments=12, rings=10)
        self._add_part(base, 'body')
        num_feathers = 12
        spread = 0.4
        for i in range(num_feathers):
            angle = (i - (num_feathers-1)/2) * spread / num_feathers
            y = math.sin(angle) * 0.06
            z_offset = -abs(angle) * 0.02
            feather = add_uv_sphere(f"TailFeather{i}", (-0.46, y, 0.0 + z_offset), radius=1.0, scale=(0.06, 0.012, 0.004), segments=8, rings=6)
            self._add_part(feather, 'tail')
    
    def build_legs(self):
        for side in [-1, 1]:
            suffix = 'L' if side < 0 else 'R'
            y = side * 0.08
            thigh = add_uv_sphere(f"Thigh{suffix}", (-0.12, y, -0.10), radius=1.0, scale=(0.04, 0.035, 0.06), segments=10, rings=8)
            self._add_part(thigh, 'body')
            tibia = add_cylinder(f"Tibia{suffix}", (-0.13, y, -0.20), radius=0.018, depth=0.14, vertices=10, rotation=(0.15, 0, 0))
            self._add_part(tibia, 'leg')
            knee = add_uv_sphere(f"Knee{suffix}", (-0.12, y, -0.13), radius=0.022, segments=8, rings=6)
            self._add_part(knee, 'leg')
            tarsus = add_cylinder(f"Tarsus{suffix}", (-0.14, y, -0.32), radius=0.012, depth=0.10, vertices=8, rotation=(0.1, 0, 0))
            self._add_part(tarsus, 'leg')
            ankle = add_uv_sphere(f"Ankle{suffix}", (-0.135, y, -0.27), radius=0.018, segments=8, rings=6)
            self._add_part(ankle, 'leg')
            foot = add_uv_sphere(f"Foot{suffix}", (-0.12, y, -0.37), radius=1.0, scale=(0.035, 0.045, 0.012), segments=10, rings=8)
            self._add_part(foot, 'leg')
            toe_angles = [-0.5, -0.17, 0.17, 0.5]
            toe_lengths = [0.06, 0.08, 0.08, 0.06]
            for j, (angle, length) in enumerate(zip(toe_angles, toe_lengths)):
                toe_y = y + math.sin(angle) * 0.08
                toe_x = -0.06
                toe = add_uv_sphere(f"Toe{suffix}{j}", (toe_x, toe_y, -0.38), radius=1.0, scale=(length, 0.012, 0.005), segments=8, rings=6)
                self._add_part(toe, 'leg')
                claw = add_cone(f"Claw{suffix}{j}", (toe_x + length * 0.8, toe_y, -0.38), radius1=0.008, radius2=0.002, depth=0.02, vertices=6, rotation=(0, math.pi/2, 0))
                self._add_part(claw, 'leg')
            for j in range(3):
                web_y = y + (toe_angles[j] + toe_angles[j+1]) / 2 * 0.08
                web = add_uv_sphere(f"Web{suffix}{j}", (-0.08, web_y, -0.385), radius=1.0, scale=(0.05, 0.03, 0.003), segments=8, rings=6)
                self._add_part(web, 'leg')
    
    def build_breast(self):
        breast = add_uv_sphere("Breast", (0.25, 0, -0.02), radius=1.0, scale=(0.12, 0.14, 0.12), segments=16, rings=12)
        self._add_part(breast, 'head')
        lower_breast = add_uv_sphere("LowerBreast", (0.12, 0, -0.08), radius=1.0, scale=(0.10, 0.12, 0.10), segments=14, rings=10)
        self._add_part(lower_breast, 'body')
    
    def finalize(self):
        if not self.parts:
            return None
        pelican = join_all(self.parts, "BrownPelican_Pelecanus_occidentalis")
        if pelican:
            select_only(pelican)
            if bpy.context.active_object:
                bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN')
            try:
                bpy.ops.object.shade_smooth()
            except Exception:
                pass
            pelican = bpy.data.objects.get("BrownPelican_Pelecanus_occidentalis")
        return pelican
    
    def build(self):
        self.build_body()
        self.build_breast()
        self.build_neck()
        self.build_head()
        self.build_bill()
        self.build_eyes()
        self.build_wings()
        self.build_tail()
        self.build_legs()
        return self.finalize()

def main():
    clear_scene()
    builder = PelicanBuilder()
    pelican = builder.build()
    if pelican:
        select_only(pelican)

if __name__ == "__main__":
    main()