# -*- coding: utf-8 -*-
r"""Le as duas listas de um modelo de arma (`Character/Weapon/*._OB`) sem assumir offsets.

O contador de cada lista fica pouco antes do primeiro nome, MAS NAO SEMPRE NA MESMA
DISTANCIA: na maioria dos modelos esta 12 bytes antes, e nos `SET_17` ha um bloco de texto
("EFFECTIDs 0x...") pelo meio que o empurra. Assumir -12 dava lixo em 4 modelos -- e num
deles (contador 10 lido como 13) foi o que travou o client em 01/09.

Aqui procura-se: para cada offset candidato antes do nome, le-se o u32 como numero de
entradas e percorre-se a lista; so se aceita o candidato cuja lista FECHA sem passar do
fim do ficheiro e sem invadir o que vem a seguir. E uma medicao, nao um palpite.

Formato de cada entrada:
    efeitos:  [valor 4][len 4][nome]
    slots:    [marcador 4][len 4][nome][dados 40]
"""
import re
import struct

CAUDA_SLOT = 40


def _percorre(d, ini, n, cauda):
    o = ini
    for _ in range(n):
        if o + 8 > len(d):
            return None
        ln = struct.unpack_from("<I", d, o + 4)[0]
        if not (1 <= ln <= 60) or o + 8 + ln + cauda > len(d):
            return None
        o = o + 8 + ln + cauda
    return o


def achar(d, primeiro_nome_off, cauda=0, limite=None):
    """(off_contador, ini, n, fim) da lista, ou None. `limite` = onde a lista tem de acabar."""
    ini = primeiro_nome_off - 8
    melhor = None
    for recuo in range(4, 84, 4):
        oc = ini - recuo
        if oc < 0:
            continue
        n = struct.unpack_from("<I", d, oc)[0]
        if not (1 <= n <= 64):
            continue
        fim = _percorre(d, ini, n, cauda)
        if fim is None:
            continue
        if limite is not None and fim > limite:
            continue
        # FICA O QUE PREENCHE MAIS. Um n pequeno "fecha" sempre -- n=1 fecha a primeira
        # entrada e para. O contador verdadeiro e o que leva a lista ate ao fim do espaco
        # disponivel, entao escolhe-se o maior `fim`, e a igualdade o recuo mais curto.
        if melhor is None or fim > melhor[3]:
            melhor = (oc, ini, n, fim)
    return melhor


def listas_do_modelo(d):
    """(efeitos, slots) em que cada um e (off_contador, ini, n, fim). None se nao der."""
    me = re.search(rb"[A-Za-z0-9_]{4,40}\._EF", d)
    ms = re.search(rb"Weapon_Enchant01", d)
    if not me or not ms:
        return None
    sl = achar(d, ms.start(), cauda=CAUDA_SLOT, limite=len(d))
    if not sl:
        return None
    ef = achar(d, me.start(), cauda=0, limite=sl[1] - 4)
    if not ef:
        return None
    return ef, sl


def entradas(d, ini, n, cauda=0):
    out, o = [], ini
    for _ in range(n):
        ln = struct.unpack_from("<I", d, o + 4)[0]
        out.append((d[o:o + 4], d[o + 8:o + 8 + ln],
                    d[o + 8 + ln:o + 8 + ln + cauda]))
        o = o + 8 + ln + cauda
    return out
