# Which atlas square each SKCombat tile model uses on its top and on its sides (to give the sk-* floor materials
# their own side texture). Reads the GLBs' UVs and normals; matches squares to textures/sk-*.png by their middle.
# Run: python -I topside.py <VTTMapTool dir> <atlas dir with greece.jpg / nature.jpg>
import sys, os, json, struct, glob, collections
import numpy as np
from PIL import Image
root, atlas_dir = sys.argv[1], sys.argv[2]
CT = {5120: np.int8, 5121: np.uint8, 5122: np.int16, 5123: np.uint16, 5125: np.uint32, 5126: np.float32}
NC = {'SCALAR': 1, 'VEC2': 2, 'VEC3': 3, 'VEC4': 4}
def glb(path):
    b = open(path, 'rb').read()
    n = struct.unpack_from('<I', b, 12)[0]
    js = json.loads(b[20:20 + n])
    off = 20 + n
    bn = struct.unpack_from('<I', b, off)[0]
    bin_ = b[off + 8:off + 8 + bn]
    def acc(i):
        a = js['accessors'][i]
        v = js['bufferViews'][a['bufferView']]
        dt = CT[a['componentType']]; k = NC[a['type']]
        start = v.get('byteOffset', 0) + a.get('byteOffset', 0)
        stride = v.get('byteStride', 0)
        isz = np.dtype(dt).itemsize * k
        if stride and stride != isz:
            raw = np.frombuffer(bin_, np.uint8, count=stride * a['count'], offset=start).reshape(a['count'], stride)[:, :isz]
            return raw.copy().view(dt).reshape(a['count'], k)
        return np.frombuffer(bin_, dt, count=a['count'] * k, offset=start).reshape(a['count'], k)
    out = []
    for m in js['meshes']:
        for p in m['primitives']:
            at = p['attributes']
            if 'TEXCOORD_0' not in at: continue
            pos, uv = acc(at['POSITION']), acc(at['TEXCOORD_0'])
            idx = acc(p['indices']).ravel() if 'indices' in p else np.arange(len(pos))
            img = None
            if 'material' in p:
                mt = js['materials'][p['material']]
                t = mt.get('pbrMetallicRoughness', {}).get('baseColorTexture')
                if t: img = js['images'][js['textures'][t['index']]['source']].get('uri')
            out.append((pos, uv, idx.reshape(-1, 3), img))
    return out
# the atlas square (256 px) under a UV point; atlases are 2048 px
def square(u, v):
    x = int((u % 1) * 2048) // 256 * 256
    y = int((v % 1) * 2048) // 256 * 256  # glTF uv: v down from the top
    return x, y
# sk textures → atlas squares, by comparing the untouched middle (seamless.py only changes the edge bands)
atl = {a: np.asarray(Image.open(os.path.join(atlas_dir, a + '.jpg')).convert('RGB')).astype(np.float32) for a in ('greece', 'nature')}
sk = {}
for f in glob.glob(os.path.join(root, 'textures', 'sk-*.png')):
    t = np.asarray(Image.open(f).convert('RGB').resize((256, 256))).astype(np.float32)[80:176, 80:176]
    best = None
    for a, im in atl.items():
        for size in (256, 512):
            for y in range(0, 2048, size):
                for x in range(0, 2048, size):
                    c = np.asarray(Image.fromarray(im[y:y + size, x:x + size].astype(np.uint8)).resize((256, 256))).astype(np.float32)[80:176, 80:176]
                    d = float(np.abs(c - t).mean())
                    if best is None or d < best[0]: best = (d, a, x, y, size)
    sk[os.path.basename(f)[:-4]] = best
print('sk textures:')
for k, v in sorted(sk.items()): print(' ', k, v)
res = {}
for f in sorted(glob.glob(os.path.join(root, 'props', 'basic', '*-tiles', '*.glb'))):
    tops, sides = collections.Counter(), collections.Counter()
    for pos, uv, tri, img in glb(f):
        a = 'greece' if img and 'greece' in img else 'nature' if img and 'nature' in img else str(img)
        p = pos[tri]
        nrm = np.cross(p[:, 1] - p[:, 0], p[:, 2] - p[:, 0])
        area = np.linalg.norm(nrm, axis=1)
        ny = nrm[:, 1] / np.maximum(area, 1e-9)
        cu = uv[tri].mean(axis=1)
        for i in range(len(tri)):
            if area[i] < 1e-6: continue
            sq = (a,) + square(cu[i, 0], cu[i, 1])
            if ny[i] > 0.9: tops[sq] += area[i]
            elif abs(ny[i]) < 0.3: sides[sq] += area[i]
    res[os.path.relpath(f, root)] = {'top': [list(k) + [round(float(v), 3)] for k, v in tops.most_common(3)], 'side': [list(k) + [round(float(v), 3)] for k, v in sides.most_common(3)]}
json.dump({'sk': sk, 'tiles': res}, open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'topside.json'), 'w'), indent=1)
for k, v in res.items(): print(k, 'TOP', v['top'][:2], 'SIDE', v['side'][:2])
