# -*- coding: utf-8 -*-
r"""Extrai o fluxo de ENTRADA NO MUNDO de uma captura do oficial, cru.

    python tools/extrair_stream_entrada.py <captura.log> <saida.bin>

O que sai: todos os frames que o servidor mandou entre o `02 04` do client (pedido de
entrar no mundo) e o `04 00` dele (chegou). E o mesmo recorte do
captures/enterworld_loop_v1.bin que o world_server ja usa, so que tirado de outra sessao.

PARA QUE SERVE. Em 30/08 a velocidade de movimento ficou por resolver depois de catorze
hipoteses: o client nunca passa da base no nosso servidor, e passa no oficial, com o MESMO
executavel e as MESMAS tabelas. Tudo o que se comparou pacote a pacote bate.

A experiencia que falta e mandar o fluxo do oficial TAL E QUAL, sem substituicao nenhuma
alem da identidade, e ver se o client corre. Se correr, a diferenca esta no que a gente
troca; se nao correr, o fluxo nao e o problema e o alvo muda outra vez.

Escolhe-se uma captura em que o jogador tinha velocidade ALTA -- a de 30/08 15:05 declara
99,0 na ficha e ele andou a 98,0 medidos.
"""
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", "analise"))
import ler_hook as H                                                # noqa: E402


def extrair(cam):
    """(bytes do fluxo, entidade do jogador, quantos frames)."""
    eventos = []
    for d in ("RECV", "SEND"):
        for t, op, c in H.pacotes(cam, d):
            eventos.append((t, d, op, c))
    eventos.sort(key=lambda x: x[0])

    # a entidade do jogador: a do 08 01, que e a ficha do proprio
    import collections
    quem = collections.Counter()
    for _t, d, op, c in eventos:
        if d == "RECV" and op == bytes([8, 1]) and len(c) >= 11:
            quem[struct.unpack_from(">I", c, 7)[0]] += 1
    entidade = quem.most_common(1)[0][0] if quem else 0

    ini = fim = None
    for i, (t, d, op, c) in enumerate(eventos):
        if ini is None and d == "SEND" and op == bytes([2, 4]):
            ini = i
            continue
        if ini is not None and d == "SEND" and op == bytes([4, 0]):
            fim = i
            break
    if ini is None:
        raise SystemExit("nao achei o 02 04 do client nesta captura")
    if fim is None:
        fim = len(eventos)

    saida = bytearray()
    n = 0
    for t, d, op, c in eventos[ini + 1:fim]:
        if d != "RECV":
            continue
        saida += struct.pack(">I", len(c)) + c
        n += 1
    return bytes(saida), entidade, n


def main() -> int:
    if len(sys.argv) < 3:
        print(__doc__)
        return 1
    cam, destino = sys.argv[1], sys.argv[2]
    dados, ent, n = extrair(cam)
    with open(destino, "wb") as f:
        f.write(dados)
    print("captura : %s" % os.path.basename(cam))
    print("entidade do jogador: %d (0x%X)" % (ent, ent))
    print("frames  : %d" % n)
    print("gravado : %s (%d bytes)" % (destino, len(dados)))
    return 0


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