# -*- coding: utf-8 -*-
"""Preenche dbo.item com os campos decodificados e cria dbo.item_atributo.

Os 156 bytes numericos do itemBase sao 39 u32 LE. O que cada um e (23/08):

    0  id                       9  slot de equipamento (0 nao equipa, 1 corpo, 7 arma, 21 colar)
    2  codigo de classe         11 nivel minimo
    4  tipo (manipulador)       14 empilhamento maximo
    5  categoria                21 preco de compra      25 preco de venda
    7  linha de arma            26 linha do icone       27 coluna do icone
    34 grupo de modelo          31 duracao em segundos (itens temporarios)

O itemAttr guarda os NUMEROS de combate, uma linha por (item, refino 0..16):
    d0/d1 dano fisico min/max      d2/d3 dano magico min/max
    d4    velocidade de ataque     d5    alcance
    d6    defesa fisica            d7    defesa magica
    d8..d12 bonus ainda nao nomeados
"""
import struct
import sys

import pyodbc

sys.path.insert(0, r"D:\RoshEmulator\tools")
from itembase import registros, atributos

CONEXAO = ("DRIVER={ODBC Driver 18 for SQL Server};SERVER=localhost;"
           "DATABASE=Rosh;Trusted_Connection=yes;TrustServerCertificate=yes")

NOVAS = [
    ("classe_cod", "INT"), ("classe_req", "INT"), ("tipo", "INT"),
    ("categoria", "INT"), ("slot", "INT"), ("nivel_req", "INT"),
    ("empilha_max", "INT"), ("preco_compra", "BIGINT"), ("preco_venda", "BIGINT"),
    ("icone_linha", "INT"), ("icone_coluna", "INT"), ("duracao_seg", "INT"),
    ("arma_linha", "INT"), ("grupo_modelo", "INT"),
]

TABELA_ATTR = """
IF OBJECT_ID('dbo.item_atributo') IS NOT NULL DROP TABLE dbo.item_atributo;
CREATE TABLE dbo.item_atributo (
    item_id      INT     NOT NULL,
    refino       INT     NOT NULL,
    estrela      INT     NOT NULL DEFAULT 0,
    dano_min     FLOAT   NOT NULL DEFAULT 0,
    dano_max     FLOAT   NOT NULL DEFAULT 0,
    dano_mag_min FLOAT   NOT NULL DEFAULT 0,
    dano_mag_max FLOAT   NOT NULL DEFAULT 0,
    vel_ataque   FLOAT   NOT NULL DEFAULT 0,
    alcance      FLOAT   NOT NULL DEFAULT 0,
    defesa       FLOAT   NOT NULL DEFAULT 0,
    defesa_mag   FLOAT   NOT NULL DEFAULT 0,
    bonus8       FLOAT   NOT NULL DEFAULT 0,
    bonus9       FLOAT   NOT NULL DEFAULT 0,
    bonus10      FLOAT   NOT NULL DEFAULT 0,
    bonus11      FLOAT   NOT NULL DEFAULT 0,
    bonus12      FLOAT   NOT NULL DEFAULT 0,
    efeito       NVARCHAR(64) NULL,
    CONSTRAINT pk_item_atributo PRIMARY KEY (item_id, refino)
);
CREATE INDEX ix_item_atributo_item ON dbo.item_atributo (item_id);
"""


def main():
    con = pyodbc.connect(CONEXAO, timeout=10, autocommit=False)
    cur = con.cursor()

    existentes = {c.column_name.lower() for c in cur.columns(table="item")}
    faltando = [(n, t) for n, t in NOVAS if n not in existentes]
    if faltando:
        cur.execute("ALTER TABLE dbo.item ADD " +
                    ", ".join(f"{n} {t} NULL" for n, t in faltando))
        con.commit()
        print("colunas criadas:", ", ".join(n for n, _ in faltando))

    # ITENS NOVOS ENTRAM ANTES DO UPDATE.
    #
    # Este ficheiro so fazia UPDATE, na premissa de que a tabela ja tinha todos os itens --
    # ela foi enchida uma vez por um script avulso que nao ficou no repositorio. Enquanto o
    # client nao mudava, passava; em 31/08 o jogo actualizou e o itemBase passou de 19806
    # para 20130 itens. Os 324 novos eram lidos, contados no ecra ("20130 itens com os
    # campos preenchidos") e nao entravam em lado nenhum -- o UPDATE nao acerta em linha
    # que nao existe e nao se queixa.
    cur.execute("SELECT item_id FROM dbo.item")
    ja_tem = {int(r[0]) for r in cur.fetchall()}
    novos = []
    for num, txt in registros():
        item_id = struct.unpack_from("<I", num, 0)[0]
        if item_id in ja_tem:
            continue
        # a ordem das cadeias e a que o itembase.registros devolve:
        # 0 nome, 1 icone, 3 modelo, 11 descricao
        def _t(i):
            return (txt[i] if i < len(txt) else "") or None
        # `campos` e VARBINARY(160): entram os 156 bytes crus, sem conversao. O hex que
        # se ve no Item.ini e coisa da exportacao para Data/, nao do banco.
        novos.append((item_id, _t(0), _t(1), _t(3), _t(11), num))
    if novos:
        cur.fast_executemany = True
        cur.executemany(
            "INSERT INTO dbo.item (item_id, nome, icone, modelo, descricao, campos) "
            "VALUES (?,?,?,?,?,?)", novos)
        con.commit()
        print("dbo.item: %d item(ns) NOVO(s) inserido(s)" % len(novos))

    linhas = []
    for num, _ in registros():
        v = struct.unpack("<39I", num)
        linhas.append((
            v[2], (v[2] // 100) * 100 if v[2] < 1000 else 0, v[4], v[5], v[9], v[11],
            v[14] if v[14] < 2 ** 31 else 2 ** 31 - 1,
            v[21], v[25], v[26], v[27], v[31], v[7], v[34], v[0],
        ))
    cur.fast_executemany = True
    cur.executemany(
        "UPDATE dbo.item SET classe_cod=?, classe_req=?, tipo=?, categoria=?, slot=?, "
        "nivel_req=?, empilha_max=?, preco_compra=?, preco_venda=?, icone_linha=?, "
        "icone_coluna=?, duracao_seg=?, arma_linha=?, grupo_modelo=? WHERE item_id=?",
        linhas)
    con.commit()
    print(f"dbo.item: {len(linhas)} itens com os campos preenchidos")

    for cmd in TABELA_ATTR.split(";"):
        if cmd.strip():
            cur.execute(cmd)
    con.commit()

    attrs, vistos = [], set()
    for item_id, refino, estrela, d, s in atributos():
        if (item_id, refino) in vistos:
            continue
        vistos.add((item_id, refino))
        attrs.append((item_id, refino, estrela, d[0], d[1], d[2], d[3], d[4], d[5],
                      d[6], d[7], d[8], d[9], d[10], d[11], d[12], s[0][:64] or None))
    cur.executemany(
        "INSERT INTO dbo.item_atributo (item_id, refino, estrela, dano_min, dano_max, "
        "dano_mag_min, dano_mag_max, vel_ataque, alcance, defesa, defesa_mag, bonus8, "
        "bonus9, bonus10, bonus11, bonus12, efeito) "
        "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", attrs)
    con.commit()
    print(f"dbo.item_atributo: {len(attrs)} linhas "
          f"({len({a[0] for a in attrs})} itens x refino 0..16)")
    con.close()


if __name__ == "__main__":
    main()
