"""Extract the authored original constellation graph, without running the game.

3C41080/3C41070 return the exact galaxy-definition +110/+118 vector.
3A5ACE0 resolves each authored endpoint name through the original catalogue.
No real-world astronomy dataset or generated stellar coordinate is used here.
"""
from pathlib import Path
import argparse
import hashlib
import json
import struct
import zlib

ROOT = Path(__file__).resolve().parents[1]
KEY = 'd30f11c57448cff8ec3dd0c5c80d9868482ccadb'
SOURCE = ROOT / 'artifacts/navigation-0410-qa/resources' / (KEY + '.fres')
OUTPUT = ROOT / 'XFEEDToolBox.Core/Galaxy/original-constellations.json'


def extract():
    f = SOURCE.read_bytes()
    b = zlib.decompress(f[1620:])
    assert hashlib.sha256(b).hexdigest() == '49a18ad1f339169391abe2a76475eea6731028b9809d7120fb32d57309196f22'
    np, nr = struct.unpack_from('<I', f, 0x34)[0], struct.unpack_from('<I', f, 0x20)[0]
    assert (np, nr, len(b)) == (3227, 24, 82232836)
    cursor = grouped = 0
    while grouped < np:
        kind, count = struct.unpack_from('<HH', b, cursor)
        assert kind in (2, 3, 4)
        cursor += 4
        grouped += count
    assert grouped == np
    pools = [struct.unpack_from('<8I', b, cursor + index * 32) for index in range(np)]
    cursor += np * 32
    roots = [struct.unpack_from('<4I', b, cursor + index * 16) for index in range(nr)]
    cursor += nr * 16
    start = len(b) - sum(pool[2] for pool in pools)
    assert start == 13127884 and start - cursor == 814013 * 16 + 16
    refs = {}
    for index in range(814013):
        s, o, t, v = struct.unpack_from('<4I', b, cursor + index * 16)
        assert s < np and t < np and o < pools[s][2] and v < pools[t][2] and (s, o) not in refs
        refs[s, o] = t, v

    def at(pointer):
        pool, offset = pointer
        assert 0 <= pool < np and 0 <= offset < pools[pool][2]
        return start + pools[pool][3] + offset

    def label(pointer):
        pos = at(pointer)
        end = b.index(0, pos, pos + 128)
        return b[pos:end].decode('ascii')

    root = next(row for row in roots if row[:2] == (1765098201, 2090718614))
    assert root[2:] == (2647, 4032)
    rp, ro = root[2:]
    vector = refs[rp, ro + 0x110]
    count = struct.unpack_from('<I', b, at((rp, ro)) + 0x118)[0]
    assert vector == (2561, 7680) and count == 16
    groups = []
    for index in range(count):
        row = vector[0], vector[1] + index * 24
        name = label(refs[row])
        edge_pointer = refs[row[0], row[1] + 8]
        edge_count = struct.unpack_from('<I', b, at(row) + 16)[0]
        assert 0 < edge_count <= 64
        edges = []
        for edge in range(edge_count):
            pair = edge_pointer[0], edge_pointer[1] + edge * 16
            edges.append(dict(fromName=label(refs[pair]), toName=label(refs[pair[0], pair[1] + 8])))
        groups.append(dict(name=name, edges=edges))
    assert len(groups) == 16 and sum(len(group['edges']) for group in groups) == 206
    assert groups[0]['name'] == 'Aries' and groups[14]['name'] == 'Orion'
    return dict(sourceResource=KEY,
        sourceSha256='ba4e049fd4eae066e72f29898a25d52692422e5905fc7a4a65105e44eb631ade',
        fresSha256=hashlib.sha256(f).hexdigest(), frozenSha256=hashlib.sha256(b).hexdigest(),
        root=list(root), sourceVector=list(vector), sourceVectorField='0x110', sourceCountField='0x118',
        coordinateSource='Resolve original endpoint names using actual catalogue records; no positions are generated by this asset',
        gameProcessAccess=False, generatedStellarRecords=False, groups=groups)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--verify', action='store_true')
    args = parser.parse_args()
    result = extract()
    text = json.dumps(result, ensure_ascii=False, indent=2) + '\n'
    if args.verify:
        assert OUTPUT.read_text(encoding='utf-8') == text, 'Original constellation graph changed'
        print('PASS:16 original groups,206 exact authored edges, frozen resource hash and pointers')
    else:
        OUTPUT.write_text(text, encoding='utf-8')
        print(OUTPUT)
