"""Minimal SP3C v1 reader (numpy only). Sky Patrol V3 docs: /docs/formats.html"""
import json
import struct

import numpy as np

TYPES = {"f8": "<f8", "f4": "<f4", "i4": "<i4", "u4": "<u4", "i2": "<i2", "u2": "<u2", "u1": "u1"}


def read_sp3c(buf):
    """Return (header, columns) from one SP3C payload; str columns become lists of str."""
    buf = memoryview(buf).cast("B")
    if bytes(buf[:4]) != b"SP3C":
        raise ValueError("not an SP3C payload")
    (hlen,) = struct.unpack_from("<I", buf, 4)
    header = json.loads(bytes(buf[8:8 + hlen]))
    if int(str(header["version"]).split(".")[0]) != 1:
        raise ValueError("unsupported SP3C version %r" % header["version"])
    base, n, cols = 8 + hlen, header["n"], {}
    for c in header["columns"]:
        if c["offset"] % 8:
            raise ValueError("misaligned column %r" % c["name"])
        at = base + c["offset"]
        if c["type"] == "str":  # u4 offsets[n+1], then UTF-8 bytes
            offs = np.frombuffer(buf, "<u4", n + 1, at).tolist()
            blob = bytes(buf[at + 4 * (n + 1):at + c["nbytes"]])
            cols[c["name"]] = [blob[offs[i]:offs[i + 1]].decode("utf-8") for i in range(n)]
            continue
        if c["type"] not in TYPES:
            raise ValueError("unknown column type %r" % c["type"])
        dt = np.dtype(TYPES[c["type"]])
        if c.get("shuffled") and dt.itemsize > 1:  # itemsize planes of n bytes each
            planes = np.frombuffer(buf, np.uint8, n * dt.itemsize, at).reshape(dt.itemsize, n)
            cols[c["name"]] = np.ascontiguousarray(planes.T).view(dt).reshape(n)
        else:
            cols[c["name"]] = np.frombuffer(buf, dt, n, at)
    return header, cols


def expand(header, cols, name):
    """Strings for a dictionary-coded column such as 'camera' or 'band'."""
    spec = next(c for c in header["columns"] if c["name"] == name)
    return np.asarray(header["dictionaries"][spec["dict"]])[cols[name]]
