# -*- coding: utf-8 -*-
r"""Leva de 13 para 18 os modelos de arma que guardam as listas em TEXTO.

    python tools/brilho_texto18.py DSM_SET_17_DUALSWORD Sword_1H
    python tools/brilho_texto18.py --todos --aplicar

Doze modelos (as `SET_17` e afins) tem as duas listas em texto legivel:

    #.EFs_SIZE          13
    #.EFFECTIDs        000 0xEBA24A56 DSM_DUALSWORD_11._EF
    #.OEs_SIZE          13
    #.OUT_EFFECTsEX2   000   0 0x00010001 "Weapon_Enchant01" : 0xFFFFFFFF 0x01 0 \
                       <POS> 0.0000 0.0000 0.0000 <ROT> 1.5708 1.5708 0.0000

Com 13 entradas o brilho fica trocado: o client usa `slot = refino`, entao um +18 procura o
slot 18, nao o encontra, e o +13 mostra o efeito que estava na ultima posicao. Foi o que o
jogador viu na adaga.

Acrescentam-se cinco entradas a cada lista e poem-se os dois tamanhos a 18. O hash de cada
nome de efeito e o DJB2 (h=5381; h = h*33 + c), medido nos pares que os proprios ficheiros
trazem. As linhas novas copiam o <POS>/<ROT> da ultima, que e a ancoragem ja acertada.
"""
import argparse
import collections
import os
import re
import shutil

WEAPON = r"C:\Games\Axion\ResUnpak\Character\Weapon"
EFEITOS = r"C:\Games\Axion\ResUnpak\Character\Effect"
EF = re.compile(r"([ \t]*#\.EFFECTIDs\s+)(\d+)(\s+0x)([0-9A-Fa-f]{8})(\s+)([A-Za-z0-9_]+\._EF)")
OE = re.compile(r"([ \t]*#\.OUT_EFFECTsEX2\s+)(\d+)(\s+)(\d+)(\s+0x)([0-9A-Fa-f]{8})"
                r'(\s+")Weapon_Enchant(\d\d)(".*)')
FAM = {"SWORD": "Sword_1H", "DUALSWORD": "Sword_1H", "BLADE": "Sword_2H", "STICK": "Stick",
       "WAND": "Stick", "STAFF": "Stick", "LANCE": "Stick", "HILL": "Stick", "BOW": "Bow",
       "CANNON": "FireArms", "HANDGUN": "FireArms", "RIFLE": "FireArms", "DAGGER": "Dagger",
       "SHELD": "Shield", "SHIELD": "Shield"}


def djb2(s):
    h = 5381
    for c in s:
        h = (h * 33 + ord(c)) & 0xFFFFFFFF
    return h


def familia_de(nome):
    alto = nome.upper()
    for k in sorted(FAM, key=len, reverse=True):
        if re.search(r"(?:^|_)%s(?:\d*)(?:_|$)" % k, alto):
            return FAM[k]
    return None


def trata(nome, fam, aplicar):
    cam = os.path.join(WEAPON, nome + "._OB")
    bruto = open(cam, "rb").read()
    if b"#.EFs_SIZE" not in bruto:
        return "nao e formato de texto"
    txt = bruto.decode("latin-1")
    linhas = txt.split("\n")

    ief = [i for i, l in enumerate(linhas) if EF.match(l)]
    ioe = [i for i, l in enumerate(linhas) if OE.match(l)]
    if not ief or not ioe:
        return "listas nao encontradas"
    if len(ief) >= 18 and len(ioe) >= 18:
        return "ja"

    nomes = ["Enchant_%s_%02d._EF" % (fam, min(18, max(6, k))) for k in range(1, 19)]
    faltam = [x for x in set(nomes) if not os.path.exists(os.path.join(EFEITOS, x))]
    if faltam:
        return "faltam efeitos: %s" % faltam[0]

    molde_ef = EF.match(linhas[ief[-1]])
    novas_ef = []
    for k in range(18):
        m = EF.match(linhas[ief[k]]) if k < len(ief) else molde_ef
        novas_ef.append("%s%03d%s%08X%s%s" % (m.group(1), k, m.group(3),
                                              djb2(nomes[k]), m.group(5), nomes[k]))
    molde_oe = OE.match(linhas[ioe[-1]])
    novas_oe = []
    for k in range(18):
        m = OE.match(linhas[ioe[k]]) if k < len(ioe) else molde_oe
        novas_oe.append("%s%03d%s%d%s%08X%sWeapon_Enchant%02d%s"
                        % (m.group(1), k, m.group(3), k, m.group(5),
                           0x00010001 + k, m.group(7), k + 1, m.group(9)))

    saida = []
    for i, l in enumerate(linhas):
        if i == ief[0]:
            saida.extend(novas_ef)
        elif i == ioe[0]:
            saida.extend(novas_oe)
        elif i in ief or i in ioe:
            continue
        else:
            saida.append(re.sub(r"(#\.(?:EFs|OEs)_SIZE\s+)\d+", r"\g<1>18", l))
    novo = "\n".join(saida).encode("latin-1")

    if aplicar:
        if not os.path.exists(cam + ".bak13"):
            shutil.copy2(cam, cam + ".bak13")
        open(cam, "wb").write(novo)
    return "ok"


def main() -> int:
    ap = argparse.ArgumentParser(description="Modelos em texto: 13 -> 18 niveis de brilho.")
    ap.add_argument("modelo", nargs="?")
    ap.add_argument("familia", nargs="?")
    ap.add_argument("--todos", action="store_true")
    ap.add_argument("--aplicar", action="store_true")
    args = ap.parse_args()

    c = collections.Counter()
    alvos = ([f[:-4] for f in sorted(os.listdir(WEAPON)) if f.endswith("._OB")]
             if args.todos else [args.modelo])
    for nome in alvos:
        d = open(os.path.join(WEAPON, nome + "._OB"), "rb").read()
        if b"#.EFs_SIZE" not in d:
            continue
        fam = args.familia or familia_de(nome)
        if not fam:
            c["sem familia"] += 1
            continue
        r = trata(nome, fam, args.aplicar)
        c[r] += 1
        if not args.todos:
            print("   %s -> %s (familia %s)" % (nome, r, fam))
    for k, v in c.most_common():
        print("   %-28s %d" % (k, v))
    if not args.aplicar:
        print("\nnada gravado. junte --aplicar")
    return 0


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