"""bom.py -- the shopping list for a real copy of one of Pin's builds (Ivy, Oct 23 2026: "part of the budget should go towards
buying the pieces it picks for its best builds for the write up").

    python3 bom.py RUN [RUN ...]     -> ../buy/RUN.md (readable) and ../buy/RUN_rebrickable.csv (Part,Color,Quantity)

Colours are LDraw codes, which Rebrickable's colour ids follow; part numbers are LDraw's (Rebrickable maps most, a few letter
suffixes like 3040b may need checking on import). Nothing is bought here. Written by Iris."""
import os, sys, re, json, collections
HERE = os.path.dirname(os.path.abspath(__file__)); OUT = os.path.join(os.path.dirname(HERE), 'buy')
def colours():
    out = {}
    for l in open(os.path.expanduser('~/lego/ldraw/LDConfig.ldr'), errors='ignore'):
        m = re.match(r'0 !COLOUR (\S+)\s+CODE\s+(\d+)', l)
        if m: out[int(m.group(2))] = m.group(1).replace('_', ' ')
    return out
def names():
    try: return {p: n for p, n, _ in json.load(open(os.path.join(HERE, 'catalog.json')))}
    except OSError: return {}
def bom(run):
    d = os.path.join(HERE, 'runs', run)
    ldr = os.path.join(d, 'build.ldr') if os.path.exists(os.path.join(d, 'build.ldr')) else sorted(
        [os.path.join(d, f) for f in os.listdir(d) if re.match(r'frame_\d\d\.ldr$', f)])[-1]
    c = collections.Counter()
    for l in open(ldr):
        t = l.split()
        if len(t) >= 15 and t[0] == '1': c[(t[14].lower().replace('.dat', ''), int(t[1]))] += 1
    return ldr, c
if __name__ == '__main__':
    os.makedirs(OUT, exist_ok=True); col, nm = colours(), names()
    for run in sys.argv[1:]:
        ldr, c = bom(run)
        try: became = next((b['became'][-1] for b in json.load(open(os.path.join(HERE, 'self.json')))['builds'] if b.get('run') == run), run)
        except Exception: became = run
        lines = [f"# {became.split('.')[0]}", f"Pin's build `{run}` ({os.path.basename(ldr)}): {sum(c.values())} pieces, {len(c)} different.", '',
                 '| count | part | name | colour |', '|---|---|---|---|']
        lines += [f"| {n} | {p} | {nm.get(p, p)} | {col.get(k, k)} |" for (p, k), n in sorted(c.items(), key=lambda x: (-x[1], x[0]))]
        open(os.path.join(OUT, f'{run}.md'), 'w').write('\n'.join(lines) + '\n')
        open(os.path.join(OUT, f'{run}_rebrickable.csv'), 'w').write('Part,Color,Quantity\n' + ''.join(f'{p},{k},{n}\n' for (p, k), n in sorted(c.items())))
        print(f"{run}: {sum(c.values())} pieces, {len(c)} different -- {became.split('.')[0][:60]}")
