# -*- coding: utf-8 -*-
"""Importa a tabela de encantamento (+N) do client para Data/Items/ItemEnchant.ini.

    python tools/importar_encantamento.py            mostra
    python tools/importar_encantamento.py --aplicar  grava

DE ONDE VEM

ItemEnchant.lod, decifrado com pular_zero=True (o modo importa: com False sai lixo).
Cabecalho de 8 bytes e depois registos de 32:

    [0:4]   nivel do encantamento, 1 a 13   (u32 LE)
    [4:8]   categoria do item               (u32 LE)
    [8:16]  chance de SUCESSO em percentagem (double LE)
    [16:24] chance de QUEBRAR em percentagem (double LE)
    [24:32] terceiro double, sempre zero -- nao sei o que e

DOIS BLOCOS. O que os separa e a CATEGORIA 25.

O ficheiro tem 65 registos, que se partem em 39 + 26:

    bloco 1   niveis 1-13 x categorias 1, 2 e 25
    bloco 2   niveis 1-13 x categorias 1 e 2        <- sem a 25

                      bloco 1        bloco 2
        +4            60 / 30        80 / 15
        +8            15 / 49        21 / 58,6
        +12          1,2 / 29       1,6 / 34
        +13          0,6 / 24      0,87 / 26

E o que sao as categorias, contadas no proprio catalogo de itens:

    categoria  1   1007 itens   Grande espada, espada pesada...      armas
    categoria  2   1588 itens   Escudo redondo, Escudo pareado...    defesa
    categoria 25     12 itens   Protecao de fletta, Asas do Caos...  PROTECOES

Ou seja, o bloco que inclui a categoria das protecoes e o bloco 1. O jogador confirmou que
existem itens que impedem a arma de partir quando o encantamento falha, e a categoria 25
chama-se literalmente "Protecao de fletta".

ISTO E A MELHOR EXPLICACAO QUE OS DADOS SUPORTAM, NAO UM FACTO MEDIDO. Contra ela pesa que
nenhum dos blocos da quebra ZERO, que e o que se esperaria de um encantamento protegido.

O QUE EU TINHA ESCRITO AQUI ANTES, e estava errado: que o bloco 2 seria o encantamento
abencoado, por existir um blessingEnchant.lod ao lado. Fui abri-lo e ele esta VAZIO --
contagem de registos zero, nas oito linguas do client. A tabela existe no formato e nao tem
dados nesta versao. Vale a pena guardar como se caiu nisso: eu tinha visto o nome do ficheiro
e presumido o conteudo, sem o abrir.

Dentro de cada bloco a categoria NAO muda nada -- as linhas de cada nivel trazem os mesmos
numeros. Guardo-as todas na mesma, porque descobrir que afinal mudam num patch futuro e mais
facil se o dado estiver la do que se eu o tiver deitado fora.
"""
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 data_files as D                                              # noqa: E402
import lod_crypto                                                   # noqa: E402

LOD = r"C:\Games\Karos\ResUnpak\Interface\IE_Lod\ItemEnchant.lod_pt"
XOR = 0

ORIGENS = {"karos": (LOD, 0),
           "arena": (r"C:\Games\Arena\ResUnpak\Interface\IE_Lod\itemEnchant.lod_en", 0x0D)}
"""De onde ler a tabela de encantamento.

O do KAROS vai ate +13; o do ARENA vai ate +18 -- 90 registos contra 65, cinco por nivel.
E esta tabela que traz a TAXA DE SUCESSO de cada nivel: sem as linhas de 14 a 18 o jogo nao
sabe refinar acima de 13, por mais que o item aceite o numero."""


def escolher_origem():
    global LOD, XOR
    nome = "karos"
    if "--origem" in sys.argv:
        i = sys.argv.index("--origem")
        if i + 1 < len(sys.argv):
            nome = sys.argv[i + 1]
    if nome not in ORIGENS:
        print("origem desconhecida: %s" % nome)
        raise SystemExit(2)
    LOD, XOR = ORIGENS[nome]
    print("origem: %s (xor 0x%02X)" % (nome, XOR))
TAM = 32
CAB = 8
CR = chr(13) + chr(10)


def ler():
    """[(bloco, nivel, categoria, sucesso, quebra, custo)], em ordem de ficheiro.

    O REGISTO TEM 32 BYTES e o layout e este -- conferido byte a byte no nivel 13, onde
    `33 33 33 33 33 33 e3 3f` em [12] da 0,6 exacto e `17 00 00 00` em [28] da 23:

        [ 0: 4] bloco      [ 4: 8] nivel     [ 8:12] categoria
        [12:20] sucesso    [20:28] quebra    [28:32] custo em protectores

    A VERSAO ANTERIOR LIA ERRADO: pegava `nivel, categoria` no offset 0 (onde esta o BLOCO)
    e inferia o bloco pela repeticao dos niveis. Dava certo por acidente enquanto os blocos
    vinham em ordem; e lia tres doubles a partir do 8, que davam zero nos tres, porque os
    doubles NAO estao alinhados a 8. O custo em protectores estava nos ultimos quatro bytes
    e nunca tinha sido lido.
    """
    d = lod_crypto.decrypt_lod_stream(open(LOD, "rb").read(), xor=XOR)
    quantos = struct.unpack_from("<I", d, 0)[0]
    saida = []
    for k in range(quantos):
        off = 4 + k * 32
        if off + 32 > len(d):
            break
        bloco, nivel, categoria = struct.unpack_from("<3I", d, off)
        if nivel > 100 or categoria > 100000:
            break                      # lixo depois do fim util
        saida.append((bloco, nivel, categoria,
                      struct.unpack_from("<d", d, off + 12)[0],
                      struct.unpack_from("<d", d, off + 20)[0],
                      struct.unpack_from("<I", d, off + 28)[0]))
    return saida

def main() -> int:
    escolher_origem()
    aplicar = "--aplicar" in sys.argv
    linhas = ler()
    print("registos lidos: %d" % len(linhas))
    print()
    blocos = sorted({l[0] for l in linhas})
    print("%-7s %-7s %-9s %-9s %s" % ("nivel", "bloco", "sucesso", "quebra", "categorias"))
    for nivel in range(1, 14):
        for b in blocos:
            iguais = [l for l in linhas if l[0] == b and l[1] == nivel]
            if not iguais:
                continue
            cats = sorted({l[2] for l in iguais})
            taxas = {(l[3], l[4]) for l in iguais}
            marca = "" if len(taxas) == 1 else "  <- categorias DIFEREM"
            print("%-7d %-7d %-9.2f %-9.2f %s%s"
                  % (nivel, b, iguais[0][3], iguais[0][4], cats, marca))
    if not aplicar:
        print()
        print("nada gravado. rode com --aplicar")
        return 0

    D.carregar()
    destino = os.path.join(RAIZ, "server", "Data", "Items", "ItemEnchant.ini")
    with open(destino, "w", encoding="utf-8", newline=CR) as fp:
        w = fp.write
        w("; ItemEnchant -- chance de sucesso e de quebra ao encantar (+N)." + CR)
        w("; colunas: bloco, nivel, categoria, sucesso, quebra, custo" + CR)
        w(";" + CR)
        w("; CUSTO e quantos PROTECTORES a tentativa consome. Cresce com o nivel e a arma" + CR)
        w("; custa mais do que a defesa: +4 pede 1, +13 pede 23/21, +18 pede 45/38. Do +1 ao" + CR)
        w("; +3 e zero -- nesses niveis nao ha risco, entao nao ha o que proteger." + CR)
        w(";" + CR)
        w("; O jogador: *com protector nunca quebra*. Entao a coluna `quebra` e a taxa de" + CR)
        w("; quando NAO se usa, e o custo e o preco de anular esse risco -- um interruptor," + CR)
        w("; nao uma taxa alternativa. Nenhum dos dois blocos da quebra zero." + CR)
        w(";" + CR)
        w("; Do ItemEnchant.lod do client. Percentagens, ja em unidades de 0 a 100." + CR)
        w(";" + CR)
        w("; SAO DOIS BLOCOS com taxas diferentes para o mesmo nivel. O que os separa e a" + CR)
        w("; CATEGORIA 25:" + CR)
        w(";" + CR)
        w(";     bloco 1   categorias 1, 2 e 25      bloco 2   categorias 1 e 2" + CR)
        w(";" + CR)
        w(";     categoria  1   1007 itens   armas" + CR)
        w(";     categoria  2   1588 itens   defesa" + CR)
        w(";     categoria 25     12 itens   Protecao de fletta, Asas do Caos -- PROTECOES" + CR)
        w(";" + CR)
        w("; O bloco 1 e o que inclui a categoria das protecoes. Existem itens que impedem a" + CR)
        w("; arma de partir quando o encantamento falha, e a categoria 25 chama-se mesmo" + CR)
        w("; 'Protecao de fletta'." + CR)
        w(";" + CR)
        w("; E A MELHOR EXPLICACAO QUE OS DADOS SUPORTAM, NAO UM FACTO MEDIDO. Contra ela pesa" + CR)
        w("; que nenhum dos blocos da quebra ZERO, que e o que se esperaria de um encantamento" + CR)
        w("; protegido." + CR)
        w(";" + CR)
        w("; Aqui esteve escrito que o bloco 2 seria encantamento abencoado, por existir um" + CR)
        w("; blessingEnchant.lod ao lado. Errado: esse ficheiro esta VAZIO -- zero registos, nas" + CR)
        w("; oito linguas. Eu tinha visto o nome e presumido o conteudo sem o abrir." + CR)
        w(";" + CR)
        w("; A categoria nao muda as taxas dentro do mesmo bloco. Fica guardada na mesma:" + CR)
        w("; se um patch futuro as separar, e mais facil dar por isso com o dado presente." + CR)
        w(";" + CR)
        w("; Reparar no +8: sobe para 15 por cento quando o +7 tinha 9, e a quebra desce de" + CR)
        w("; 61 para 49. Nao e erro de leitura, esta assim no ficheiro." + CR)
        w(CR)
        for i, (bloco, nivel, categoria, sucesso, quebra, custo) in enumerate(linhas):
            w("[%d]" % i + CR)
            w("bloco = %d" % bloco + CR)
            w("nivel = %d" % nivel + CR)
            w("categoria = %d" % categoria + CR)
            w("sucesso = %g" % sucesso + CR)
            w("quebra = %g" % quebra + CR)
            w("custo = %d" % custo + CR)
            w(CR)
    print()
    print("gravado: %s (%s bytes)" % (destino, "{:,}".format(os.path.getsize(destino))))
    return 0


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