# -*- coding: utf-8 -*-
r"""Poe o brilho de refino a ir ate +18 em TODAS as armas.

    python tools/brilho_refino_armas.py             mostra o plano
    python tools/brilho_refino_armas.py --aplicar   executa
    python tools/brilho_refino_armas.py --reverter  desfaz (usa os .bak13)

DUAS VIAS, e a primeira e a boa:

  COPIAR do Arena  quando ele tem o mesmo modelo E o `._RC` (a geometria) e byte a byte
                   igual ao nosso. E entao literalmente a mesma arma, com o ficheiro de
                   descricao certo. Foi assim que se resolveu a HKF_SET_01_SWORD.
  CONVERTER o nosso  quando o Arena nao o tem, ou tem outra geometria. Trocam-se os 13
                   nomes de efeito pelos Enchant_<familia>_06.._18 e acrescentam-se 5
                   entradas em cada lista, com os contadores a acompanhar.

PORQUE A ESPADA NAO BRILHAVA -- e nao era do refino. A HKF_SET_01_SWORD era o UNICO modelo
deste client com `OBJECT TYPE : ACT` entre os que tem lista de encantamento; todos os outros
sao FIX, e no Arena esse mesmo modelo tambem e FIX. Objectos ACT nao desenham o brilho. A
espada inicial nunca brilhou, com refino nenhum, muito antes de mexermos nisto.

AS DEPENDENCIAS VAO JUNTO. O modelo do Arena pedia `Trace_01.DDS`, que nao existia ca -- sem
ela a arma fica sem a trilha do golpe. Antes de copiar um modelo confere-se o que ele refere
e traz-se o que faltar.

VERIFICACAO ANTES DE GRAVAR. Cada ficheiro tem de reler-se por completo: as duas listas
(efeitos e Weapon_EnchantNN) fecham com o numero de entradas que o contador anuncia, e sem
passar do fim. Sem este teste, em 01/09, ficaram 11 modelos com o contador a dizer 18 e so
10 entradas, e o client TRAVAVA a caminho da tela de personagem -- carrega os modelos todos
no arranque, e um mal formado leva o resto a frente.
"""
import argparse
import hashlib
import os
import re
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"))
from refino18_modelo import converter, coerente                     # noqa: E402

NW = r"C:\Games\Axion\ResUnpak\Character\Weapon"
AW = r"C:\Games\Arena\ResUnpak\Character\Weapon"
NE = r"C:\Games\Axion\ResUnpak\Character\Effect"
AE = r"C:\Games\Arena\ResUnpak\Effect"
BAK = "._OB.bak13"


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


def catalogo_nosso():
    fora = set()
    for base in (NW, NE, os.path.join(os.path.dirname(NW), "Sound")):
        if os.path.isdir(base):
            fora |= set(x.lower() for x in os.listdir(base))
    return fora


def deps(d):
    return sorted(set(m.group().decode("latin-1") for m in
                      re.finditer(rb"[A-Za-z0-9_\-]{3,40}\.(?:_EF|_RC|_RE|dds|DDS|OGG|ogg)", d)))


def main() -> int:
    ap = argparse.ArgumentParser(description="Brilho de refino ate +18 em todas as armas.")
    ap.add_argument("--aplicar", action="store_true")
    ap.add_argument("--reverter", action="store_true")
    args = ap.parse_args()

    if args.reverter:
        n = 0
        for f in sorted(os.listdir(NW)):
            if f.endswith(BAK):
                shutil.copy2(os.path.join(NW, f), os.path.join(NW, f[:-len(".bak13")]))
                n += 1
        print("revertidos %d modelos a partir dos .bak13" % n)
        return 0

    tenho = catalogo_nosso()
    copiar, converter_lista, trazer = [], [], {}
    for f in sorted(os.listdir(NW)):
        if not f.endswith("._OB"):
            continue
        d = open(os.path.join(NW, f), "rb").read()
        if not re.search(rb"Weapon_Enchant01", d):
            continue
        nome = f[:-4]
        pa = os.path.join(AW, f)
        if os.path.exists(pa):
            da = open(pa, "rb").read()
            rc_n, rc_a = os.path.join(NW, nome + "._RC"), os.path.join(AW, nome + "._RC")
            if (re.search(rb"Enchant_[A-Za-z0-9_]+_\d\d\._EF", da)
                    and os.path.exists(rc_n) and os.path.exists(rc_a)
                    and md5(rc_n) == md5(rc_a)):
                falta = [x for x in deps(da) if x.lower() not in tenho]
                for x in falta:
                    for base in (AW, AE):
                        p = os.path.join(base, x)
                        if os.path.exists(p):
                            trazer[x] = p
                            break
                copiar.append((nome, pa, falta))
                continue
        converter_lista.append(nome)

    print("copiar do Arena (geometria identica): %d" % len(copiar))
    print("converter o nosso                   : %d" % len(converter_lista))
    print("dependencias a trazer               : %d  %s"
          % (len(trazer), ", ".join(sorted(trazer)[:5])))
    semfonte = [x for c in copiar for x in c[2] if x not in trazer]
    if semfonte:
        print("AVISO: %d dependencias sem fonte: %s" % (len(semfonte), sorted(set(semfonte))[:5]))

    if not args.aplicar:
        print("\nnada gravado. junte --aplicar")
        return 0

    for x, origem in sorted(trazer.items()):
        dest = os.path.join(NW if origem.startswith(AW) else NE, x)
        if not os.path.exists(dest):
            shutil.copy2(origem, dest)
    print("\ntrazidas %d dependencias" % len(trazer))

    ok = mau = 0
    # AOS COPIADOS NAO SE APLICA A NOSSA VERIFICACAO DE RELEITURA. Ela serve para apanhar
    # ficheiros que EU escrevo mal; os do Arena sao originais de um jogo a funcionar e tem
    # um formato de cabecalho diferente do nosso -- o campo antes da lista de efeitos tem
    # la 0x44000000 em vez do numero de entradas. Correr-lhes o nosso parser recusava os
    # 185. O que se exige e o que se confirmou na HKF_SET_01_SWORD: geometria (`._RC`)
    # identica byte a byte e todas as dependencias presentes -- ja verificado acima.
    for nome, pa, falta in copiar:
        alvo = os.path.join(NW, nome + "._OB")
        if falta:
            print("   RECUSADO %s: faltam %s" % (nome, falta[:2]))
            mau += 1
            continue
        if not os.path.exists(alvo + ".bak13"):
            shutil.copy2(alvo, alvo + ".bak13")
        shutil.copy2(pa, alvo)
        ok += 1
    print("copiados do Arena: %d (recusados %d)" % (ok, mau))

    import collections
    c = collections.Counter()
    for nome in converter_lista:
        r = converter(os.path.join(NW, nome + "._OB"), True, verboso=False)
        c[r[0] if r else "sem lista"] += 1
    for k, v in c.most_common():
        print("   convertidos: %-34s %d" % (k, v))
    return 0


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