# -*- coding: utf-8 -*-
r"""Resolve os efeitos de FANTASIA em (atributo, valor plano, valor percentual).

    python tools/extrair_efeitos_fantasia.py  ->  server/Data/Items/FantasiaEfeito.ini

NAO HA MECANISMO PROPRIO DE FANTASIA. E a MESMA cadeia das skills, do principio ao fim:

    item      ItemPackage            -> as DUAS opcoes do grau da peca
    instancia bruto[76:80]           -> qual das duas ela sorteou
    efeito    skillLevel @264        -> ate 5 codigos, um por atributo
    codigo    skillData              -> (tipo, plano @24 double, percentual @32 double)
    conjunto  FantasiaConjunto/Set   -> patamares de 3 e 4 pecas, pela mesma via

O `tipo` e o switch FUN_006076cb, o mesmo dos bonus de item e dos buffs -- o TIPO_DE_BONUS
do db serve tal e qual, sem tabela de traducao pelo meio.

O ERRO QUE ISTO CORRIGE (30/08). A primeira versao lia o valor dos DOIS ULTIMOS DIGITOS do
codigo -- `codigo = grupo * 100 + valor` -- e dava certo nas quatro medicoes que existiam
na altura, porque nesses casos os digitos calhavam bater. Nao batem em geral:

    codigo 41727  digitos dizem 27   skillData diz 33   client diz "Proteccao fisica+33,00"
    codigo 41732  digitos dizem 32   skillData diz 82   ficha do jogador mede +82
    codigo 70006  digitos dizem  6   skillData diz 70   client diz "Saude +70,00"

A terceira linha e a que fecha a familia 7xx, que ficou por explicar um dia inteiro: nunca
foi um "patamar sem escala", e uma chave de skillData como todas as outras. Procurava-se
uma tabela de conversao que nao existe, com o valor a espera na tabela do lado.

PLANO OU PERCENTAGEM: sao dois campos separados no mesmo registo, e o que estiver a zero
nao conta. O 42710 traz plano 0 e percentual 10, e o client escreve "Velocidade de
movimento+10.00%"; o 43906 traz percentual 6 e o client escreve "Forca de ataque+6.00%".

CONFERIDO CONTRA O OFICIAL, conjunto 33 ("Terno para banquete"), quatro pecas mais a
Phoenix, com as fichas antes e depois:

    torso  40615  atq_fisico        15      ficha +15, e o ATQ passa de 15/17 a 32/34
    pes    41732  protecao_fisica   82      (+82)
    mao    41727  protecao_fisica   33      (+33)  os tres somam 148, e a Phoenix da 70:
    pe     41727  protecao_fisica   33      (+33)  218, que e o que a ficha mede
    3p     41103  chance_critico     3      ficha: critico 8,1 -> 14,1 (3 da mao + 3 daqui)
    3p     70006  saude             70      ficha: 223 -> 293
    4p     43906  poder_de_ataque    6%     (15+15)*1,06=31,8 -> 32 e (17+15)*1,06=33,9 -> 34

Sete de sete. A percentagem entra DEPOIS do plano, e o que as duas ultimas linhas mostram.
"""
import io
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 extrair_buffs                                                # noqa: E402
import db                                                           # noqa: E402

PASTA = r"C:\Games\Axion\ResUnpak\Interface\IE_Lod"
BIN, N_STR, OFF_RANHURAS, N_RANHURAS = 284, 18, 264, 5


def ranhuras_por_efeito():
    d = L.decrypt_lod_stream(open(os.path.join(PASTA, "skillLevel.lod_pt"), "rb").read(),
                             pular_zero=False)
    n = struct.unpack_from("<I", d, 4)[0]
    saida, q = {}, 8
    for _ in range(n):
        if q + BIN > len(d):
            break
        b = d[q:q + BIN]
        q += BIN
        for _k in range(N_STR + 1):
            ln = struct.unpack_from("<I", d, q)[0]
            q += 4 + ln
        cod = [struct.unpack_from("<I", b, OFF_RANHURAS + 4 * k)[0] for k in range(N_RANHURAS)]
        saida.setdefault(struct.unpack_from("<I", b, 0)[0], [c for c in cod if c])
    return saida


def efeitos_usados():
    """{efeito: Counter} com o nome da peca -- so os que o ItemPackage liga a fantasias."""
    import collections
    import re
    p = os.path.join(RAIZ, "server", "Data", "Items", "ItemPackage.ini")
    saida = collections.defaultdict(collections.Counter)
    for ln in io.open(p, encoding="utf-8", errors="replace"):
        if ln.startswith((";", "//")) or '"' not in ln:
            continue
        c = ln.split()
        if len(c) < 8 or not c[0].isdigit():
            continue
        nome = ln[ln.index('"') + 1:ln.rindex('"')]
        m = re.search(r"\(([^)]*)\)\s*$", nome)
        saida[int(c[4])][(m.group(1) if m else nome).lower()] += 1
    return saida


def conjuntos():
    d = L.decrypt_lod_stream(open(os.path.join(PASTA, "itemMixedSetEffect.lod_pt"), "rb").read())
    n = struct.unpack_from("<I", d, 0)[0]
    return [struct.unpack_from("<III", d, 4 + i * 12) for i in range(n)]


def item_para_conjunto():
    """{item_id: conjunto}, do itemMixedType.

    A tabela nao diz o que a segunda coluna e -- chamava-se "tipo" e estava por abrir. Sao
    182 valores distintos e o itemMixedSetEffect tem exactamente 182 conjuntos, e os dois
    conjuntos de ids sao IGUAIS, sem sobra de nenhum lado. E o mapa peca -> conjunto.
    """
    d = L.decrypt_lod_stream(open(os.path.join(PASTA, "itemMixedType.lod_pt"), "rb").read())
    n = struct.unpack_from("<I", d, 0)[0]
    return dict(struct.unpack_from("<II", d, 4 + i * 8) for i in range(n))


def main() -> int:
    ranhuras = ranhuras_por_efeito()
    codigos = extrair_buffs.efeitos()
    usados, conj = efeitos_usados(), conjuntos()
    do_conjunto = {c for _a, _b, c in conj}
    nl, tb = chr(10), chr(9)
    destino = os.path.join(RAIZ, "server", "Data", "Items", "FantasiaEfeito.ini")
    linhas = por_resolver = 0
    with io.open(destino, "w", encoding="utf-8", newline="") as f:
        f.write("; FantasiaEfeito -- efeito de fantasia -> (atributo, plano, percentual)." + nl)
        f.write("; Gerado por tools/extrair_efeitos_fantasia.py. Ver o cabecalho: o valor" + nl)
        f.write("; sai do skillData, a mesma tabela das skills -- nao dos digitos do codigo." + nl)
        f.write("; origem: peca = veio do ItemPackage; conjunto = do itemMixedSetEffect." + nl)
        f.write(tb.join(["//efeito", "origem", "codigo", "tipo", "atributo",
                         "plano", "pct"]) + nl)
        for ef in sorted(set(usados) | do_conjunto):
            if ef not in ranhuras:
                continue
            pecas = "/".join(sorted(usados[ef])) if ef in usados else ""
            origem = pecas or ("conjunto" if ef in do_conjunto else "?")
            for c in ranhuras[ef]:
                if c not in codigos:
                    por_resolver += 1
                    continue
                tipo, v1, v2 = codigos[c]
                f.write(tb.join([str(ef), origem, str(c), str(tipo),
                                 db.TIPO_DE_BONUS.get(tipo, "?"),
                                 "%g" % v1, "%g" % v2]) + nl)
                linhas += 1
    print("efeitos de peca: %d, de conjunto: %d" % (len(usados), len(do_conjunto)))
    print("linhas escritas: %d em %s" % (linhas, os.path.normpath(destino)))
    if por_resolver:
        print("codigos sem linha no skillData: %d" % por_resolver)

    # peca -> conjunto
    pertence = item_para_conjunto()
    d2 = os.path.join(RAIZ, "server", "Data", "Items", "FantasiaConjunto.ini")
    with io.open(d2, "w", encoding="utf-8", newline="") as f:
        f.write("; FantasiaConjunto -- que conjunto cada peca de fantasia pertence." + nl)
        f.write("; Do itemMixedType. Ver o cabecalho de tools/extrair_efeitos_fantasia.py." + nl)
        f.write(tb.join(["//item_id", "conjunto"]) + nl)
        for it in sorted(pertence):
            f.write(tb.join([str(it), str(pertence[it])]) + nl)
    print("pecas com conjunto: %d em %s" % (len(pertence), os.path.normpath(d2)))

    # conjunto + numero de pecas -> efeito
    d3 = os.path.join(RAIZ, "server", "Data", "Items", "FantasiaSet.ini")
    with io.open(d3, "w", encoding="utf-8", newline="") as f:
        f.write("; FantasiaSet -- o bonus que entra ao vestir N pecas do mesmo conjunto." + nl)
        f.write("; Do itemMixedSetEffect. So existem dois patamares: 3 pecas e 4 pecas." + nl)
        f.write(tb.join(["//conjunto", "pecas", "efeito"]) + nl)
        for a, b, c in sorted(conj):
            f.write(tb.join([str(a), str(b), str(c)]) + nl)
    print("patamares de conjunto: %d em %s" % (len(conj), os.path.normpath(d3)))
    return 0


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