# -*- coding: utf-8 -*-
r"""Acrescenta os niveis 14 a 18 DENTRO do ItemEnchant de 2019, sem trocar o ficheiro.

    python tools/mesclar_itemenchant.py            mostra
    python tools/mesclar_itemenchant.py --aplicar  grava no client

PORQUE NAO SE COPIA O DELES. Copiou-se, em 31/08, e o client deixou de desenhar o refino --
nem o +13, que funcionava antes. O ficheiro do Arena tem o mesmo formato de registo (32
bytes, medido) mas o CABECALHO do .lod e outro: cada .lod traz a sua tabela de substituicao
de 31 bytes e a chave inicial da cifra, e o corpo tem de ser recifrado com os do ficheiro que
se esta a substituir.

A PRIMEIRA MESCLAGEM (01/09 16:23) TAMBEM PARTIU O +13, por tres motivos ja corrigidos:
  1. colava no fim os 96 bytes de sobra do corpo original -- ruido de enchimento da cifra,
     nao registos; o ficheiro do Arena nao tem sobra nenhuma;
  2. punha os 25 registos novos a seguir aos 65 nossos, quebrando a ordenacao por
     (bloco, nivel, categoria) que o nosso ficheiro tem;
  3. copiava o campo do efeito visual tal como vem do Arena -- 24, 27, 30, 33, 34, 38 e 45,
     ids acima do 23 que e o tecto desta tabela em 2019.

O EFEITO VISUAL, e porque ha duas opcoes. Contados os ficheiros de efeito de encantamento
(Eff_Inchent_Sheld*, DSM_Dagger_Inchent_*) em 01/09: 177 no nosso Character/Effect contra
155 no Effect do Arena, e NENHUM dos deles falta ca. Os ids acima de 23 nao apontam para
nada que este client nao tenha -- so nunca foram pedidos. Por isso:

    --efeitos arena             poe 27, 30, 34, 38, 45 como o Arena
    --efeitos treze   (padrao)  poe o efeito do +13 do mesmo (bloco, categoria)
    --efeitos amostra           poe cinco ids POR USAR, um por nivel, para os comparar

TESTADO EM 01/09: o modo `arena` NAO funciona -- com o id 45 o +18 nao brilha de todo. Este
client so resolve ate ao 23. Os dois clients tem exactamente os MESMOS 27 ficheiros de
efeito de encantamento (21 Eff_Inchent_Sheld + 3 DSM_Dagger_Inchent + 3), portanto a tabela
que traduz id em efeito esta no BINARIO, nao nos dados: trazer ficheiros nunca ia resolver.

O modo `amostra` existe porque dez ids dentro do nosso alcance nunca sao usados pela tabela
de refino -- 5, 8, 10, 11, 13, 15, 17, 19, 20 e 22. Sao brilhos que este client sabe
desenhar e que o jogo de 2019 nunca mostrou. Poe-se um em cada nivel novo para se ver os
cinco lado a lado e escolher.

Formato do registo, conferido byte a byte contra o Arena (sao identicos ate ao +13):
    bloco(u32) nivel(u32) categoria(u32) valor(double) maximo(double) efeito(u32)
"""
import argparse
import os
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 lod_crypto as L                                              # noqa: E402

ORIG = os.path.join(RAIZ, "backups", "antes_skills_arena_20260831_2033",
                    "client_IE_Lod", "ItemEnchant.lod_pt")
ARENA = r"C:\Games\Arena\ResUnpak\Interface\IE_Lod\itemEnchant.lod_en"
DEST = r"C:\Games\Axion\ResUnpak\Interface\IE_Lod"
TAM = 32
OFF_EFEITO = 28
TETO_NOSSO = 13
# ids que a tabela de refino nunca usa, um por nivel novo -- ver --efeitos amostra
AMOSTRA = {14: 5, 15: 11, 16: 17, 17: 20, 18: 22}


def registos(d):
    """Devolve [(bloco, nivel, categoria, bytes)] pela contagem do cabecalho."""
    n = struct.unpack_from("<I", d, 0)[0]
    out = []
    for i in range(n):
        off = 4 + i * TAM
        if off + TAM > len(d):
            break
        bl, nv, cat = struct.unpack_from("<3I", d, off)
        out.append((bl, nv, cat, bytes(d[off:off + TAM])))
    return out


def main() -> int:
    ap = argparse.ArgumentParser(description="Niveis 14-18 dentro do ItemEnchant de 2019.")
    ap.add_argument("--aplicar", action="store_true")
    ap.add_argument("--efeitos", choices=("arena", "treze", "amostra"), default="treze",
                    help="de onde vem o id do efeito visual dos niveis 14-18")
    args = ap.parse_args()

    cru19 = open(ORIG, "rb").read()
    d19 = L.decrypt_lod_stream(cru19)
    d26 = L.decrypt_lod_stream(open(ARENA, "rb").read(), xor=L.XOR_ARENA)
    r19, r26 = registos(d19), registos(d26)
    print("2019: %d registos (ate +%d)" % (len(r19), max(r[1] for r in r19)))
    print("2026: %d registos (ate +%d)" % (len(r26), max(r[1] for r in r26)))

    # efeito visual do nivel 13 de cada (bloco, categoria) -- o teto que este client conhece
    efeito13 = {}
    for bl, nv, cat, b in r19:
        if nv == TETO_NOSSO:
            efeito13[(bl, cat)] = struct.unpack_from("<I", b, OFF_EFEITO)[0]

    novos = []
    for bl, nv, cat, b in r26:
        if nv <= TETO_NOSSO:
            continue
        orig = struct.unpack_from("<I", b, OFF_EFEITO)[0]
        if args.efeitos == "arena":
            troca = orig
        elif args.efeitos == "amostra":
            troca = AMOSTRA.get(nv, efeito13.get((bl, cat), 0)) if cat != 25                 else efeito13.get((bl, cat), 0)
        else:
            troca = efeito13.get((bl, cat), 0)
        b = bytearray(b)
        struct.pack_into("<I", b, OFF_EFEITO, troca)
        novos.append((bl, nv, cat, bytes(b)))
        print("   +%-3d bloco=%d cat=%-3d  efeito %d%s"
              % (nv, bl, cat, troca, "" if troca == orig else "  (arena tinha %d)" % orig))

    todos = sorted(r19 + novos, key=lambda r: (r[0], r[1], r[2]))
    corpo = bytearray(struct.pack("<I", len(todos)))
    for _bl, _nv, _cat, b in todos:
        corpo += b
    print("\ncorpo novo: %d bytes = 4 + %d*%d (sem sobra; o original tinha %d de ruido)"
          % (len(corpo), len(todos), TAM, len(d19) - 4 - len(r19) * TAM))

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

    prefixo, tabela, chave = L._extract_header(cru19)
    cifrado = prefixo + L.encrypt_lod_body(bytes(corpo), tabela, chave)
    n = 0
    for f in os.listdir(DEST):
        if f.lower().startswith("itemenchant.lod_"):
            open(os.path.join(DEST, f), "wb").write(cifrado)
            n += 1
    print("gravado em %d ficheiros, %d bytes cada" % (n, len(cifrado)))

    r = registos(L.decrypt_lod_stream(cifrado))
    ordem_ok = r == sorted(r, key=lambda x: (x[0], x[1], x[2]))
    print("releitura: %d registos, ate +%d, ordenado=%s"
          % (len(r), max(x[1] for x in r), ordem_ok))
    return 0


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