# -*- coding: utf-8 -*-
"""Deduz o que cada NPC faz e grava em Data/NpcPapel.ini.

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

No Karos, NPC e monstro dividem a mesma tabela e nada no catalogo diz "este vende, aquele
da missao". O papel sai de cruzar quatro fontes, e elas NAO tem a mesma confianca:

    missao   CERTO.    Vem do proprio Quest.ini, coluna doador_npc -- e o mesmo campo que o
                       servidor usa pra validar o 09 00. Nao ha o que interpretar.
    loja     PROVAVEL. O npcShop.lod cita o id do NPC, mas os registros tem tamanho variavel
                       (tem texto dentro), entao a busca aceita offset desalinhado e pode
                       casar por coincidencia. Um id de 4 bytes aparece a toa em 39 KB.
    criacao  PROVAVEL. Mesma ressalva, no combinationNPC.lod.
    dialogo  PROVAVEL. Mesma ressalva, no npcDialogue.lod.

A coluna `confianca` no arquivo diz qual e qual. Antes de ligar loja ou criacao no servidor,
vale conferir no jogo: abrir o NPC e ver se a janela aparece. O de missao pode ir direto.
"""
import os
import struct
import sys

sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "server"))
import lod_crypto                                                    # noqa: E402
import data_files as D                                               # noqa: E402

BASE = r"C:\Games\Karos\ResUnpak\Interface\IE_Lod"
NL = chr(10)
# O caminho vem do data_files, nao escrito aqui: os arquivos foram pra subpastas por
# sistema em 25/08, e caminho fixo passaria a gravar um segundo arquivo na raiz da
# Data -- calado, enquanto o servidor seguia lendo o certo.
DESTINO = D.caminho("NpcPapel", "Npcs")

FONTES = [("npcShop", "loja"), ("combinationNPC", "criacao"), ("npcDialogue", "dialogo")]


def ids_citados(arquivo, universo):
    d = lod_crypto.decrypt_lod_stream(
        open(os.path.join(BASE, arquivo + ".lod_pt"), "rb").read())
    achados = set()
    for i in range(0, len(d) - 4):
        v = struct.unpack_from("<I", d, i)[0]
        if v in universo:
            achados.add(v)
    return achados


def main() -> int:
    aplicar = "--aplicar" in sys.argv
    D.carregar()
    npcs = D.indice("Npc", "npc_id")
    universo = set(npcs)

    # missao: certo, vem do Quest.ini
    da_missao, alvo_de_missao = {}, set()
    for q in D.tabela("Quest"):
        d = q.get("doador_npc")
        if d in universo:
            da_missao.setdefault(d, []).append(q["quest_id"])
        if q.get("tipo") in (8, 9) and q.get("alvo") in universo:
            alvo_de_missao.add(q["alvo"])

    outros = {}
    for arquivo, rotulo in FONTES:
        for nid in ids_citados(arquivo, universo):
            outros.setdefault(nid, []).append(rotulo)

    # so os que estao no mundo
    no_mundo = {sp["npc_id"] for sp in D.tabela("NpcSpawn") if sp.get("tipo") == "npc"}

    linhas = []
    for nid in sorted(universo):
        papeis = []
        if nid in da_missao or nid in alvo_de_missao:
            papeis.append("missao")
        papeis += outros.get(nid, [])
        if not papeis:
            continue
        confianca = "certo" if papeis == ["missao"] else (
            "misto" if "missao" in papeis else "provavel")
        linhas.append({
            "npc_id": nid,
            "nome": (npcs[nid].get("nome") or "").replace(NL, " "),
            "papeis": ",".join(papeis),
            "confianca": confianca,
            "quantas_missoes": len(da_missao.get(nid, [])) or None,
            "no_mundo": 1 if nid in no_mundo else 0,
        })

    print("NPCs com algum papel: %d de %d no catalogo" % (len(linhas), len(universo)))
    print("   dao missao      : %d  (certo, do Quest.ini)" % len(da_missao))
    for arquivo, rotulo in FONTES:
        print("   %-15s : %d  (provavel, busca no %s.lod)"
              % (rotulo, sum(1 for l in linhas if rotulo in l["papeis"]), arquivo))
    print("   estao no mundo  : %d" % sum(1 for l in linhas if l["no_mundo"]))
    print()
    print("os que estao no mundo:")
    print("%-10s %-26s %-28s %s" % ("npc_id", "nome", "papeis", "confianca"))
    print("-" * 78)
    for l in linhas:
        if l["no_mundo"]:
            print("%-10d %-26s %-28s %s"
                  % (l["npc_id"], l["nome"][:25], l["papeis"], l["confianca"]))

    if not aplicar:
        print()
        print("nada gravado. rode com --aplicar")
        return 0

    cols = ("npc_id", "nome", "papeis", "confianca", "quantas_missoes", "no_mundo")
    with open(DESTINO, "w", encoding="utf-8", newline=NL) as fp:
        fp.write("; NpcPapel -- o que cada NPC faz: missao, loja, criacao, dialogo." + NL)
        fp.write("; confianca=certo veio do Quest.ini e pode ser usado direto." + NL)
        fp.write("; confianca=provavel/misto veio de busca em .lod de registro variavel --" + NL)
        fp.write("; confira no jogo antes de ligar loja ou criacao." + NL)
        fp.write("; colunas: " + ", ".join(cols) + NL + NL)
        for l in linhas:
            fp.write("[%d]" % l["npc_id"] + NL)
            for c in cols[1:]:
                if l[c] not in (None, ""):
                    fp.write("%s = %s" % (c, l[c]) + NL)
            fp.write(NL)
    print()
    print("gravado: %s (%s bytes, %d npcs)"
          % (os.path.basename(DESTINO), "{:,}".format(os.path.getsize(DESTINO)), len(linhas)))
    return 0


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