# -*- coding: utf-8 -*-
r"""Troca SO os nomes dos efeitos de refino de um modelo de arma. Nada mais.

    python tools/brilho_trocar_nomes.py HAM_SET_03_LANCE Stick
    python tools/brilho_trocar_nomes.py HAM_SET_03_LANCE Stick --aplicar

PARA OS MODELOS QUE FOGEM AO FORMATO. Quatro armas ficaram de fora da conversao normal: duas
tem 10 e 12 efeitos em vez de 13, e nas outras duas (os `SET_17`) o contador nao esta onde
esta nas restantes. Mexer-lhes nos contadores foi o que travou o client em 01/09.

Aqui nao se mexe em contador nenhum nem se acrescentam entradas: leem-se os N nomes que la
estao e escrevem-se N nomes novos por cima, os ULTIMOS N niveis ate ao 18. E o que os
modelos do Arena fazem -- 13 entradas para os niveis 6..18 -- e o que se confirmou a
funcionar quando se copiaram 185 modelos dele para ca.

Como os nomes novos podem ter outro comprimento, o ficheiro muda de tamanho; o campo `len`
de cada entrada acompanha, que e para isso que existe.
"""
import argparse
import os
import shutil
import struct
import sys

RAIZ = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(RAIZ, "tools"))
import re                                                           # noqa: E402

WEAPON = r"C:\Games\Axion\ResUnpak\Character\Weapon"
EFEITOS = r"C:\Games\Axion\ResUnpak\Character\Effect"


def main() -> int:
    ap = argparse.ArgumentParser(description="Troca os nomes dos efeitos de refino.")
    ap.add_argument("modelo")
    ap.add_argument("familia", help="Stick, Bow, Dagger, Shield, FireArms, Sword_1H, Sword_2H")
    ap.add_argument("--aplicar", action="store_true")
    args = ap.parse_args()

    cam = os.path.join(WEAPON, args.modelo + "._OB")
    d = open(cam, "rb").read()
    me = re.search(rb"[A-Za-z0-9_]{4,40}\._EF", d)
    if not me:
        print("sem lista de efeitos")
        return 1

    # percorre as entradas [valor 4][len 4][nome] a partir da primeira, ate deixar de bater
    ini = me.start() - 8
    o, velhos = ini, []
    while o + 8 <= len(d):
        ln = struct.unpack_from("<I", d, o + 4)[0]
        if not (1 <= ln <= 60) or o + 8 + ln > len(d):
            break
        nome = d[o + 8:o + 8 + ln]
        if not nome.endswith(b"._EF"):
            break
        velhos.append((d[o:o + 4], nome))
        o = o + 8 + ln
    fim = o
    n = len(velhos)
    print("%s: %d bytes, %d efeitos de %d a %d" % (args.modelo, len(d), n, ini, fim))

    # com 13 ou menos entradas usam-se os ultimos N niveis ate ao 18; com 18 (os modelos
    # que ja convertemos) o slot N e o refino N, e os niveis 1..5 repetem o efeito do 6,
    # que e o mais fraco que existe.
    niveis = (list(range(6, 19))[-n:] if n <= 13
              else [min(18, max(6, k)) for k in range(1, n + 1)])
    novos = ["Enchant_%s_%02d._EF" % (args.familia, k) for k in niveis]
    if len(novos) != n:
        print("nao consigo mapear %d entradas" % n)
        return 1
    faltam = [x for x in novos if not os.path.exists(os.path.join(EFEITOS, x))]
    if faltam:
        print("faltam efeitos: %s" % faltam)
        return 1

    bloco = b""
    for (val, velho), novo in zip(velhos, novos):
        bloco += val + struct.pack("<I", len(novo)) + novo.encode("latin-1")
        print("   %-24s -> %s" % (velho.decode(), novo))
    saida = d[:ini] + bloco + d[fim:]
    print("ficheiro: %d -> %d bytes (contadores intactos)" % (len(d), len(saida)))

    if not args.aplicar:
        print("nada gravado. junte --aplicar")
        return 0
    if not os.path.exists(cam + ".bak13"):
        shutil.copy2(cam, cam + ".bak13")
    open(cam, "wb").write(saida)
    print("gravado.")
    return 0


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