# -*- coding: utf-8 -*-
r"""Mete as skills novas do Arena DENTRO do .lod de 2019, sem mudar o formato.

    python tools/mesclar_skills_lod.py            mostra o que faria
    python tools/mesclar_skills_lod.py --aplicar  escreve em backups/client_mesclado/

PORQUE ISTO E MELHOR DO QUE COPIAR O DO ARENA. Copiar o ficheiro deles inteiro traz duas
coisas que nao se quer: o rebalanceamento (a Pocao do vento la da +30% e nos MEDIMOS +45% no
oficial) e o risco de o client de 2019 nao ler um ficheiro de 2026.

O FORMATO E O MESMO NOS DOIS -- medido: bloco binario de 284 bytes, as cadeias a comecar no
offset 292, e o primeiro registo identico em 291 dos 292 bytes. Entao da para ficar com o
NOSSO ficheiro e so acrescentar os registos das skills que la nao existem.

    2019   4623 registos      as nossas, com os valores medidos
    2026   8851 registos      as deles
    saida  4623 + as novas    formato de 2019, conteudo dos dois

ESTRUTURA DO REGISTO (ver tools/extrair_skills_lod.py, que a decifrou):

    284 bytes de bloco binario   (o skill_id nos primeiros 4)
    18 cadeias numericas         (u32 de comprimento + texto)
    1  cadeia de descricao
"""
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

BLOCO = 284
N_CADEIAS = 19          # 18 numericas + a descricao
ORIG_2019 = os.path.join(RAIZ, "backups", "antes_skills_arena_20260831_2033",
                         "client_IE_Lod")
ARENA = r"C:\Games\Arena\ResUnpak\Interface\IE_Lod"
DESTINO = os.path.join(RAIZ, "backups", "client_mesclado")


def ler_registos(d):
    """[(skill_id, bytes do registo)] e o offset onde acabou."""
    i = 8
    out = []
    n = struct.unpack_from("<I", d, 4)[0]
    for _ in range(n):
        ini = i
        if i + BLOCO > len(d):
            break
        sid = struct.unpack_from("<I", d, i)[0]
        i += BLOCO
        for _c in range(N_CADEIAS):
            if i + 4 > len(d):
                break
            ln = struct.unpack_from("<I", d, i)[0]
            i += 4 + ln
        out.append((sid, bytes(d[ini:i])))
    return out, i


def montar(cabecalho, registos):
    """Refaz o ficheiro decifrado: assinatura, contagem e os registos."""
    p = bytearray(cabecalho[:8])
    struct.pack_into("<I", p, 4, len(registos))
    for _sid, r in registos:
        p += r
    return bytes(p)


def main() -> int:
    ap = argparse.ArgumentParser(description="Skills novas dentro do .lod de 2019.")
    ap.add_argument("--aplicar", action="store_true")
    args = ap.parse_args()

    for nome, pular in (("skillLevel", False), ("skillBase", True)):
        d19 = L.decrypt_lod_stream(
            open(os.path.join(ORIG_2019, nome + ".lod_pt"), "rb").read(), pular_zero=pular)
        d26 = L.decrypt_lod_stream(
            open(os.path.join(ARENA, nome + ".lod_en"), "rb").read(),
            pular_zero=pular, xor=L.XOR_ARENA)
        r19, fim19 = ler_registos(d19)
        r26, fim26 = ler_registos(d26)
        print("%-12s 2019: %d registos (li ate %d de %d)"
              % (nome, len(r19), fim19, len(d19)))
        print("%-12s 2026: %d registos (li ate %d de %d)"
              % ("", len(r26), fim26, len(d26)))
        if nome != "skillLevel":
            # o skillBase tem outra estrutura de cadeias; so o skillLevel se mescla aqui
            print("             (o skillBase nao se mescla por este caminho -- ver abaixo)")
            print()
            continue
        tenho = set(s for s, _ in r19)
        novos = [(s, r) for s, r in r26 if s not in tenho]
        print("             skills novas a acrescentar: %d (de %d distintas la)"
              % (len(set(s for s, _ in novos)), len(set(s for s, _ in r26))))
        saida = r19 + novos
        print("             resultado: %d registos, formato de 2019" % len(saida))
        if args.aplicar:
            os.makedirs(DESTINO, exist_ok=True)
            corpo = montar(d19, saida)
            cru = open(os.path.join(ORIG_2019, nome + ".lod_pt"), "rb").read()
            cifrado = L.patch_lod_bytes(cru, corpo) if hasattr(L, "patch_lod_bytes") else None
            if cifrado is None:
                print("             NAO GRAVADO: falta a funcao de recifrar -- ver abaixo")
            else:
                open(os.path.join(DESTINO, nome + ".lod_pt"), "wb").write(cifrado)
                print("             gravado em %s" % DESTINO)
        print()
    if not args.aplicar:
        print("nada gravado. corra com --aplicar")
    return 0


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