# -*- coding: utf-8 -*-
r"""Extrai a sequencia de combate de uma captura: dano por golpe, HP do alvo e EXP ganha.

Serve pra comparar o nosso servidor com o oficial no mesmo gesto -- bater numa aranha ate
matar -- em vez de comparar numero solto.

Uso:
    python analisar_combate.py "C:\RoshHook\traffic.log"
    python analisar_combate.py oficial.log nosso.log     (lado a lado)

Pacotes lidos (todos ja decodificados em docs/findings.md):
    05 00  ataque comeca      [33:37] alvo BE
    05 01  golpe conecta      [34:38] alvo BE
    05 02  numero de dano     [24:28] dano BE
    08 06  barra de HP        [7:11] entidade  [15:19] HP  [19:23] HP maximo
    08 01  gage do heroi      [66:70] EXP BE   [43] nivel
"""
import struct
import sys


def frames(caminho):
    import re
    ev, cur = [], None
    for linha in open(caminho, encoding="latin-1"):
        m = re.match(r"\[([\d:.]+)\] (SEND|RECV) sock=(0x[0-9A-Fa-f]+) len=(\d+)", linha)
        if m:
            cur = [m.group(1), m.group(2), m.group(3), bytearray()]
            ev.append(cur)
            continue
        if cur is not None and re.fullmatch(r"(?:[0-9A-Fa-f]{2} )+\s*", linha):
            cur[3].extend(bytes.fromhex(linha.strip()))
    por = {}
    for hora, direcao, sock, dados in ev:
        por.setdefault((sock, direcao), bytearray()).extend(dados)
    saida = []
    for (sock, direcao), fluxo in por.items():
        pos = 0
        while pos + 4 <= len(fluxo):
            (t,) = struct.unpack_from(">I", fluxo, pos)
            if t == 0 or t > 8192 or pos + 4 + t > len(fluxo):
                pos += 1
                continue
            saida.append((direcao, bytes(fluxo[pos + 4:pos + 4 + t])))
            pos += 4 + t
    return saida


def analisar(caminho, rotulo):
    print(f"===== {rotulo} =====")
    danos, hps, exps = [], [], []
    for direcao, c in frames(caminho):
        if direcao != "RECV":
            continue
        op = c[:2]
        if op == bytes([5, 2]) and len(c) >= 28:
            danos.append(struct.unpack_from(">I", c, 24)[0])
        elif op == bytes([8, 6]) and len(c) >= 23:
            ent = struct.unpack_from(">I", c, 7)[0]
            hp = struct.unpack_from(">I", c, 15)[0]
            hpm = struct.unpack_from(">I", c, 19)[0]
            if hpm and hpm < 100000:
                hps.append((ent, hp, hpm))
        elif op == bytes([8, 1]) and len(c) == 109:
            exps.append((c[43], struct.unpack_from(">I", c, 66)[0]))
    if danos:
        print(f"  golpes: {len(danos)}   dano: min {min(danos)} max {max(danos)} "
              f"media {sum(danos)/len(danos):.1f}")
        print(f"  sequencia: {danos[:30]}")
    else:
        print("  nenhum 05 02 (numero de dano) na captura")
    if hps:
        # so a primeira entidade que apanhou, pra nao misturar mobs
        alvo = hps[0][0]
        serie = [h for e, h, _ in hps if e == alvo]
        print(f"  HP do alvo {alvo}: {serie[:20]} (maximo {hps[0][2]})")
    if exps:
        vistos = []
        for nivel, exp in exps:
            if not vistos or vistos[-1] != (nivel, exp):
                vistos.append((nivel, exp))
        print(f"  (nivel, EXP) ao longo da sessao: {vistos[:14]}")
    print()


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(__doc__)
        raise SystemExit(1)
    for i, cam in enumerate(sys.argv[1:]):
        analisar(cam, cam.split("\\")[-1])
