"""Lossless packet remux of renderer chunks onto the original L01 clock."""
import bootstrap
import csv
from fractions import Fraction
import json
import shutil
import time
import av
from inputs import OUT
from render_space import entries_for,sha,TB,W,H

entries=entries_for('SELF_L01')
dest=OUT/'download/SELF_L01';data=dest/'data';data.mkdir(parents=True,exist_ok=True)
output=dest/'B_Integrated_Space_Review.mp4';report=data/'video_validation.json'
if report.exists():
    assert sha(output)==json.loads(report.read_text())['sha256'];print('REUSED L01');raise SystemExit(0)
draft=dest/'.B_Integrated_Space_Review.remux_v3.partial.mp4';assert not draft.exists() and not output.exists()
started=time.monotonic();con=av.open(str(draft),'w',options={'movflags':'faststart'});stream=None
origin=entries[0]['seconds'];last_dts=None;states=[]
for start in range(0,len(entries),1000):
    token=f'{start:05d}';folder=OUT/'work/l01_video_segments_v2'
    check=json.loads((folder/(token+'.json')).read_text());path=folder/(token+'.mp4')
    assert sha(path)==check['sha256']
    offset=round((check['origin_seconds']-origin)*90000)
    absolute_ticks={round((e['seconds']-check['origin_seconds'])*90000):round((e['seconds']-origin)*90000)
                    for e in entries[start:start+1000]}
    with av.open(str(path)) as inp:
        source=inp.streams.video[0]
        if stream is None:
            stream=con.add_stream_from_template(source);stream.time_base=TB
        for packet in inp.demux(source):
            if packet.dts is None:continue
            local_pts=round(packet.pts*packet.time_base*90000)
            local_dts=round(packet.dts*packet.time_base*90000)
            pts=absolute_ticks[local_pts]
            dts=absolute_ticks.get(local_dts,local_dts+offset)
            assert last_dts is None or dts>last_dts,(token,dts,last_dts)
            last_dts=dts;duration=round(packet.duration*packet.time_base*90000)
            packet.stream=stream;packet.time_base=TB;packet.pts=pts;packet.dts=dts;packet.duration=duration
            con.mux(packet)
    for s in json.loads((folder/(token+'_display.json')).read_text()):
        s['output_frame']+=start;states.append(s)
con.close();times=[]
with av.open(str(draft)) as inp:
    inp.streams.video[0].codec_context.thread_count=1
    for frame in inp.decode(video=0):
        assert not frame.is_corrupt and (frame.width,frame.height)==(W,H)
        times.append(float(frame.pts*frame.time_base))
assert len(times)==len(entries)==8306
error=max(abs(t-round((e['seconds']-origin)*90000)/90000) for t,e in zip(times,entries))
assert error<1/90000 and all(b>a for a,b in zip(times,times[1:]))
assert entries[1044]['kind']=='gap' and entries[1045]['part_id']=='L01_P02'
assert states[1044]['state']=='GAP'
mapping=[dict(output_frame=i+1,kind=e['kind'],part_id=e['part_id'],sample=e.get('row',{}).get('sample'),
    source_frame=e.get('row',{}).get('source_frame'),source_seconds=e['seconds'],output_seconds=times[i],
    review_frame=e.get('result',{}).get('review_frame')) for i,e in enumerate(entries)]
with (data/'B_Integrated_Space_Review_frame_map.csv').open('w',newline='') as f:
    w=csv.DictWriter(f,fieldnames=mapping[0]);w.writeheader();w.writerows(mapping)
(data/'B_Integrated_Space_Review_display.json').write_text(json.dumps(states,separators=(',',':')))
draft.rename(output)
result=dict(status='PASS',frames=8306,observations=8305,gap_slates=1,origin_seconds=origin,
    last_source_seconds=entries[-1]['seconds'],sha256=sha(output),resolution=[W,H],nominal_fps=10,
    max_pts_error=error,observation_gap_seconds=1.057729,slate_start_seconds=entries[1044]['seconds'],
    slate_end_seconds=entries[1045]['seconds'],slate_duration_seconds=entries[1045]['seconds']-entries[1044]['seconds'],
    chunk_count=9,stream_copy=True,text_overlap=0,text_overflow=0,rgb_hashes_verified=8305,
    elapsed_seconds=time.monotonic()-started)
report.write_text(json.dumps(result,indent=2));print('L01_ASSEMBLY_PASS',json.dumps(result),flush=True)
failed=dest/'.B_Integrated_Space_Review.remux_v2.partial.mp4'
if failed.exists():
    target=OUT/'work/l01_video_segments_v2/remux_attempt02_pts_rounding.mp4'
    assert not target.exists();failed.rename(target)
for source,name in (('smoke_sample_00003.jpg','L01_gap.jpg'),('smoke_sample_00004.jpg','L01_part2_start.jpg')):
    shutil.copy2(OUT/'work/l01_video_segments_v2'/source,data/name)
