# -*- coding: utf-8 -*-
"""Acrescenta ao Quest.ini o texto das missoes, que estava no quest.lod e nunca foi lido.

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

O registro do quest.lod tem 6336 bytes e o importador original mapeava so uns 96 -- ids,
niveis, doador, tipo, alvo e quantidade. Todo o resto e TEXTO, em quatro campos de offset
fixo, preenchidos em 730 dos 730 registros:

    1624  fala_oferta    o que o NPC diz ao oferecer
    3160  objetivo       "Falar com o Domingo", "Matar 15 caes pequenos / 20 caes cinzentos"
    4696  fala_entrega   o que ele diz ao receber de volta
    6232  nome           "Um grito de Socorro"

Isso importa por dois motivos praticos, alem de dar texto pro jogo mostrar:

1. Ate agora o Quest.ini so tinha numero. Ninguem consegue olhar 730 linhas de id e dizer
   se estao certas. Com nome da pra conferir de relance.
2. A coluna `categoria` (1..9) continua sem significado conhecido. Com os nomes na mao da
   pra procurar as diarias pelo texto e ver em que categoria elas caem -- que e medir, em
   vez de deduzir da forma dos dados.
"""
import os
import re
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\quest.lod_pt"
INI, TAM = 4, 6336
OFFS = (("fala_oferta", 1624), ("objetivo", 3160), ("fala_entrega", 4696), ("nome", 6232))
CR = chr(13) + chr(10)
NL = chr(10)
BARRA = chr(92)
# palavras que denunciam repeticao, pra achar as diarias sem chutar pela forma dos dados
PISTAS = ("diari", "diaria", "todo dia", "por dia", "repet", "semanal")


def escapar(v):
    if v is None:
        return None
    s = str(v).replace(BARRA, BARRA + BARRA)
    return s.replace(chr(9), BARRA + "t").replace(chr(13), "").replace(NL, BARRA + "n")


def texto(r, off, limite=1500):
    fim = r.find(b"\x00", off)
    if fim < 0 or fim - off > limite:
        fim = off + limite
    return r[off:fim].decode("latin-1", "replace").strip()


def do_client():
    d = lod_crypto.decrypt_lod_stream(open(LOD, "rb").read())
    n = struct.unpack_from("<I", d, 0)[0]
    for k in range(n):
        o = INI + k * TAM
        r = d[o:o + TAM]
        if len(r) < TAM:
            break
        linha = {"quest_id": struct.unpack_from("<I", r, 0)[0]}
        for nome, off in OFFS:
            linha[nome] = texto(r, off)
        yield linha


def main() -> int:
    aplicar = "--aplicar" in sys.argv
    D.carregar()
    atuais = {int(r["quest_id"]): dict(r) for r in D.tabela("Quest")}
    novos = {c["quest_id"]: c for c in do_client()}

    print("quest.lod : %d missoes" % len(novos))
    print("Quest.ini : %d missoes" % len(atuais))
    faltam = sorted(set(novos) - set(atuais))
    sobram = sorted(set(atuais) - set(novos))
    print("so no lod : %d  %s" % (len(faltam), faltam[:6]))
    print("so no ini : %d  %s" % (len(sobram), sobram[:6]))
    print()

    # o que este import existe pra responder: onde estao as diarias
    achadas = []
    for qid, c in novos.items():
        alvo = (c["nome"] + " " + c["objetivo"] + " " + c["fala_oferta"]).lower()
        if any(p in alvo for p in PISTAS):
            achadas.append((qid, (atuais.get(qid) or {}).get("categoria"), c["nome"]))
    print("missoes cujo texto menciona repeticao: %d" % len(achadas))
    for qid, cat, nome in achadas[:25]:
        print("   %-10s categoria=%-4s %s" % (qid, cat, nome[:52]))
    if achadas:
        cats = sorted(set(c for _, c, _ in achadas if c is not None))
        print("   -> categorias envolvidas: %s" % cats)
    else:
        print("   nenhuma. O texto nao denuncia a repeticao, entao categoria continua aberta.")
    print()
    print("exemplos do que sera gravado:")
    for qid in sorted(novos)[:4]:
        c = novos[qid]
        print("   %-10s %-36s | %s" % (qid, c["nome"][:36], c["objetivo"][:44]))

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

    # junta: mantem tudo que ja existe e acrescenta as quatro colunas de texto
    cols = []
    for r in atuais.values():
        for k in r:
            if k not in cols and not k.startswith("__"):
                cols.append(k)
    for nome, _ in OFFS:
        if nome not in cols:
            cols.append(nome)

    destino = D.caminho("Quest", "Quests")
    with open(destino, "w", encoding="utf-8", newline=CR) as fp:
        w = fp.write
        w("; Quest -- catalogo de missoes, extraido do quest.lod do client." + CR)
        w("; colunas: " + ", ".join(cols) + CR)
        w(";" + CR)
        w("; tipo: 1=matar  3=coletar  8=falar  9=desconhecido (ver tipo_nome)" + CR)
        w("; alvo: mob, item ou npc, conforme o tipo." + CR)
        w(";" + CR)
        w("; repeticao: unica | diaria | repetivel. COLUNA OPCIONAL, e enquanto ela nao" + CR)
        w("; estiver preenchida o servidor trata a missao como UNICA. O padrao e escolhido," + CR)
        w("; nao neutro: marcar de menos so impede refazer uma diaria, marcar de mais deixa" + CR)
        w("; reentregar missao unica e farmar a recompensa." + CR)
        w(";" + CR)
        w("; categoria: 1 a 9, SIGNIFICADO DESCONHECIDO. Vem do offset 8 do registro. A" + CR)
        w("; categoria 2 e quase toda encadeada (284 de 292 com pre_requisito) e a 3 nao tem" + CR)
        w("; nenhuma -- sugestivo, mas nao e prova de que uma delas seja a diaria." + CR)
        w(CR)
        for qid in sorted(set(atuais) | set(novos)):
            r = dict(atuais.get(qid) or {"quest_id": qid})
            c = novos.get(qid)
            if c:
                for nome, _ in OFFS:
                    r[nome] = c[nome]
            w("[%d]" % qid + CR)
            for col in cols:
                if col == "quest_id":
                    continue
                v = escapar(r.get(col))
                if v not in (None, ""):
                    w("%s = %s" % (col, v) + CR)
            w(CR)
    print()
    print("gravado: %s  (%s bytes)" % (destino, "{:,}".format(os.path.getsize(destino))))
    return 0


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