# -*- coding: utf-8 -*-
r"""Gera Data/Items/ItemEnchant.ini a partir do ItemEnchant.lod do client.

    python tools/sincronizar_itemenchant.py             mostra o que mudaria
    python tools/sincronizar_itemenchant.py --aplicar   grava o .ini

PORQUE. O .ini em uso tinha sido importado do ARENA (importar_encantamento.py --origem
arena), nao do nosso client, e as duas tabelas discordavam em 17 linhas:

    +1 a +3, cat 25   client 100% de sucesso e 0 de quebra;  arena 95/5, 90/5, 85/10
    +4 a +13, cat 25  o CUSTO em protectores e 1..21 no client e 0 no arena
    +8, cat 1 e 2     sucesso 15% e 21% no client;  6% e 8,5% no arena

Mesmo erro que o item_atributo tinha: o servidor a calcular por uma tabela e o client a
mostrar outra. O .lod passa a ser a fonte unica -- e ja traz o que queremos, porque a
mesclagem la poe os 65 registos nossos (niveis 1-13) mais os 25 do Arena (14-18, que so
existem la).

Registo de 32 bytes, conferido byte a byte contra o ficheiro do Arena:
    bloco(u32) nivel(u32) categoria(u32) sucesso(double) quebra(double) custo(u32)

O CAMPO DO CUSTO quase custou caro: em 01/09 tomei-o por um id de efeito visual e cheguei a
escrever-lhe 5, 11, 17, 20 e 22 para "testar brilhos" -- estava a reescrever quantos
protectores cada tentativa consome. NAO HA NENHUM ID DE EFEITO NESTE FICHEIRO.

FORMATO DE SAIDA: seccoes "[n]" com "chave = valor", terminadas em \n. E o que os outros
Data/Items usam (ver ItemOption.ini). Escrito em TAB, o data_files le zero linhas.
"""
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, "tools"))
sys.path.insert(0, os.path.join(RAIZ, "server"))
import lod_crypto as L                                              # noqa: E402
import data_files                                                   # noqa: E402

LOD = r"C:\Games\Axion\ResUnpak\Interface\IE_Lod\ItemEnchant.lod_pt"
SAIDA = os.path.join(RAIZ, "server", "Data", "Items", "ItemEnchant.ini")
FIM = chr(10)
TAM = 32
CABECALHO = [
    "; ItemEnchant -- chance de sucesso e de quebra ao encantar (+N).",
    "; colunas: bloco, nivel, categoria, sucesso, quebra, custo",
    ";",
    "; CUSTO e quantos PROTECTORES a tentativa consome. Cresce com o nivel e a arma",
    "; custa mais do que a defesa: +4 pede 1, +13 pede 23/21, +18 pede 45/38. Do +1 ao",
    "; +3 e zero -- nesses niveis nao ha risco, entao nao ha o que proteger.",
    ";",
    "; O jogador: *com protector nunca quebra*. Entao a coluna `quebra` e a taxa de",
    "; quando NAO se usa, e o custo e o preco de anular esse risco -- um interruptor,",
    "; nao uma taxa alternativa. Nenhum dos dois blocos da quebra zero.",
    ";",
    "; GERADO por tools/sincronizar_itemenchant.py a partir do ItemEnchant.lod do client.",
    "; Nao editar a mao: o client mostra o que esta no .lod e as duas tabelas tem de bater.",
    ";",
]


def ler_lod():
    d = L.decrypt_lod_stream(open(LOD, "rb").read())
    n = struct.unpack_from("<I", d, 0)[0]
    out = []
    for i in range(n):
        o = 4 + i * TAM
        bl, nv, cat = struct.unpack_from("<3I", d, o)
        suc, qbr = struct.unpack_from("<2d", d, o + 12)
        custo = struct.unpack_from("<I", d, o + 28)[0]
        out.append((bl, nv, cat, suc, qbr, custo))
    return sorted(out, key=lambda r: (r[0], r[1], r[2]))


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

    regs = ler_lod()
    antigo = {(int(r["bloco"]), int(r["nivel"]), int(r["categoria"])):
              (float(r["sucesso"]), float(r["quebra"]), int(r["custo"] or 0))
              for r in data_files.tabela("ItemEnchant")}
    print("lod: %d registos | .ini actual: %d linhas" % (len(regs), len(antigo)))

    muda = [((bl, nv, cat), antigo[(bl, nv, cat)], (suc, qbr, custo))
            for bl, nv, cat, suc, qbr, custo in regs
            if (bl, nv, cat) in antigo
            and (abs(antigo[(bl, nv, cat)][0] - suc) > 0.005
                 or abs(antigo[(bl, nv, cat)][1] - qbr) > 0.005
                 or antigo[(bl, nv, cat)][2] != custo)]
    falta = [(bl, nv, cat) for bl, nv, cat, _s, _q, _c in regs if (bl, nv, cat) not in antigo]
    print("a corrigir: %d | em falta no .ini: %d" % (len(muda), len(falta)))
    for k, a, b in muda[:20]:
        print("   %-12s ini=(%g, %g, %d)  ->  lod=(%g, %g, %d)"
              % (str(k), a[0], a[1], a[2], b[0], b[1], b[2]))

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

    linhas = list(CABECALHO)
    for i, (bl, nv, cat, suc, qbr, custo) in enumerate(regs):
        linhas += ["[%d]" % i, "bloco = %d" % bl, "nivel = %d" % nv,
                   "categoria = %d" % cat, "sucesso = %g" % suc,
                   "quebra = %g" % qbr, "custo = %d" % custo]
    open(SAIDA, "wb").write((FIM.join(linhas) + FIM).encode("latin-1"))
    print("\ngravado: %s (%d registos)" % (SAIDA, len(regs)))
    return 0


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