"""Check preserving a complete SSTV header versus adding blank audio later.

Usage: python reproduce.py ORIGINAL.wav PINNED_OPEN_SSTV_REPO NEW_OUTPUT_DIRECTORY
Requires NumPy, Pillow and the pinned Open-SSTV dependencies. No radio or audio device.
"""
from pathlib import Path
import hashlib, json, os, subprocess, sys, tomllib, wave
import numpy as np
from PIL import Image, ImageChops

source, repo, out = [Path(p).resolve() for p in sys.argv[1:]]
expected = 'dbb64c8cb34d3599c5aa7c36144746039632832da554eefff324a9fc68ae0166'
base = 'f773bd4fcb9da0c68ab915e9a1fa2c024bfdcc98'
assert hashlib.sha256(source.read_bytes()).hexdigest() == expected
subprocess.run(['git', 'diff', '--exit-code', base, '--', 'src'], cwd=repo, check=True, capture_output=True)
out.mkdir(parents=True, exist_ok=False)
with wave.open(str(source)) as w:
    assert (w.getframerate(), w.getnchannels(), w.getsampwidth()) == (48000, 1, 2)
    pcm = np.frombuffer(w.readframes(w.getnframes()), dtype='<i2')
silence = np.zeros(2 * 48000, dtype='<i2')
cropped = pcm[3 * 48000:]
cases = [('original', pcm), ('leading-silence', np.concatenate([silence, pcm])),
         ('missing-start', cropped), ('missing-start-with-silence', np.concatenate([silence, cropped]))]
report = {'experiment': 'Complete header versus removed start / 2026-11-05',
          'scope': 'Generated Robot36 audio; pinned Open-SSTV CLI only. Not RF, a microphone or an SSTV Signal Desk app test.',
          'source_sha256': expected, 'decoder_source_base': base,
          'decoder_revision': subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=repo, text=True).strip(),
          'decoder_version': tomllib.loads((repo / 'pyproject.toml').read_text())['project']['version'],
          'python': sys.version.split()[0], 'removed_start_seconds': 3, 'added_silence_seconds': 2,
          'new_upstream_submission': False, 'cases': []}
for name, samples in cases:
    p = out / (name + '.wav')
    with wave.open(str(p), 'wb') as w:
        w.setparams((1, 2, 48000, 0, 'NONE', 'not compressed'))
        w.writeframes(samples.astype('<i2').tobytes())
    image = out / (name + '.png')
    args = [sys.executable, '-m', 'open_sstv.cli.decode', str(p), '-o', str(image)]
    result = subprocess.run(args, cwd=repo, env={**os.environ, 'PYTHONPATH': str(repo / 'src')},
                            capture_output=True, text=True, timeout=180)
    log = (result.stdout + result.stderr).replace(str(out), 'OUTPUT').replace(str(repo), 'OPEN_SSTV_REPO')
    (out / (name + '.log')).write_text(log)
    row = {'name': name, 'sample_rate': 48000, 'channels': 1, 'bits': 16,
           'frames': len(samples), 'duration_seconds': len(samples) / 48000,
           'file_sha256': hashlib.sha256(p.read_bytes()).hexdigest(), 'exit_code': result.returncode,
           'decoded_dimensions': list(Image.open(image).size) if image.exists() else None,
           'decoder_output': log,
           'decode_command': ['python', '-m', 'open_sstv.cli.decode', p.name, '-o', image.name]}
    report['cases'].append(row)
    (out / 'report.json').write_text(json.dumps(report, indent=2) + '\n')
    print(json.dumps(row), flush=True)
assert [r['exit_code'] for r in report['cases']] == [0, 0, 1, 1]
a = Image.open(out / 'original.png').convert('RGB')
b = Image.open(out / 'leading-silence.png').convert('RGB')
report['leading_silence_pixels_match_control'] = a.size == b.size and ImageChops.difference(a, b).getbbox() is None
assert a.size == b.size == (320, 240)
assert hashlib.sha256(source.read_bytes()).hexdigest() == expected
(out / 'report.json').write_text(json.dumps(report, indent=2) + '\n')
print('PASS: preserving the complete recording and adding leading silence decode; removing the first three seconds fails automatic identification, even after blank audio is added.', flush=True)
