# -*- coding: utf-8 -*-
"""Tira das missoes de recolha a lista de que monstro larga que item, por regiao.

    python tools/importar_drop_missao.py            mostra
    python tools/importar_drop_missao.py --aplicar  grava Data/Drop/QuestDrop.ini

DE ONDE VEM, E POR QUE ISTO EXISTE

Nao ha tabela de drop de monstro no client -- procurada nos 234 bytes do npcInfo, nas 114
tabelas .lod e nos nomes de classe do AMo.exe (ver Data/Drop/RvrDrop.ini). Mas ha UM sitio
onde o client liga um monstro a um item: as missoes de recolha.

    Quest.ini, tipo 3 ("coletar"):   alvo = o ITEM     alvo2 = o MONSTRO

Sao 315 missoes, 267 com um monstro reconhecido, que dao 257 pares distintos cobrindo 143
monstros e 20 regioes. Contra os 18 monstros que a captura mediu, e oito vezes mais alcance,
e de graca -- ja estava no client desde sempre.

ISTO NAO E A MESMA COISA QUE O MobDrop.ini, E A DIFERENCA IMPORTA

    MobDrop.ini    o que o monstro larga SEMPRE. Medido no fio, com contagem de abates.
                   18 monstros, 110 itens, com probabilidade.

    QuestDrop.ini  o que o monstro larga PARA QUEM TEM A MISSAO. Do client, sem
                   probabilidade nenhuma -- diz que acontece, nao diz quantas vezes.
                   143 monstros, 257 itens.

A sobreposicao entre as duas e ZERO pares. Nao e coincidencia: sao familias de id diferentes
(70xxx nas missoes, 80xxx e 90xxx nos materiais soltos), e portanto duas coisas distintas.

O QUE FALTA, E NAO SE RESOLVE AQUI

Nenhuma das duas cobre o drop de EQUIPAMENTO -- as espadas e armaduras que cairam na captura
(32402, 36002, 42002...). Esse so sai por medicao, e por muita: sao itens raros.

E nao ha PROBABILIDADE nenhuma nesta tabela. A coluna peso fica vazia de proposito: por um
lado seria inventada, por outro o servidor tem de tratar estes itens de outra maneira, que e
so os largar a quem tem a missao aceite. Enquanto isso nao existir, esta tabela e um mapa do
que ha, e nao uma tabela que o sorteio le.
"""
import collections
import os
import sys

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

DESTINO = os.path.join(RAIZ, "server", "Data", "Drop", "QuestDrop.ini")
TIPO_COLETAR = 3
# Percentagem por abate. NAO MEDIDA -- ver a nota no cabecalho do ficheiro gerado.
CHANCE_PADRAO = 50
TAB = chr(9)
ASPA = chr(34)
CR = chr(13) + chr(10)


def celula(v):
    s = "" if v is None else str(v)
    return (ASPA + s + ASPA) if (" " in s or TAB in s) else s


def main() -> int:
    aplicar = "--aplicar" in sys.argv
    D.carregar()
    itens = dict((r["item_id"], str(r.get("nome") or "")) for r in D.tabela("Item"))
    mobs = dict((r["mob_id"], str(r.get("nome") or "")) for r in D.tabela("Mob"))
    mapas = dict((r["mapa_id"], str(r.get("nome") or "")) for r in D.tabela("Map"))

    pares = {}
    for r in D.tabela("Quest"):
        if r.get("tipo") != TIPO_COLETAR:
            continue
        mob, item = r.get("alvo2"), r.get("alvo")
        if mob not in mobs or item not in itens:
            continue
        # a mesma dupla pode aparecer em varias missoes; fica a que pede mais unidades,
        # que e a que diz melhor que o item cai varias vezes
        chave = (mob, item)
        anterior = pares.get(chave)
        qtd = int(r.get("quantidade") or 1)
        if anterior is None or qtd > anterior[0]:
            pares[chave] = (qtd, r.get("regiao"), r.get("quest_id"), r.get("nivel"))

    medidos = set((r["mob_id"], r["item_id"]) for r in D.tabela("MobDrop"))
    print("pares monstro->item das missoes de recolha: %d" % len(pares))
    print("  monstros distintos: %d de %d no catalogo" % (len(set(m for m, _i in pares)), len(mobs)))
    print("  itens distintos   : %d" % len(set(i for _m, i in pares)))
    print("  regioes cobertas  : %d" % len(set(v[1] for v in pares.values())))
    print("  ja medidos no MobDrop.ini: %d (a sobreposicao)" % len(set(pares) & medidos))
    print()
    # O NOME DA REGIAO nao existe. Os ids 19002..19014 so aparecem no Quest.regiao e no
    # npcShop.regiao -- nao estao no Map.ini (que so tem 19021, 19031, 19041 nessa gama) nem
    # no IE_Maplist, nem no mapWarp. Em vez de escrever "?" e ficar por ai, a regiao e
    # identificada pelo NIVEL das missoes que la acontecem, que e o que diz onde ela fica.
    por_regiao = collections.Counter(v[1] for v in pares.values())
    print("%-9s %-8s %-10s %-30s %s"
          % ("regiao", "pares", "monstros", "niveis das missoes", "nome (se conhecido)"))
    for reg, n in por_regiao.most_common():
        quantos = len(set(m for (m, _i), v in pares.items() if v[1] == reg))
        nivs = sorted(v[3] for v in pares.values() if v[1] == reg and v[3])
        faixa = ("%d a %d" % (nivs[0], nivs[-1])) if nivs else "-"
        print("%-9s %-8d %-10d %-30s %s"
              % (reg, n, quantos, faixa, str(mapas.get(reg, ""))[:30]))

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

    with open(DESTINO, "w", encoding="utf-8", newline="") as fp:
        w = fp.write
        for l in [
            "; QuestDrop -- que monstro larga que item de missao, e em que regiao.",
            "; Tirado das missoes de recolha do client (Quest.ini, tipo 3): alvo = item,",
            "; alvo2 = monstro. Gerado por tools/importar_drop_missao.py.",
            ";",
            "; NAO E A MESMA COISA QUE O MobDrop.ini:",
            ";",
            ";   MobDrop.ini    o que o monstro larga SEMPRE. Medido no fio do oficial, com",
            ";                  contagem de abates -- tem probabilidade. 18 monstros.",
            ";",
            ";   QuestDrop.ini  o que o monstro larga PARA QUEM TEM A MISSAO. Vem do client,",
            ";                  sem probabilidade nenhuma: diz que acontece, nao diz quantas",
            ";                  vezes. 143 monstros, 20 regioes.",
            ";",
            "; A sobreposicao entre as duas e ZERO pares, e nao e acaso: sao familias de id",
            "; diferentes (70xxx aqui, 80xxx e 90xxx nos materiais soltos). Sao duas coisas.",
            ";",
            "; A COLUNA chance E A PROBABILIDADE POR ABATE, em percentagem, e NAO FOI MEDIDA.",
            "; Vem toda com o mesmo valor, que e escolha de balanceamento e nao um numero do",
            "; jogo. Fica em coluna, e nao numa constante escondida no codigo, para se poder",
            "; afinar par a par quando houver medicao ou queixa.",
            ";",
            "; PORQUE NAO FOI MEDIDA, e o que ISSO PROVA. Nas capturas do oficial o Espadachim",
            "; aguia morreu 773 vezes e a Garra da aguia espadachim NUNCA caiu; o Cao branco",
            "; 306 vezes, e nem Carne nem Costela nem Pele apareceram. Nenhum dos 257 itens",
            "; desta tabela caiu, em 1091 abates. O unico 70xxx que caiu foi a Pena divina, que",
            "; nao esta aqui. Isso nao e falta de amostra -- e a confirmacao de que estes itens",
            "; SO caem a quem tem a missao aceite, e o jogador da captura nao as tinha.",
            ";",
            "; COMO MEDIR: aceitar uma missao de recolha no oficial com a captura ligada e",
            "; contar quantos abates dao quantos itens. Uma missao chega para um ponto.",
            ";",
            "; A coluna quantidade e quantas unidades a missao PEDE, e nao quantas caem de cada",
            "; vez. Serve de pista da raridade: pedir 30 de um item sugere que cai muito.",
            ";",
            "; A COLUNA mapa VEM VAZIA nas regioes 19002..19014, e nao e falha da importacao: o",
            "; NOME dessas regioes nao existe em lado nenhum que tenhamos. Os ids so aparecem no",
            "; Quest.regiao e no npcShop.regiao; o Map.ini so tem 19021, 19031 e 19041 nessa",
            "; gama, e nem o IE_Maplist nem o mapWarp os trazem. As 29xxx, essas tem nome.",
            "; Para saber onde fica uma regiao sem nome, use a coluna nivel: as missoes de",
            "; nivel 1 a 10 estao na zona inicial, e assim por diante.",
            "",
        ]:
            w(l + CR)
        w("//" + TAB.join(["mob_id", "item_id", "quantidade", "chance", "regiao", "quest_id",
                           "nivel", "monstro", "item", "mapa"]) + CR)
        for (mob, item) in sorted(pares):
            qtd, reg, qid, nivel = pares[(mob, item)]
            w(TAB.join([str(mob), str(item), str(qtd), str(CHANCE_PADRAO),
                        str(reg or ""), str(qid or ""),
                        str(nivel or ""), celula(mobs.get(mob, "")),
                        celula(itens.get(item, "")),
                        celula(mapas.get(reg, ""))]) + CR)

    D.carregar()
    t = D.tabela("QuestDrop")
    print()
    print("gravado: %s (%s bytes)" % (DESTINO, "{:,}".format(os.path.getsize(DESTINO))))
    print("relido pelo servidor: %d linhas, colunas %s" % (len(t), list(t[0].keys())))
    return 0


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