"""Extrai TODOS os personagens de uma captura e monta a tabela de classes.

    python extrair_classes.py [traffic.log]

Para cada personagem visto na captura, junta o que veio nos tres pacotes:

    02 02  criacao        classe, aparencia, nome (o que o jogador escolheu)
    02 01  lista          id, classe, nome
    02 05  info completa  id, classe, HP maximo, MP maximo -- 1812 bytes

Salva um MOLDE do 02 05 por classe em captures/classes/. Com um molde real por classe, o
emulador serve aparencia e equipamento corretos sem precisar decodificar os 1808 bytes que
ainda nao entendemos.
"""
import json
import os
import struct
import sys

sys.path.insert(0, r"D:\RoshEmulator\tools")
from analisar_criacao import blocos, frames

PADRAO = r"C:\RoshHook\traffic.log"
SAIDA = r"D:\RoshEmulator\captures\classes"


def nome_do(buf, base):
    n = struct.unpack_from("<H", buf, base)[0]
    if 1 <= n <= 20 and base + 2 + n * 4 <= len(buf):
        try:
            return buf[base + 2:base + 2 + n * 4].decode("utf-32-le")
        except UnicodeDecodeError:
            pass
    return None


def main():
    caminho = sys.argv[1] if len(sys.argv) > 1 else PADRAO
    os.makedirs(SAIDA, exist_ok=True)
    criados, listas, infos = [], {}, {}

    for ts, d, buf in blocos(caminho):
        for f in frames(buf):
            if len(f) < 2:
                continue
            op = (f[0], f[1])
            if d == "SEND" and op == (2, 2) and len(f) >= 21:
                nome = nome_do(f, 19)
                if nome:
                    criados.append({"classe": struct.unpack_from("<H", f, 6)[0],
                                    "aparencia": f[16], "nome": nome, "hora": ts})
            elif d == "RECV" and op == (2, 1) and len(f) > 380:
                cid = struct.unpack_from(">I", f, 7)[0]
                if cid:
                    nome = None
                    for base in range(len(f) - 30, max(0, len(f) - 120), -1):
                        nome = nome_do(f, base)
                        if nome:
                            break
                    listas[cid] = {"id": cid, "classe": struct.unpack_from("<I", f, 15)[0],
                                   "nome": nome, "bytes": f}
            elif d == "RECV" and op == (2, 5) and len(f) >= 34:
                cid = struct.unpack_from(">I", f, 6)[0]
                infos[cid] = {"id": cid, "classe": struct.unpack_from("<I", f, 10)[0],
                              "hp_max": struct.unpack_from("<I", f, 22)[0],
                              "mp_max": struct.unpack_from("<I", f, 30)[0],
                              "bytes": f}

    print(f"{caminho}\n")
    print(f"criacoes (02 02) : {len(criados)}")
    for c in criados:
        print(f"    {c['hora']}  classe={c['classe']:<5} aparencia=0x{c['aparencia']:02x}  "
              f"'{c['nome']}'")
    print(f"\nlistas (02 01)   : {len(listas)} personagem(ns)")
    for c in listas.values():
        print(f"    id={c['id']:<7} classe={c['classe']:<5} nome={c['nome']!r}")
    print(f"\ninfos (02 05)    : {len(infos)} personagem(ns)")

    tabela = {}
    for cid, info in infos.items():
        classe = info["classe"]
        nome = (listas.get(cid) or {}).get("nome")
        tabela[classe] = {"classe": classe, "nome_exemplo": nome,
                          "hp_max": info["hp_max"], "mp_max": info["mp_max"]}
        alvo = os.path.join(SAIDA, f"charinfo_classe_{classe}.bin")
        with open(alvo, "wb") as f:
            f.write(info["bytes"])
        lista = listas.get(cid)
        if lista:
            with open(os.path.join(SAIDA, f"charlist_classe_{classe}.bin"), "wb") as f:
                f.write(lista["bytes"])
        print(f"    classe {classe:<5} id={cid:<7} HP={info['hp_max']:<5} MP={info['mp_max']:<5}"
              f"  nome={nome!r}  -> molde salvo")

    # junta com o que ja tinhamos de capturas anteriores
    jaTem = os.path.join(SAIDA, "tabela_classes.json")
    if os.path.exists(jaTem):
        with open(jaTem, encoding="utf-8") as f:
            antigo = json.load(f)
        antigo.update({str(k): v for k, v in tabela.items()})
        tabela_final = antigo
    else:
        tabela_final = {str(k): v for k, v in tabela.items()}
    with open(jaTem, "w", encoding="utf-8") as f:
        json.dump(tabela_final, f, indent=2, ensure_ascii=False)

    print(f"\n=== TABELA DE CLASSES ACUMULADA ({len(tabela_final)} de 7) ===")
    for k in sorted(tabela_final, key=int):
        v = tabela_final[k]
        print(f"  classe {k:<5} HP={v['hp_max']:<5} MP={v['mp_max']:<5} "
              f"exemplo={v.get('nome_exemplo')!r}")
    faltam = [c for c in (100, 200, 300, 400, 500, 600, 800) if str(c) not in tabela_final]
    if faltam:
        print(f"\n  FALTAM: {faltam}")
    print(f"\n  tabela em {jaTem}")


if __name__ == "__main__":
    main()
