# -*- coding: utf-8 -*-
"""Importa classInfo.lod: as 56 classes do jogo, com nome, arma e descricao.

    python tools/importar_classes_info.py            mostra
    python tools/importar_classes_info.py --aplicar  grava em Data/Class.ini

Registro de 1202 bytes, 56 deles. O cabecalho declara 56 e todos os 56 ids batem com a
arvore do ClassEvolution.ini, entao o passo esta certo.

    [   0:4] id da classe
    [  76: ] nome, terminado em zero
    [ 127: ] arma que a classe usa
    [ 178: ] descricao longa

Validado contra os prints do jogador: 800 sai "Canhoneiro" e 820 sai "Engenheiro", que sao
os dois rotulos que apareceram na ficha dele antes e depois da promocao.

O QUE ESTE ARQUIVO NAO TEM: os status. Procurei 189/117 (HP e MP do Artilheiro) e a
sequencia 10/5/9/7/9 dos atributos dentro do registro, e nao estao la. Os numeros das 7
classes base vieram de CAPTURA, do pacote 02 05 -- ver tools/extrair_classes.py. Para as 48
promovidas so ha o mesmo caminho: promover um personagem e ler a ficha nova.

Por isso este importador NAO sobrescreve hp_base, mp_base nem os atributos das classes que
ja existem. Ele acrescenta as que faltam e preenche o nome de todas.
"""
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

LOD = r"C:\Games\Karos\ResUnpak\Interface\IE_Lod\classInfo.lod_pt"
PASSO = 1202
OFF_NOME, OFF_ARMA, OFF_DESC = 76, 127, 178
NL = chr(10)
BARRA = chr(92)
# 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("Class", "Character")


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(chr(10), BARRA + "n")


def texto(d, off, limite=400):
    fim = d.find(b"\x00", off)
    if fim < 0 or fim - off > limite:
        fim = off + limite
    return d[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 = 4 + k * PASSO
        if o + PASSO > len(d):
            break
        yield {"classe_id": struct.unpack_from("<I", d, o)[0],
               "nome_client": texto(d, o + OFF_NOME, 60),
               "arma": texto(d, o + OFF_ARMA, 60),
               "descricao": texto(d, o + OFF_DESC, 400)}


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

    faltavam = sorted(set(novos) - set(atuais))
    print("classInfo.lod: %d classes" % len(novos))
    print("Class.ini    : %d classes" % len(atuais))
    print("faltando     : %d  -> %s" % (len(faltavam), faltavam[:12]))
    print()
    print("nomes que DIVERGEM entre o nosso e o do client:")
    for cid in sorted(set(atuais) & set(novos)):
        a, b = (atuais[cid].get("nome") or ""), novos[cid]["nome_client"]
        if a and b and a != b:
            print("   %-5d nosso %-14s | client %s" % (cid, a, b))

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

    # junta: mantem tudo que ja existe, acrescenta nome_client/arma/descricao e as faltantes
    linhas = []
    for cid in sorted(set(atuais) | set(novos)):
        r = dict(atuais.get(cid) or {"classe_id": cid})
        c = novos.get(cid)
        if c:
            r["nome_client"] = c["nome_client"]
            r["arma"] = c["arma"]
            r["descricao"] = c["descricao"]
            r.setdefault("nome", c["nome_client"])
            if not r.get("nome"):
                r["nome"] = c["nome_client"]
        linhas.append(r)

    cols = []
    for r in linhas:
        for k in r:
            if k not in cols:
                cols.append(k)
    with open(DESTINO, "w", encoding="utf-8", newline=NL) as fp:
        fp.write("; Class -- as 56 classes do jogo." + NL)
        fp.write("; nome_client, arma e descricao vem do classInfo.lod." + NL)
        fp.write("; hp_base, mp_base e os atributos vem de CAPTURA (pacote 02 05) e so" + NL)
        fp.write("; existem para as classes base -- as promovidas ainda precisam ser medidas." + NL)
        fp.write("; colunas: " + ", ".join(cols) + NL + NL)
        for r in linhas:
            fp.write("[%d]" % r["classe_id"] + NL)
            for c in cols:
                if c == "classe_id":
                    continue
                v = escapar(r.get(c))
                if v not in (None, ""):
                    fp.write("%s = %s" % (c, v) + NL)
            fp.write(NL)
    print()
    print("gravado: Class.ini com %d classes (%s bytes)"
          % (len(linhas), "{:,}".format(os.path.getsize(DESTINO))))
    return 0


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