# -*- coding: utf-8 -*-
r"""Copia do Arena a ANCORAGEM do brilho, para os modelos com a mesma malha.

    python tools/brilho_ancorar.py DSM_SET_01_DAGGER
    python tools/brilho_ancorar.py --todos --aplicar

Cada slot `Weapon_EnchantNN` tem 40 bytes de dados, e do offset 12 ao 36 estao a posicao e
a rotacao a que o efeito se cola a arma:

    nossa adaga      0.00  0.00  0.00  0.00  0.00  0.00     tudo a zero
    adaga do Arena   0.00 -2.00  0.00  0.00  1.57  1.57     1.57 = pi/2, roda 90 graus
    bengala (ok)     0.00  0.00 -3.00  0.00  0.00  0.00
    espada (ok)      0.00  0.00 -2.00  0.00  0.00  0.00

Com tudo a zero o brilho da adaga aparecia ao lado do corpo em vez de na lamina. So se copia
quando o `._RC` (a malha) e byte a byte igual nos dois clients -- e entao a mesma arma, e a
ancoragem dele aplica-se tal e qual.

NAO SE TOCA nos primeiros 12 bytes: sao o indice do efeito e dois campos fixos. E os slots
que nos acrescentamos (do 14 ao 18) levam a ancoragem do ultimo slot que o Arena tem.
"""
import argparse
import hashlib
import os
import re
import struct

NW = r"C:\Games\Axion\ResUnpak\Character\Weapon"
AW = r"C:\Games\Arena\ResUnpak\Character\Weapon"
INI, FIM = 12, 40


def md5(p):
    return hashlib.md5(open(p, "rb").read()).hexdigest()


def slots(d):
    return [(m.end(), d[m.end():m.end() + 40]) for m in re.finditer(rb"Weapon_Enchant(\d\d)", d)]


def trata(nome, aplicar, verboso=True, forcar=False):
    pn, pa = os.path.join(NW, nome + "._OB"), os.path.join(AW, nome + "._OB")
    rn, ra = os.path.join(NW, nome + "._RC"), os.path.join(AW, nome + "._RC")
    if not (os.path.exists(pa) and os.path.exists(rn) and os.path.exists(ra)):
        return "arena nao tem"
    if md5(rn) != md5(ra) and not forcar:
        return "malha diferente"
    dn, da = open(pn, "rb").read(), open(pa, "rb").read()
    sn, sa = slots(dn), slots(da)
    if not sn or not sa:
        return "sem slots"
    nosso = sn[0][1][INI:FIM]
    deles = sa[0][1][INI:FIM]
    if nosso == deles:
        return "ja igual"
    if verboso:
        f = lambda b: " ".join("%6.2f" % x for x in struct.unpack_from("<7f", b, 0))
        print("   %-26s nosso %s" % (nome, f(nosso)))
        print("   %-26s arena %s" % ("", f(deles)))
    if aplicar:
        saida = bytearray(dn)
        for i, (off, _dados) in enumerate(sn):
            fonte = sa[i][1] if i < len(sa) else sa[-1][1]
            saida[off + INI:off + FIM] = fonte[INI:FIM]
        open(pn, "wb").write(bytes(saida))
    return "ok"


def main() -> int:
    ap = argparse.ArgumentParser(description="Ancoragem do brilho, copiada do Arena.")
    ap.add_argument("modelo", nargs="?")
    ap.add_argument("--todos", action="store_true")
    ap.add_argument("--aplicar", action="store_true")
    ap.add_argument("--mesmo-com-malha-diferente", action="store_true",
                    help="copia a ancoragem mesmo quando o ._RC nao bate; e um palpite")
    args = ap.parse_args()
    import collections
    c = collections.Counter()
    alvos = ([f[:-4] for f in sorted(os.listdir(NW)) if f.endswith("._OB")]
             if args.todos else [args.modelo])
    for nome in alvos:
        d = open(os.path.join(NW, nome + "._OB"), "rb").read()
        if not re.search(rb"Weapon_Enchant01", d):
            continue
        c[trata(nome, args.aplicar, verboso=True,
                forcar=args.mesmo_com_malha_diferente)] += 1
    for k, v in c.most_common():
        print("   %-20s %d" % (k, v))
    if not args.aplicar:
        print("\nnada gravado. junte --aplicar")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
