# -*- coding: utf-8 -*-
r"""Converte uma tabela de Data/ do formato de seccao para o tabular alinhado.

    python tools/seccao_para_tabela.py <ficheiro.ini> [--aplicar]
    python tools/seccao_para_tabela.py --todos [--aplicar]

O formato de seccao repete o nome de cada coluna em cada registo:

    [1001]
    item_id = 1001
    nome = Grande espada
    slot = 7

O tabular escreve o nome uma vez so:

    //item_id  nome           slot
    1001       Grande espada  7

No Item.ini, com 19.806 registos e 20 colunas, a diferenca e de 13,8 MB para menos de 3.

CELULA VAZIA LEVA MARCADOR. E a parte que interessa: no formato de seccao, uma coluna sem
valor simplesmente nao aparece na seccao, e nao se perde nada. No tabular separado por
espacos, uma celula vazia seria so espacos -- indistinguivel do separador -- e a coluna
desaparecia, deslocando todas as seguintes uma casa. Foi assim que o Item.ini se partiu em
27/08 e o servidor passou a recusar equipar tudo, porque `slot` lia o valor de `classe_req`.
Aqui escreve-se `~` nessas celulas, e `data_files._separar` converte-o de volta em vazio.
"""
import glob
import io
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

CR = chr(13) + chr(10)
TAB = chr(9)
DADOS = os.path.join(RAIZ, "server", "Data")
LARGURA_MAXIMA = 44
MARGEM = 2


def ler_seccoes(caminho):
    """(cabecalho, colunas, registos) ou None se nao for formato de seccao."""
    txt = io.open(caminho, encoding="latin-1").read()
    linhas = txt.replace(chr(13), "").split(chr(10))
    cabecalho, registos, atual = [], [], None
    colunas = []
    for l in linhas:
        if l.startswith(";"):
            if atual is None and not registos:
                cabecalho.append(l)
            continue
        if l.startswith("//"):
            return None                      # ja e tabular
        if l.startswith("[") and l.rstrip().endswith("]"):
            if atual:
                registos.append(atual)
            atual = {}
            continue
        if atual is not None and "=" in l:
            k, _, v = l.partition("=")
            k, v = k.strip(), v.strip()
            atual[k] = v
            if k not in colunas:
                colunas.append(k)
    if atual:
        registos.append(atual)
    if not registos:
        return None
    return cabecalho, colunas, registos


def escrever(caminho, cabecalho, colunas, registos):
    larguras = []
    for i, c in enumerate(colunas):
        maior = len(c) + (2 if i == 0 else 0)
        for r in registos:
            maior = max(maior, len(r.get(c, "") or ""))
        larguras.append(min(maior + MARGEM, LARGURA_MAXIMA))

    def linha(vals, prefixo=""):
        partes = []
        for i, v in enumerate(vals):
            if i == 0 and prefixo:
                v = prefixo + v
            partes.append(v)
        # TABULACAO COMO SEPARADOR, com o alinhamento so por cima.
        #
        # Tentou-se separar por espacos, para o ficheiro se ler alinhado num editor. Partiu
        # os dados tres vezes em 27/08, cada uma por um motivo diferente: celula vazia que
        # desaparecia, valor mais comprido que a coluna que colava no vizinho, e valor com
        # dois espacos la dentro. Cada correcao destapou a seguinte.
        #
        # A tabulacao nao tem nenhum desses problemas: e um caracter que nao aparece nos
        # dados, e duas seguidas dizem "celula vazia" sem precisar de marcador. O alinhamento
        # visual fica pelos espacos ANTES dela -- se o editor expandir a tabulacao e desalinhar
        # um pouco, paciencia; dados certos valem mais do que colunas direitinhas.
        fora = []
        for i, v in enumerate(partes):
            if i == len(partes) - 1:
                fora.append(v)
            else:
                fora.append(v.ljust(max(0, larguras[i] - 1)) + TAB)
        return "".join(fora).rstrip()

    with io.open(caminho, "w", encoding="latin-1", newline="") as f:
        for c in cabecalho:
            if c.startswith("; colunas:"):
                c = "; colunas: " + ", ".join(colunas)
            f.write(c + CR)
        f.write(CR)
        f.write(linha(colunas, "//") + CR)
        for r in registos:
            f.write(linha([r.get(c, "") for c in colunas]) + CR)


def main() -> int:
    aplicar = "--aplicar" in sys.argv
    args = [a for a in sys.argv[1:] if not a.startswith("--")]
    if "--todos" in sys.argv:
        alvos = sorted(glob.glob(os.path.join(DADOS, "**", "*.ini"), recursive=True))
    elif args:
        alvos = args
    else:
        print(__doc__.strip().splitlines()[2])
        return 1

    total_antes = total_depois = 0
    feitos = 0
    for cam in alvos:
        if not os.path.exists(cam):
            print("nao existe: %s" % cam)
            continue
        r = ler_seccoes(cam)
        if r is None:
            continue
        cabecalho, colunas, registos = r
        antes = os.path.getsize(cam)
        if aplicar:
            escrever(cam, cabecalho, colunas, registos)
            depois = os.path.getsize(cam)
        else:
            depois = antes
        total_antes += antes
        total_depois += depois
        feitos += 1
        print("   %-40s %6d registos  %2d colunas  %9d -> %9d bytes"
              % (os.path.relpath(cam, DADOS), len(registos), len(colunas), antes, depois))
    print()
    print("convertidas: %d   %d -> %d bytes" % (feitos, total_antes, total_depois))
    if not aplicar:
        print("nada gravado. rode com --aplicar")
    return 0


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