# -*- coding: utf-8 -*-
r"""Vigia a gravacao do NOSSO servidor ao vivo e resume o que vai passando.

    python tools/vigiar.py            [ficheiro]  [segundos entre relatorios]

Le so o que foi acrescentado desde a ultima passagem, portanto pode ficar a correr enquanto
se joga. Serve para nao ter de parar o jogo, fechar o gravador e analisar depois.

Segue com atencao o que tem dado problema: a vida do jogador pelos DOIS caminhos (08 01 e
08 06 -- ver analisar_captura.seccao_regeneracao), o movimento dos monstros, e os opcodes
que nunca vimos.
"""
import collections
import os
import re
import struct
import sys
import time

CAB = re.compile(r"\[([\d:.]+)\] (SEND|RECV) sock=")
HEX = re.compile(r"(?:[0-9A-Fa-f]{2} )+\s*")


def seg(t):
    h, m, s = t.split(":")
    return int(h) * 3600 + int(m) * 60 + float(s)


def quadros(texto):
    atual, blocos = None, []
    for l in texto.splitlines():
        m = CAB.match(l)
        if m:
            atual = [m.group(1), m.group(2), bytearray()]
            blocos.append(atual)
            continue
        if atual is not None and HEX.fullmatch(l + " "):
            try:
                atual[2].extend(bytes.fromhex(l.strip()))
            except ValueError:
                pass
    for t, d, b in blocos:
        pos = 0
        while pos + 4 <= len(b):
            (n,) = struct.unpack_from(">I", b, pos)
            if n == 0 or n > 65535 or pos + 4 + n > len(b):
                break
            yield seg(t), d, bytes(b[pos + 4:pos + 4 + n])
            pos += 4 + n


CONHECIDOS = {
    "02 00", "02 01", "02 04", "02 05", "02 06", "02 07", "02 0b", "03 00", "03 02",
    "04 00", "04 01", "04 02", "04 03", "04 04", "05 00", "05 01", "05 02", "05 04",
    "05 05", "05 0c", "06 00", "06 01", "06 03", "06 04", "06 05", "06 06", "06 25",
    "06 28", "08 00", "08 01", "08 02", "08 06", "08 07", "08 08", "09 02", "09 03",
    "09 04", "0a 0f", "0b 00", "0d 0f", "0e 0c", "0f 11", "16 01",
}


def main() -> int:
    cam = sys.argv[1] if len(sys.argv) > 1 else r"C:\RoshHook2\traffic.log"
    espera = float(sys.argv[2]) if len(sys.argv) > 2 else 10.0
    print("a vigiar %s (relatorio de %.0f em %.0f s). Ctrl+C para parar." % (cam, espera, espera))
    lido = 0
    eu = None
    vida = []
    while True:
        try:
            tam = os.path.getsize(cam)
        except OSError:
            print("   (ainda nao ha ficheiro -- abre o jogo)")
            time.sleep(espera)
            continue
        if tam <= lido:
            time.sleep(espera)
            continue
        with open(cam, encoding="latin-1", errors="replace") as f:
            f.seek(lido)
            novo = f.read()
        lido = tam
        ops = collections.Counter()
        movs = collections.defaultdict(list)
        for t, d, c in quadros(novo):
            if d != "RECV" or len(c) < 2:
                continue
            k = "%02x %02x" % (c[0], c[1])
            ops[k] += 1
            if k == "08 01" and len(c) >= 44:
                if eu is None:
                    eu = struct.unpack_from(">I", c, 7)[0]
                vida.append((t, struct.unpack_from("<I", c, 15)[0],
                             struct.unpack_from("<I", c, 19)[0], "08 01"))
            elif k == "08 06" and len(c) >= 19 and eu is not None:
                if struct.unpack_from(">I", c, 8)[0] == eu:
                    vida.append((t, struct.unpack_from(">I", c, 15)[0], 0, "08 06"))
            elif k == "04 01" and len(c) == 42:
                movs[struct.unpack_from(">I", c, 8)[0]].append(
                    (t, struct.unpack_from("<3f", c, 12), struct.unpack_from("<3f", c, 24)))
        if not ops:
            time.sleep(espera)
            continue
        print()
        print("--- +%d bytes, %d pacotes ---" % (len(novo), sum(ops.values())))
        novos = sorted(set(ops) - CONHECIDOS)
        if novos:
            print("   OPCODES NUNCA VISTOS: %s" % novos)
        print("   %s" % ", ".join("%s x%d" % (k, v) for k, v in ops.most_common(8)))
        if vida:
            v = vida[-6:]
            print("   vida: %s" % " -> ".join("%d(%s)" % (hp, f) for _t, hp, _m, f in v))
        if movs:
            import math
            av = []
            for ms in movs.values():
                for i in range(len(ms) - 1):
                    g = ms[i + 1][0] - ms[i][0]
                    if 0.2 < g < 1.5:
                        av.append((math.dist(ms[i][1][:2], ms[i + 1][1][:2]), g))
            if av:
                av.sort()
                p, g = av[len(av) // 2]
                print("   monstros: %d a mexer, avanco mediano %.2f, intervalo %.3f s -> %.1f u/s"
                      % (len(movs), p, g, p / g if g else 0))
        time.sleep(espera)
    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except KeyboardInterrupt:
        print()
        print("parado")
