# -*- coding: utf-8 -*-
r"""Poe a tabela item_atributo a bater, linha a linha, com o itemAttr.lod do client.

    python tools/sincronizar_itematributo.py             mostra
    python tools/sincronizar_itematributo.py --aplicar   grava no banco

PORQUE E PRECISO. O client desenha os atributos a partir do itemAttr.lod dele; o servidor
calcula o combate a partir de item_atributo (exportada depois para Data/Items/ItemOption.ini).
Se as duas divergirem, o jogador ve um numero e leva outro. Divergiam:

    espada 5001 +15   banco 7,00 / 23,00     client 9,12 / 27,48
    espada 5001 +18   banco 9,00 / 31,00     client 10,95 / 36,35
    botas 43201 +18   banco defesa 32        client defesa 22

O banco tinha os valores CRUS do Arena, postos pelo importar_refino_alto.py; o client tem os
nossos, com a curva do Arena aplicada a nossa base (ver mesclar_itemattr.py). Os do banco
estavam alem de diferentes, errados: o +15 da espada (7,00) era MENOR que o +13 (7,91) --
refinar piorava a arma.

O .lod passa a ser a fonte unica, e e ele que se copia para ca. Conferido antes de gravar:
nos refinos 0 a 13 as duas tabelas ja batiam a 100% em todas as colunas, o que valida o
mapeamento de offset para coluna aqui em baixo; so 14 a 18 e que divergiam.

Mapeamento dos 116 bytes, medido: item(4) refino(4) estrela(4) + 13 doubles de 8.
"""
import argparse
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, "server"))
sys.path.insert(0, os.path.join(RAIZ, "tools"))
import db                                                           # noqa: E402
import lod_crypto as L                                              # noqa: E402

LOD = r"C:\Games\Axion\ResUnpak\Interface\IE_Lod\itemAttr.lod_pt"
COLS = [("dano_min", 12), ("dano_max", 20), ("dano_mag_min", 28), ("dano_mag_max", 36),
        ("vel_ataque", 44), ("alcance", 52), ("defesa", 60), ("defesa_mag", 68),
        ("bonus8", 76), ("bonus9", 84), ("bonus10", 92), ("bonus11", 100), ("bonus12", 108)]


def ler_lod():
    d = L.decrypt_lod_stream(open(LOD, "rb").read(), pular_zero=False)
    _ver, tot = struct.unpack_from("<II", d, 0)
    pos, out = 8, {}
    for _ in range(tot):
        iid, ref, estrela = struct.unpack_from("<3I", d, pos)
        out[(iid, ref)] = (estrela,
                           [struct.unpack_from("<d", d, pos + o)[0] for _n, o in COLS])
        pos += 116
        for _s in range(2):
            (t,) = struct.unpack_from("<I", d, pos)
            pos += 4 + t
    return out


def main() -> int:
    ap = argparse.ArgumentParser(description="item_atributo <- itemAttr.lod do client.")
    ap.add_argument("--aplicar", action="store_true")
    args = ap.parse_args()

    lod = ler_lod()
    con = db._conectar()
    cur = con.cursor()
    cur.execute("SELECT item_id, refino, estrela, %s FROM item_atributo"
                % ",".join(c for c, _o in COLS))
    banco = {(r[0], r[1]): (r[2], [float(x or 0) for x in r[3:]]) for r in cur.fetchall()}
    print("lod: %d linhas | banco: %d linhas" % (len(lod), len(banco)))

    mudar, inserir = [], []
    for k, (estrela, vals) in lod.items():
        b = banco.get(k)
        if b is None:
            inserir.append((k, estrela, vals))
        elif b[0] != estrela or any(abs(a - c) > 0.005 for a, c in zip(vals, b[1])):
            mudar.append((k, estrela, vals))
    so_no_banco = len(set(banco) - set(lod))
    print("a corrigir: %d | a inserir: %d | so no banco (ficam): %d"
          % (len(mudar), len(inserir), so_no_banco))

    for k, _e, v in sorted(mudar)[:1]:
        print("   exemplo %s: banco %s -> lod %s"
              % (k, [round(x, 2) for x in banco[k][1][:4]], [round(x, 2) for x in v[:4]]))

    if not args.aplicar:
        print("\nnada gravado. corra com --aplicar")
        return 0

    sets = ", ".join("%s=?" % c for c, _o in COLS)
    cur.executemany("UPDATE item_atributo SET estrela=?, %s WHERE item_id=? AND refino=?" % sets,
                    [tuple([e] + v + [k[0], k[1]]) for k, e, v in mudar])
    if inserir:
        campos = ", ".join(c for c, _o in COLS)
        marcas = ", ".join("?" for _c in COLS)
        cur.executemany("INSERT INTO item_atributo (item_id, refino, estrela, %s) "
                        "VALUES (?, ?, ?, %s)" % (campos, marcas),
                        [tuple([k[0], k[1], e] + v) for k, e, v in inserir])
    con.commit()

    cur.execute("SELECT item_id, refino, estrela, %s FROM item_atributo"
                % ",".join(c for c, _o in COLS))
    depois = {(r[0], r[1]): (r[2], [float(x or 0) for x in r[3:]]) for r in cur.fetchall()}
    maus = sum(1 for k, (e, v) in lod.items()
               if k not in depois or depois[k][0] != e
               or any(abs(a - c) > 0.005 for a, c in zip(v, depois[k][1])))
    print("\ngravado. releitura: %d linhas divergentes do lod" % maus)
    return 0


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