import cadquery as cq
import math

FUSE_LEN = 186.0
NOSE_X = 93.0
TAIL_X = -93.0
FUSE_RY = 14.4
FUSE_RZ = 15.2
WING_ROOT_X = -9.0
WING_ROOT_Y = 6.0
WING_Z = -2.0
WING_T = 2.8
ENGINE_X = -13.0
ENGINE_Y = 46.0
ENGINE_Z = -15.0
ENGINE_LEN = 32.0
ENGINE_OD = 15.5
ENGINE_ID = 9.8


def safe_fillet(shape, radius):
    try:
        return shape.edges().fillet(radius)
    except Exception:
        return shape


def fuselage():
    stations = [
        (92.0, 2.1, 2.2),
        (88.0, 6.1, 6.4),
        (78.0, 10.8, 11.6),
        (58.0, 13.7, 14.8),
        (25.0, 14.5, 15.3),
        (-12.0, 14.0, 14.9),
        (-45.0, 11.7, 12.3),
        (-66.0, 8.2, 8.6),
        (-82.0, 4.3, 4.3),
        (-91.0, 1.7, 1.7),
    ]
    x, ry, rz = stations[0]
    wp = cq.Workplane('YZ', origin=(x, 0, 0)).ellipse(ry, rz)
    last_x = x
    for x, ry, rz in stations[1:]:
        wp = wp.workplane(offset=x - last_x).ellipse(ry, rz)
        last_x = x
    return safe_fillet(wp.loft(combine=True), 0.25)


def main_wing(side):
    root_chord = 48.0
    tip_chord = 28.0
    semi_span = 88.0
    pts = [
        (WING_ROOT_X + root_chord / 2.0, side * WING_ROOT_Y),
        (WING_ROOT_X - root_chord / 2.0, side * WING_ROOT_Y),
        (WING_ROOT_X - 18.0, side * (WING_ROOT_Y + semi_span)),
        (WING_ROOT_X + 16.0, side * (WING_ROOT_Y + semi_span)),
    ]
    wing = cq.Workplane('XY').polyline(pts).close().extrude(WING_T)
    wing = wing.translate((0, 0, WING_Z - WING_T / 2.0))
    return safe_fillet(wing, 0.75)


def wing_root_fairing(side):
    # Low, elongated fairing that overlaps fuselage and root chord without a tall visible block.
    fair = cq.Workplane('XY').ellipse(35.0, 10.0).extrude(3.2)
    fair = fair.translate((-9.0, side * 8.0, WING_Z - 1.7))
    return safe_fillet(fair, 0.8)


def engine(side):
    outer = cq.Workplane('YZ', origin=(ENGINE_X - ENGINE_LEN / 2.0, side * ENGINE_Y, ENGINE_Z)).circle(ENGINE_OD / 2.0).extrude(ENGINE_LEN)
    bore = cq.Workplane('YZ', origin=(ENGINE_X - ENGINE_LEN / 2.0 - 1.0, side * ENGINE_Y, ENGINE_Z)).circle(ENGINE_ID / 2.0).extrude(ENGINE_LEN + 2.0)
    nacelle = outer.cut(bore)
    lip_outer = cq.Workplane('YZ', origin=(ENGINE_X + ENGINE_LEN / 2.0 - 1.5, side * ENGINE_Y, ENGINE_Z)).circle(ENGINE_OD / 2.0 + 0.55).extrude(3.0)
    lip_inner = cq.Workplane('YZ', origin=(ENGINE_X + ENGINE_LEN / 2.0 - 1.8, side * ENGINE_Y, ENGINE_Z)).circle(ENGINE_ID / 2.0).extrude(3.6)
    lip = lip_outer.cut(lip_inner)
    # A small crown pad is mostly hidden by the pylon, used only to make contact robust.
    crown = cq.Workplane('XY').rect(12.0, 7.5).extrude(2.8).translate((ENGINE_X - 1.5, side * ENGINE_Y, ENGINE_Z + ENGINE_OD / 2.0 - 1.8))
    return safe_fillet(nacelle.union(lip).union(crown), 0.28)


def pylon(side):
    # One simple vertical web: top overlaps wing underside; bottom overlaps nacelle crown.
    profile = [
        (ENGINE_X + 5.8, -3.4),
        (ENGINE_X + 3.0, -9.2),
        (ENGINE_X - 6.0, -9.2),
        (ENGINE_X - 8.5, -3.4),
    ]
    web = cq.Workplane('XZ').polyline(profile).close().extrude(5.2)
    web = web.translate((0, side * (ENGINE_Y - 2.6), 0))
    return safe_fillet(web, 0.55)


def tail_root():
    fair = cq.Workplane('XY').ellipse(15.0, 6.3).extrude(4.0).translate((-76.0, 0, 5.8))
    dorsal = cq.Workplane('XY').ellipse(9.0, 3.5).extrude(4.2).translate((-75.0, 0, 8.8))
    return safe_fillet(fair.union(dorsal), 0.65)


def stabilizer(side):
    pts = [
        (-64.0, side * 4.4),
        (-87.0, side * 4.4),
        (-93.0, side * 42.0),
        (-68.0, side * 42.0),
    ]
    stab = cq.Workplane('XY').polyline(pts).close().extrude(2.2).translate((0, 0, 6.9))
    return safe_fillet(stab, 0.5)


def vertical_fin():
    pts = [(-86.0, 0.0), (-63.0, 0.0), (-69.5, 32.0), (-80.0, 33.5)]
    fin = cq.Workplane('XZ').polyline(pts).close().extrude(4.8).translate((0, -2.4, 10.1))
    root = cq.Workplane('XY').ellipse(9.0, 3.5).extrude(4.8).translate((-75.0, 0, 9.5))
    return safe_fillet(fin.union(root), 0.45)


fuselage_body = fuselage()
main_wing_left = main_wing(1)
main_wing_right = main_wing(-1)
wing_root_fairing_left = wing_root_fairing(1)
wing_root_fairing_right = wing_root_fairing(-1)
engine_nacelle_left = engine(1)
engine_nacelle_right = engine(-1)
pylon_left = pylon(1)
pylon_right = pylon(-1)
tail_root_fairing = tail_root()
horizontal_stabilizer_left = stabilizer(1)
horizontal_stabilizer_right = stabilizer(-1)
vertical_tail_fin = vertical_fin()

PARTS = {
    'fuselage_body': fuselage_body,
    'main_wing_left': main_wing_left,
    'main_wing_right': main_wing_right,
    'wing_root_fairing_left': wing_root_fairing_left,
    'wing_root_fairing_right': wing_root_fairing_right,
    'engine_nacelle_left': engine_nacelle_left,
    'engine_nacelle_right': engine_nacelle_right,
    'pylon_left': pylon_left,
    'pylon_right': pylon_right,
    'tail_root_fairing': tail_root_fairing,
    'horizontal_stabilizer_left': horizontal_stabilizer_left,
    'horizontal_stabilizer_right': horizontal_stabilizer_right,
    'vertical_tail_fin': vertical_tail_fin,
}

result = fuselage_body
for part in [main_wing_left, main_wing_right, wing_root_fairing_left, wing_root_fairing_right,
             engine_nacelle_left, engine_nacelle_right, pylon_left, pylon_right,
             tail_root_fairing, horizontal_stabilizer_left, horizontal_stabilizer_right, vertical_tail_fin]:
    result = result.union(part)

r = result
