"""Casa cada float do 02 05 com os valores das fichas de atributos das 7 classes.

Regra: um offset so e aceito se casar em TODAS as 7 classes. Com sete amostras, coincidencia
e praticamente impossivel -- foi assim que HP, MP e os atributos base foram amarrados.

Valores inteiros na tela sao truncados (13.75 aparece como 13), entao comparamos com floor.
Os que a tela mostra com decimal (critico, velocidade) tem que bater exato.
"""
import glob
import re
import struct

# lidos das fichas de atributos, uma por classe
FICHAS = {
    100: dict(saude=190, mana=60,  reg_hp=13, reg_mp=1, evasao=6, bloqueio=8,
              atq_f_min=19, atq_f_max=25, prot_f=67, atq_m_min=8,  atq_m_max=8,
              prot_m=18, critico=5.2, mult=1.2, vel_atq=0.91, vel_inv=100),
    200: dict(saude=190, mana=91,  reg_hp=13, reg_mp=2, evasao=6, bloqueio=8,
              atq_f_min=14, atq_f_max=19, prot_f=72, atq_m_min=9,  atq_m_max=9,
              prot_m=19, critico=5.2, mult=1.2, vel_atq=1.10, vel_inv=100),
    300: dict(saude=150, mana=80,  reg_hp=10, reg_mp=2, evasao=7, bloqueio=9,
              atq_f_min=15, atq_f_max=17, prot_f=63, atq_m_min=11, atq_m_max=11,
              prot_m=19, critico=7.1, mult=1.2, vel_atq=1.57, vel_inv=100),
    400: dict(saude=160, mana=90,  reg_hp=11, reg_mp=2, evasao=8, bloqueio=8,
              atq_f_min=11, atq_f_max=16, prot_f=56, atq_m_min=27, atq_m_max=39,
              prot_m=19, critico=6.1, mult=1.2, vel_atq=0.85, vel_inv=100),
    500: dict(saude=160, mana=70,  reg_hp=11, reg_mp=2, evasao=6, bloqueio=8,
              atq_f_min=19, atq_f_max=25, prot_f=58, atq_m_min=10, atq_m_max=10,
              prot_m=19, critico=6.6, mult=1.2, vel_atq=0.78, vel_inv=100),
    600: dict(saude=150, mana=90,  reg_hp=10, reg_mp=5, evasao=6, bloqueio=8,
              atq_f_min=11, atq_f_max=15, prot_f=54, atq_m_min=30, atq_m_max=35,
              prot_m=19, critico=5.7, mult=1.2, vel_atq=0.85, vel_inv=100),
    800: dict(saude=189, mana=117, reg_hp=13, reg_mp=3, evasao=6, bloqueio=8,
              atq_f_min=20, atq_f_max=28, prot_f=58, atq_m_min=10, atq_m_max=10,
              prot_m=19, critico=4.3, mult=1.2, vel_atq=0.62, vel_inv=100),
}
# a tela soma o bonus do equipamento: "27 (+8)" = base 19 + 8. O pacote guarda a BASE.
BONUS = {
    100: dict(atq_f_min=3, atq_f_max=9,  prot_f=46),
    200: dict(atq_f_min=2, atq_f_max=6,  prot_f=51),
    300: dict(atq_f_min=4, atq_f_max=6,  prot_f=42),
    400: dict(atq_f_min=4, atq_f_max=9,  prot_f=35, atq_m_min=8, atq_m_max=20),
    500: dict(atq_f_min=8, atq_f_max=13, prot_f=37),
    600: dict(atq_f_min=4, atq_f_max=9,  prot_f=33, atq_m_min=8, atq_m_max=13),
    800: dict(atq_f_min=10, atq_f_max=18, prot_f=37),
}
DECIMAIS = {"critico", "mult", "vel_atq"}


def carregar():
    m = {}
    for cam in sorted(glob.glob(r"D:\RoshEmulator\captures\classes\charinfo_classe_*.bin")):
        c = int(re.search(r"_(\d+)\.bin", cam).group(1))
        with open(cam, "rb") as f:
            m[c] = f.read()
    return m


def main():
    moldes = carregar()
    classes = sorted(moldes)
    n = min(len(b) for b in moldes.values())
    campos = list(next(iter(FICHAS.values())))

    print(f"casando {len(campos)} campos da ficha contra {n} bytes, em {len(classes)} classes\n")
    achados = {}
    for campo in campos:
        for off in range(0, n - 4, 4):
            # A tela nao usa uma regra so: "Regeneracao de HP" trunca (13.75 -> 13) e
            # "Chance de Critico" arredonda (5.244 -> 5.2). Entao testamos as duas e exigimos
            # que a MESMA regra valha nas 7 classes. Fixar uma regra so descartava campos certos.
            for regra in ("trunca", "arredonda"):
                ok = True
                for c in classes:
                    alvo = FICHAS[c][campo] - BONUS.get(c, {}).get(campo, 0)
                    v = struct.unpack_from("<f", moldes[c], off)[0]
                    if v != v or abs(v) > 1e7:
                        ok = False; break
                    if campo in DECIMAIS:
                        if abs(round(v, 1) - round(alvo, 1)) > 1e-9:
                            ok = False; break
                    elif regra == "trunca":
                        if int(v) != int(alvo):
                            ok = False; break
                    else:
                        if round(v) != round(alvo):
                            ok = False; break
                if ok:
                    achados.setdefault(campo, []).append((off, regra))
                    break

    rotulos = {"saude":"Saude (HP max)","mana":"Mana (MP max)","reg_hp":"Regeneracao de HP",
               "reg_mp":"Regeneracao de MP","evasao":"Chance de evasao","bloqueio":"Chance de bloqueio",
               "atq_f_min":"ATQ Fisico min","atq_f_max":"ATQ Fisico max","prot_f":"Protecao fisica",
               "atq_m_min":"ATQ Magico min","atq_m_max":"ATQ Magico max","prot_m":"Protecao magica",
               "critico":"Chance de Critico","mult":"Multiplicador de critico",
               "vel_atq":"Velocidade de ataque","vel_inv":"Velocidade de invocacao"}
    for campo in campos:
        offs = achados.get(campo, [])
        rot = rotulos[campo]
        if offs:
            off0, regra = offs[0]
            extra = f"   (repete em {[o for o, _ in offs[1:]]})" if len(offs) > 1 else ""
            print(f"  [{off0:4d}] {rot:26s} ({regra}){extra}")
        else:
            print(f"  [ -- ] {rot:26s}   NAO ACHADO como float")


if __name__ == "__main__":
    main()
