#!/usr/bin/env python3
"""
Convert sajeevavahini Zefania XML bible -> per-book .bin/.idx format for BibleVerse app.

Usage:
  python tools/convert_bible.py --src "World English Bible.xml" --dst assets/bible
  # or auto-download:
  python tools/convert_bible.py --download WEB --dst assets/bible
"""
import argparse, os, sys, json, struct, pathlib, urllib.request, xml.etree.ElementTree as ET

# mapping for --download shortcut to raw urls
DL_MAP = {
    "WEB": "https://raw.githubusercontent.com/sajeevavahini/bibles/main/World%20English%20Bible.xml",
    "KJV": "https://raw.githubusercontent.com/sajeevavahini/bibles/main/King%20James%20Version.xml",
    "ASV": "https://raw.githubusercontent.com/sajeevavahini/bibles/main/American%20Standard%20Version%20(1901).xml",
}

def download(url, dst_path):
    print(f"Downloading {url} -> {dst_path}")
    urllib.request.urlretrieve(url, dst_path)
    print(f"  done {os.path.getsize(dst_path)} bytes")

def parse_and_write(src_xml, dst_dir):
    os.makedirs(dst_dir, exist_ok=True)
    print(f"Parsing {src_xml} ...")
    books_data = {} # bnum -> {bname, chapters: {cnum: [(vnum,text)]}}

    cur_bnum = 0; cur_bname=""; cur_cnum=0
    # iterparse for low memory
    ctx = ET.iterparse(src_xml, events=("start","end"))
    for event, elem in ctx:
        if event=="start":
            if elem.tag=="BIBLEBOOK":
                cur_bnum=int(elem.attrib.get("bnumber","0"))
                cur_bname=elem.attrib.get("bname","").strip()
                if cur_bnum not in books_data:
                    books_data[cur_bnum]={"bname":cur_bname, "chapters":{}}
            elif elem.tag=="CHAPTER":
                cur_cnum=int(elem.attrib.get("cnumber","0"))
                if cur_bnum in books_data:
                    books_data[cur_bnum]["chapters"].setdefault(cur_cnum, [])
        else:
            if elem.tag=="VERS":
                vnum=int(elem.attrib.get("vnumber","0"))
                text=(elem.text or "").strip()
                if text and cur_bnum in books_data:
                    books_data[cur_bnum]["chapters"].setdefault(cur_cnum, []).append((vnum,text))
            if elem.tag in ("VERS","CHAPTER","BIBLEBOOK"):
                elem.clear()

    print(f"Found {len(books_data)} books")
    book_list=[]
    total=0
    for bnum in sorted(books_data.keys()):
        binfo=books_data[bnum]
        bname=binfo["bname"]
        chapters=binfo["chapters"]
        entries=[]
        for cnum in sorted(chapters.keys()):
            for vnum, txt in sorted(chapters[cnum], key=lambda x: x[0]):
                entries.append((cnum,vnum,txt))
        total+=len(entries)
        bin_path=os.path.join(dst_dir, f"{bnum:02d}.bin")
        idx_path=os.path.join(dst_dir, f"{bnum:02d}.idx")
        with open(bin_path,"wb") as bf, open(idx_path,"wb") as ix:
            ix.write(struct.pack("<4sHI", b'BIBK', 1, len(entries)))
            off=0
            for cnum,vnum,txt in entries:
                data=txt.encode('utf-8')
                bf.write(data+b'\x00')
                ix.write(struct.pack("<IHH", off, cnum, vnum))
                off+=len(data)+1
        book_list.append({"bnumber":bnum,"bname":bname,"verses":len(entries),"chapters":len(chapters)})
        print(f"  {bnum:02d} {bname:20s} {len(chapters)} ch {len(entries)} vs")

    manifest={"books":book_list,"total_verses":total,"version":1,"translation":os.path.basename(src_xml)}
    with open(os.path.join(dst_dir,"books.json"),"w") as f:
        json.dump(manifest,f,indent=2)
    print(f"Total {total} verses, wrote to {dst_dir}")

def main():
    ap=argparse.ArgumentParser()
    ap.add_argument("--src", help="Source XML file")
    ap.add_argument("--dst", default="assets/bible", help="Destination folder")
    ap.add_argument("--download", choices=list(DL_MAP.keys()), help="Download preset: WEB, KJV, ASV")
    args=ap.parse_args()

    src=args.src
    if args.download:
        # download to temp then use
        os.makedirs(os.path.dirname(args.dst) or ".", exist_ok=True)
        tmp=os.path.join(args.dst, "_tmp_download.xml") if os.path.isdir(args.dst) else args.dst+"_tmp_download.xml"
        # ensure parent exists
        pathlib.Path(args.dst).mkdir(parents=True, exist_ok=True)
        tmp_path=os.path.join(args.dst, f"_{args.download}.xml")
        if not os.path.exists(tmp_path) or os.path.getsize(tmp_path)<100000:
            download(DL_MAP[args.download], tmp_path)
        src=tmp_path
    if not src:
        print("Need --src or --download")
        sys.exit(1)
    if not os.path.exists(src):
        print(f"Source not found: {src}")
        sys.exit(1)
    parse_and_write(src, args.dst)

if __name__=="__main__":
    main()
