"""Write job.json + sheet prompt for the 20 vehicle jobs (cars, motorbikes, bicycles, buses)."""
import json, os
ROOT = os.path.expanduser('~/forge-build/forge-assets')

FRAME4 = ('Front toward +Z, up = +Y, +X = the vehicle\'s LEFT side, centred on X = 0; origin (0,0,0) on the ground plane midway '
          'between the front and rearmost axles (Forge Garage contract v2). Tyres touch y = 0.')
FRAME2 = ('Front toward +Z, up = +Y, +X = the rider\'s LEFT side, centred on X = 0; origin (0,0,0) on the ground plane midway '
          'between the two axles. Upright (not leaning, not on its stand): both tyres touch y = 0 under the centre line.')

GAME = ('GAME WEIGHT overrides the budget above: every texture image at most 1024 px, ORM / occlusion maps at most 512 px, share '
        'one baked texture set between parts where you can. ')

def paint(default, extra=''):
    return ('PAINT CONTRACT (this is how games get colour variants of one model): the body colour lives in ONE material named '
            f'exactly "Paint". Its base colour TEXTURE holds only light neutral detail (near white, with grey grime, dust and wear '
            f'baked in) and its baseColorFactor holds the paint colour; default factor = {default}. A game recolours the vehicle by '
            'changing that factor, so nothing else (rubber, chrome, glass, lights, interior, decals) may use the Paint material. '
            'Give Paint a clearcoat (KHR_materials_clearcoat 1.0, roughness about 0.05) if it is glossy paint. ' + extra +
            'In nodes.json add "paints": six good-looking real paint colours for this model as linear RGB triples (the default first), '
            'and render paints.png: a 3x2 grid of the same 3/4 view in those six paints (EEVEE or low-sample Cycles is fine) to prove '
            'the recolour works. ')

GLASS_LIGHTS = ('Windows: one material named "Glass" (alpha BLEND, tinted, low roughness, real thickness not needed). Lamps: one '
                'material named "Lamps" with emissive (headlights warm white, tail and brake lights red, indicators amber; emission '
                'strength modest, games raise it). ')

WHEELS4 = ('Separate named mesh nodes: Body (painted shell and everything fixed to it), Wheel_FL, Wheel_FR, Wheel_RL, Wheel_RR (tyre + rim + '
           'brake disc, origin exactly at the wheel centre so a game spins it about X and steers the front ones about Y), '
           'SteeringWheel (origin at its hub). Empty nodes (no mesh): Seat_Driver (driver hip point), Door_L, Door_R (where a '
           'character gets in), Exhaust, Light_Head_L, Light_Head_R, Light_Tail_L, Light_Tail_R. A simple but believable '
           'interior must be visible through the glass: seats, dashboard, steering wheel, door cards, no empty hollow shell. ')

def two_wheel(kind):
    crank = ('Crank (cranks + chainring, origin on the bottom bracket axis, turns about X) with children Pedal_L, Pedal_R (origin '
             'on the pedal spindle). ' if kind == 'bicycle' else '')
    return ('Separate named mesh nodes: Frame (everything fixed to the frame), Fork (fork + handlebar + everything that steers, '
            'origin ON the steering axis at the head tube, so a game turns it about that axis), Wheel_F (child of Fork, origin at '
            'the wheel centre, spins about X), Wheel_R (origin at the wheel centre), ' + crank +
            'Kickstand (origin at its pivot, modelled UP/retracted). Empty nodes (no mesh): Seat_Rider (rider hip point on the '
            'saddle/seat), Hand_L, Hand_R (grip centres), Foot_L, Foot_R (footpeg or pedal points), Light_Head, Light_Tail. '
            'Spokes, cables, chain, brake lines as thin real geometry, smooth shading. ')

TIME = ('Pace: deliver a first complete GLB within about 25 minutes, then use what is left of about 45 minutes to improve what '
        'the renders show is weakest. Use Cycles with the GPU (OptiX/CUDA) and modest samples for bakes and renders. ')

SHEET = ('Video game vehicle design turnaround plate, photorealistic, on a plain light grey background. {desc} Original design, no '
         'brand, no logo, no badge, no licence plate text. Real size {size}. Layout: big orthographic side view across the top, '
         'front view, rear view and top view below, and a 3/4 front hero view; callouts naming the main parts and dimensions in '
         'metres; material swatches (paint, rubber, chrome, plastic, glass{extra}). Rich real-world construction detail, light '
         'believable wear. No other text.')

JOBS = [
 # cars
 ('vehicle.car.hatchback', 'euro-hatchback', 'A modern European compact five-door hatchback (city car class).', '4.10 m long, 1.78 m wide, 1.47 m high, wheelbase 2.62 m', 'cars', '[0.55, 0.03, 0.03] (deep red)', ''),
 ('vehicle.car.sedan', 'family-sedan', 'A modern mid-size four-door family saloon / sedan with a separate boot.', '4.80 m long, 1.84 m wide, 1.45 m high, wheelbase 2.80 m', 'cars', '[0.02, 0.05, 0.16] (dark metallic blue)', ''),
 ('vehicle.car.suv', 'family-suv', 'A modern mid-size five-door SUV crossover, roof rails, plastic wheel-arch cladding.', '4.70 m long, 1.88 m wide, 1.68 m high, wheelbase 2.75 m', 'cars', '[0.62, 0.63, 0.64] (silver grey)', ''),
 ('vehicle.car.sports', 'sports-coupe', 'A low two-door front-engine sports coupe / grand tourer, wide rear haunches, big wheels.', '4.45 m long, 1.92 m wide, 1.27 m high, wheelbase 2.55 m', 'cars', '[0.85, 0.55, 0.02] (yellow)', ''),
 ('vehicle.car.classic', 'classic-saloon', 'A 1960s European two-door compact saloon with round headlamps, chrome bumpers and hubcaps, thin pillars.', '4.00 m long, 1.58 m wide, 1.42 m high, wheelbase 2.40 m', 'cars', '[0.45, 0.62, 0.52] (pale mint green)', ''),
 # motorbikes
 ('vehicle.two-wheel.motorcycle', 'sport-bike', 'A modern fully faired 600 cc sport motorcycle, clip-on bars, rear-set pegs, underseat exhaust.', '2.05 m long, 0.72 m wide at the bars, 1.14 m high, wheelbase 1.40 m, 17 inch wheels', 'motos', '[0.60, 0.02, 0.02] (race red)', 'The fairing panels are Paint; frame, engine, wheels are not. '),
 ('vehicle.two-wheel.motorcycle', 'cruiser-bike', 'A low V-twin cruiser motorcycle, long wheelbase, wide bars, forward controls, chrome exhaust pipes, fat rear tyre, leather seat.', '2.40 m long, 0.95 m wide at the bars, 1.15 m high, wheelbase 1.65 m', 'motos', '[0.01, 0.01, 0.012] (gloss black)', 'Tank and fenders are Paint. '),
 ('vehicle.two-wheel.motorcycle', 'dirt-bike', 'A modern enduro / motocross dirt bike, knobbly tyres, long-travel suspension, high front fender, number plates.', '2.18 m long, 0.83 m wide at the bars, 1.27 m high, wheelbase 1.48 m, 21 inch front and 18 inch rear wheels', 'motos', '[0.85, 0.25, 0.01] (orange)', 'The plastics (fenders, shrouds, side panels) are Paint. '),
 ('vehicle.two-wheel.motorcycle', 'cafe-racer', 'A retro cafe racer / naked roadster motorcycle, round headlamp, teardrop tank, flat bench seat with hump, spoked wheels, twin upswept exhausts.', '2.10 m long, 0.75 m wide at the bars, 1.10 m high, wheelbase 1.42 m', 'motos', '[0.02, 0.17, 0.08] (racing green)', 'Tank and seat hump cover are Paint. '),
 ('vehicle.two-wheel.scooter', 'city-scooter', 'A classic step-through 125 cc city scooter with a curved steel body, leg shield, flat floorboard, small wheels, round headlamp on the handlebar.', '1.85 m long, 0.72 m wide at the bars, 1.15 m high, wheelbase 1.30 m, 12 inch wheels', 'motos', '[0.85, 0.80, 0.66] (cream)', 'The steel body panels are Paint. '),
 # bicycles
 ('vehicle.two-wheel.bicycle', 'city-bike', 'A Dutch step-through city bicycle: upright bars, full chain guard, mudguards, front basket, rear rack, dynamo lamps, kickstand.', '1.88 m long, 0.62 m wide at the bars, 1.12 m high, 28 inch wheels', 'bikes', '[0.01, 0.01, 0.01] (matt black)', 'The frame tubes and mudguards are Paint. '),
 ('vehicle.two-wheel.bicycle', 'road-bike', 'A modern road racing bicycle: drop handlebars with bar tape, carbon-look frame, deep rims, thin slick tyres, derailleurs, clipless pedals.', '1.70 m long, 0.42 m wide at the bars, 1.00 m high, 700c wheels', 'bikes', '[0.03, 0.12, 0.45] (blue)', 'The frame and fork are Paint. '),
 ('vehicle.two-wheel.bicycle', 'mountain-bike', 'A modern full-suspension trail mountain bike: wide flat bars, telescopic fork, rear shock, knobbly 29 inch tyres, disc brakes, dropper post.', '1.85 m long, 0.78 m wide at the bars, 1.15 m high', 'bikes', '[0.80, 0.25, 0.02] (orange)', 'The frame is Paint. '),
 ('vehicle.two-wheel.bicycle', 'bmx-bike', 'A freestyle BMX bicycle: 20 inch wheels, high-rise bars with crossbar, pegs on the axles, single speed, gyro brake.', '1.62 m long, 0.72 m wide at the bars, 1.00 m high', 'bikes', '[0.30, 0.05, 0.40] (purple)', 'The frame and fork are Paint. '),
 ('vehicle.two-wheel.bicycle', 'cargo-bike', 'A Dutch two-wheel box cargo bicycle (bakfiets): long front with a wooden cargo box with a bench, steering rod linkage to the front wheel, small 20 inch front wheel, 26 inch rear.', '2.55 m long, 0.66 m wide, 1.10 m high', 'bikes', '[0.08, 0.30, 0.15] (bottle green)', 'The frame is Paint; the wooden box is not. The front wheel is steered by a linkage, so here Fork = the front fork + front wheel group (origin on its own steering axis), and the handlebar is a separate mesh node Handlebar (origin on the head-tube axis). '),
 # buses
 ('vehicle.service.bus', 'city-bus', 'A modern 12 m low-floor single-deck city bus: three double doors on the right side, large side windows, LED destination display, roof-mounted equipment pods.', '12.0 m long, 2.55 m wide, 3.05 m high, wheelbase 5.9 m', 'buses', '[0.80, 0.80, 0.80] (white)', 'A livery stripe can be baked in a second colour on a separate non-Paint material. '),
 ('vehicle.service.school-bus', 'school-bus', 'A North American conventional yellow school bus with an engine hood in front, black rub rails, stop-sign arm, flashing lamps on the roof corners, folding front door.', '11.0 m long, 2.44 m wide, 3.20 m high, wheelbase 6.0 m', 'buses', '[0.95, 0.55, 0.01] (school-bus yellow)', 'Rub rails and lettering-free trim are not Paint. '),
 ('vehicle.service.bus', 'double-decker', 'A modern European double-deck city bus: two passenger decks with full-length windows, front door and middle door, staircase visible through the glass.', '10.6 m long, 2.55 m wide, 4.40 m high, wheelbase 5.6 m', 'buses', '[0.62, 0.02, 0.02] (bus red)', ''),
 ('vehicle.service.bus', 'intercity-coach', 'A modern 12.5 m intercity touring coach: high floor, tinted panoramic windows, luggage bays along the sides, three axles (tag axle behind the drive axle), one front door and one middle door.', '12.5 m long, 2.55 m wide, 3.70 m high', 'buses', '[0.85, 0.85, 0.86] (pearl white)', 'Add Wheel_RL2 and Wheel_RR2 for the tag axle; the origin is midway between the front axle and the rearmost axle. '),
 ('vehicle.service.bus', 'minibus', 'A modern 7 m high-roof minibus / shuttle (van based, 16 seats): sliding side door on the right, high roof, rear twin doors.', '7.0 m long, 2.02 m wide, 2.75 m high, wheelbase 4.3 m', 'buses', '[0.82, 0.82, 0.83] (white)', ''),
]

TRIS = {'cars': ('50k to 100k', 8), 'motos': ('35k to 80k', 7), 'bikes': ('20k to 50k', 6), 'buses': ('70k to 130k', 8)}
SWATCH = {'cars': '', 'motos': ', leather', 'bikes': ', leather saddle', 'buses': ', seat fabric'}

for cls, slug, desc, size, fam, default, extra in JOBS:
    tris, mats = TRIS[fam]
    if fam in ('cars', 'buses'):
        nodes = WHEELS4
        if fam == 'buses':
            nodes = nodes.replace('Door_L, Door_R (where a character gets in)', 'Door_Front (where passengers board)') + \
                'Each passenger door leaf is its own mesh node named Door_1, Door_2, ... (front to back, origin on its hinge or slide line) so a game can open it. Rows of seats, handrails and the driver cab are visible through the glass. '
        nodes += GLASS_LIGHTS + paint(default, extra)
        frame = FRAME4
    else:
        nodes = two_wheel('bicycle' if fam == 'bikes' else 'moto') + paint(default, extra)
        if fam == 'motos':
            nodes += 'Lamps: one material named "Lamps" with emissive (headlight warm white, tail light red, indicators amber). '
        frame = FRAME2
    nodes = GAME + nodes + TIME
    jd = f'{ROOT}/jobs/{slug}-v1'
    os.makedirs(jd, exist_ok=True)
    j = dict(id=f'{cls}/{slug}', version=1, name=f'{desc[2:-1].lower() if desc.startswith("A ") else desc} (the one on sheet.jpg)',
             title=' '.join(w.capitalize() for w in slug.split('-')), slug=slug, refs=['sheet.jpg'], size=size, tris=tris, mats=mats,
             effort='medium', frame=frame, nodes=nodes, family=fam)
    json.dump(j, open(f'{jd}/job.json', 'w'), indent=1)
    open(f'{jd}/sheet_prompt.txt', 'w').write(SHEET.format(desc=desc, size=size, extra=SWATCH[fam]))
print(len(JOBS), 'jobs')
