#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
scan_t00s.py — inspeciona um arquivo LATLON<itag>.t00s (saida NOPACK do ETAPOST).

Layout (ver post_new/NOPACK.f):
  rec 1 : IHRST,IMM,IDD,IYY,IHH                      (5 x int4)
  rec 2 : KGTYP(int4), PROJ(char6), NORTH(log4),
          IMOUT(int4), JMOUT(int4),
          POLEI,POLEJ,ALATVT,ALONVT,XMESHL           (5 x real4)
  depois, por campo:
  rec   : FIELD (char20), SFC (real4)
  rec   : GRID(IMOUT,JMOUT)                          (real4)

Registros Fortran sequential unformatted: marcador int4 antes/depois.

Uso: python3 scan_t00s.py LATLON2026010100.t00s [-v]
  -v  imprime tambem min/max/#NaN de cada campo (le os dados; mais lento)
"""
import struct
import sys


def read_marker(f):
    b = f.read(4)
    if len(b) < 4:
        return None
    return struct.unpack('<i', b)[0]


def read_record(f, want_data=True):
    """Le um registro; devolve (bytes|None, nbytes) ou (None, -1) em EOF/truncagem."""
    pos = f.tell()
    n = read_marker(f)
    if n is None:
        return None, -1, pos, 'EOF limpo'
    if n < 0 or n > 2**31 - 8:
        return None, -1, pos, f'marcador invalido ({n}) — arquivo corrompido/truncado'
    if want_data:
        data = f.read(n)
        if len(data) < n:
            return None, -1, pos, (f'registro truncado: marcador diz {n} bytes, '
                                   f'so restam {len(data)}')
    else:
        data = None
        f.seek(n, 1)
    tail = read_marker(f)
    if tail is None:
        return None, -1, pos, f'registro truncado: falta marcador final (esperado {n})'
    if tail != n:
        return None, -1, pos, f'marcadores nao batem ({n} != {tail})'
    return data, n, pos, None


def main():
    args = [a for a in sys.argv[1:] if a != '-v']
    verbose = '-v' in sys.argv[1:]
    if not args:
        print(__doc__)
        sys.exit(1)
    fname = args[0]
    f = open(fname, 'rb')
    f.seek(0, 2)
    fsize = f.tell()
    f.seek(0)
    print(f'arquivo: {fname}  ({fsize} bytes)')

    # header rec 1
    data, n, pos, err = read_record(f)
    if err:
        print(f'ERRO no header rec1 @byte {pos}: {err}')
        sys.exit(2)
    ihrst, imm, idd, iyy, ihh = struct.unpack('<5i', data[:20])
    print(f'rec1: IHRST={ihrst} data={idd:02d}/{imm:02d}/{iyy + 1900} IHH={ihh}')

    # header rec 2
    data, n, pos, err = read_record(f)
    if err:
        print(f'ERRO no header rec2 @byte {pos}: {err}')
        sys.exit(2)
    kgtyp = struct.unpack('<i', data[0:4])[0]
    proj = data[4:10].decode('ascii', 'replace')
    north, imout, jmout = struct.unpack('<3i', data[10:22])
    polei, polej, alatvt, alonvt, xmeshl = struct.unpack('<5f', data[22:42])
    print(f'rec2: KGTYP={kgtyp} PROJ={proj!r} NORTH={north} '
          f'IMOUT={imout} JMOUT={jmout}')
    print(f'      POLEI={polei} POLEJ={polej} ALATVT={alatvt} '
          f'ALONVT={alonvt} XMESHL={xmeshl}')
    grid_bytes = imout * jmout * 4
    print(f'      => cada registro de dados deve ter {grid_bytes} bytes')

    nfld = 0
    while True:
        # header do campo: char20 + real4
        data, n, pos, err = read_record(f)
        if err:
            if err == 'EOF limpo':
                print(f'\nfim: {nfld} campos completos, EOF em fronteira de registro.')
                break
            print(f'\nTRUNCAGEM no header do campo #{nfld + 1} @byte {pos}: {err}')
            break
        if n != 24:
            print(f'\nAVISO: header de campo #{nfld + 1} com {n} bytes (esperado 24)')
        field = data[:20].decode('ascii', 'replace')
        sfc = struct.unpack('<f', data[20:24])[0]

        # dados
        data, n, pos, err = read_record(f, want_data=verbose)
        if err:
            print(f'\nTRUNCAGEM nos dados do campo #{nfld + 1} '
                  f'{field!r} SFC={sfc} @byte {pos}: {err}')
            break
        nfld += 1
        line = f'{nfld:3d}  {field}  SFC={sfc:<12g} rec={n}B'
        if n != grid_bytes:
            line += f'  <-- TAMANHO ERRADO (esperado {grid_bytes})'
        if verbose:
            vals = struct.unpack(f'<{n // 4}f', data)
            nan = sum(1 for v in vals if v != v)
            finite = [v for v in vals if v == v]
            zeros = sum(1 for v in finite if v == 0.0)
            if finite:
                line += (f'  min={min(finite):<12g} max={max(finite):<12g} '
                         f'zeros={zeros} NaN={nan}')
            else:
                line += f'  TODOS NaN ({nan})'
        print(line)

    f.close()


if __name__ == '__main__':
    main()
