# -*- coding: utf-8 -*-
"""Importa mapWarpTargetPos.lod para dbo.mapa: os 384 mapas do jogo, com tipo.

Registro de 1065 bytes, cabecalho de 4 (a contagem). Campos:
    [ 0:4 ]  id do mapa
    [ 4:8 ]  id do destino do teleporte
    [ 8:16]  x  (double)      [16:24] y (double)
    [40:  ]  nome, terminado em zero

O TIPO sai do prefixo do id, e o padrao se confirma nos nomes dos 384:

    19xxx  campo aberto e cidade   Cidade Berneo, Portao Lupinel, Pantano da tristeza
    29xxx  CALABOUCO               Mina escura, Cripta de zendarun, Catacumbas da Capela
    70xxx  mina                    Mina lupinel, "Na fase D da mina"
    90/91/92xxx  campo de batalha  Campo de batalha de Decade
    10/20xxxxxx  instancia especial  Ilha flutuante, Torre Demoniaca

Isso importa porque o jogo oficial NAO devolve o jogador para dentro de um calabouco
quando ele reloga -- ele volta pra cidade. Sem essa tabela, a persistencia de posicao
que a gente acabou de ligar faria o personagem renascer dentro da instancia.
"""
import os
import struct
import sys

import pyodbc

sys.path.insert(0, r"D:\RoshEmulator\server")
import lod_crypto  # noqa: E402

BASE = r"C:\Games\Karos\ResUnpak\Interface\IE_Lod"
CONEXAO = ("DRIVER={ODBC Driver 18 for SQL Server};SERVER=localhost;"
           "DATABASE=Rosh;Trusted_Connection=yes;TrustServerCertificate=yes")
TAM = 1065

TABELA = """
IF OBJECT_ID('dbo.mapa') IS NOT NULL DROP TABLE dbo.mapa;
CREATE TABLE dbo.mapa (
    mapa_id     BIGINT      NOT NULL PRIMARY KEY,
    nome        NVARCHAR(80) NULL,
    destino_id  BIGINT       NULL,
    x           FLOAT        NULL,
    y           FLOAT        NULL,
    tipo        VARCHAR(16)  NULL,
    persiste    BIT          NOT NULL
);
"""


def tipo_de(mapa_id: int) -> tuple[str, bool]:
    """(tipo, persiste). persiste=0 significa: nao salvar a posicao aqui."""
    p = str(mapa_id)
    if p.startswith("19"):
        return "campo", True
    if p.startswith("29"):
        return "calabouco", False
    if p.startswith("70"):
        return "mina", False
    if p[:2] in ("90", "91", "92"):
        return "batalha", False
    if p[:2] in ("10", "20") and len(p) > 5:
        return "instancia", False
    return "outro", False


def registros():
    for suf in ("_pt", "_en", ""):
        cam = os.path.join(BASE, "mapWarpTargetPos.lod" + suf)
        if os.path.exists(cam):
            break
    else:
        raise SystemExit("nao achei o mapWarpTargetPos.lod")
    d = lod_crypto.decrypt_lod_stream(open(cam, "rb").read())
    (n,) = struct.unpack_from("<I", d, 0)
    vistos = set()
    for i in range(n):
        r = d[4 + i * TAM: 4 + (i + 1) * TAM]
        if len(r) < TAM:
            break
        mapa_id, destino = struct.unpack_from("<2I", r, 0)
        x, y = struct.unpack_from("<2d", r, 8)
        bruto = r[40:100]
        z = bruto.find(b"\x00")
        nome = bruto[:z if z >= 0 else len(bruto)].decode("latin-1", "replace").strip()
        if mapa_id in vistos:
            continue
        vistos.add(mapa_id)
        tipo, persiste = tipo_de(mapa_id)
        yield (mapa_id, nome, destino, x, y, tipo, 1 if persiste else 0)


def main() -> int:
    linhas = list(registros())
    con = pyodbc.connect(CONEXAO, autocommit=False)
    cur = con.cursor()
    for parte in TABELA.split(";"):
        if parte.strip():
            cur.execute(parte)
    cur.fast_executemany = True
    cur.executemany("INSERT INTO dbo.mapa (mapa_id, nome, destino_id, x, y, tipo, persiste) "
                    "VALUES (?,?,?,?,?,?,?)", linhas)
    con.commit()
    cur.execute("SELECT tipo, COUNT(*), SUM(CAST(persiste AS INT)) FROM dbo.mapa "
                "GROUP BY tipo ORDER BY COUNT(*) DESC")
    print("dbo.mapa: %d mapas" % len(linhas))
    for t, n, p in cur.fetchall():
        print("   %-11s %3d mapas, %d salvam posicao" % (t, n, p or 0))
    print()
    cur.execute("SELECT TOP 6 mapa_id, nome FROM dbo.mapa WHERE persiste = 0 ORDER BY mapa_id")
    print("exemplos onde a posicao NAO deve ser salva:")
    for l in cur.fetchall():
        print("   %-9d %s" % (l[0], l[1]))
    con.close()
    return 0


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