"""Eğitim - 01 senkronu (sadece okur, çıktı: senkron.json).

Ana saat: Rode vericilerinin BWF time_reference değeri (gece yarısından beri saniye). İki vericinin saati
birbirini 2 ms içinde tutuyor, bu yüzden WAV'lar doğrudan bu değerle yerleşir.
Kamera klipleri: kameranın sol/sağ kanalı, konuşma bandına süzülüp WAV'larla normalize çapraz
korelasyonla karşılaştırılır; klip başına 3 noktada ölçülür, tutarlı sonuçların ortancası alınır.
Aynı ölçüm, kanalın hangi mikrofonu taşıdığını da gösterir (korelasyon katsayısı çok yüksekse
o kanal o vericinin sinyalidir).
"""
import json, os, re, subprocess, glob
import numpy as np

K = "/home/stark/Desktop/Eğitim - 01"
OUT = os.path.join(K, "Önizleme", "medya", "senkron.json")
SR = 4000
SUZGEC = "highpass=f=250,lowpass=f=1800"


def ffprobe(p, args):
    return subprocess.run(["ffprobe", "-v", "error", *args, p], capture_output=True, text=True).stdout


def ses(p, bas, sure, kanal=None):
    pan = f"pan=mono|c0=c{kanal}," if kanal is not None else "pan=mono|c0=c0,"
    r = subprocess.run(["ffmpeg", "-v", "error", "-nostdin", "-ss", f"{max(bas, 0):.3f}", "-t", f"{sure:.3f}", "-i", p,
                        "-map", "0:a:0", "-af", pan + SUZGEC, "-ar", str(SR), "-f", "f32le", "-"], capture_output=True)
    return np.frombuffer(r.stdout, np.float32).astype(np.float64)


def ncc(a, b):
    """a'nın b içindeki en iyi yeri (sn) ve normalize korelasyon katsayısı (0..1)."""
    if len(a) < SR * 5 or len(b) < len(a):
        return None, 0.0
    a = (a - a.mean()); na = np.linalg.norm(a) + 1e-12
    n = 1 << int(np.ceil(np.log2(len(a) + len(b))))
    c = np.fft.irfft(np.fft.rfft(b, n) * np.conj(np.fft.rfft(a, n)), n)[: len(b) - len(a) + 1]
    cs = np.concatenate([[0], np.cumsum(b)]); cs2 = np.concatenate([[0], np.cumsum(b * b)])
    L = len(a); s = cs[L:] - cs[:-L]; s2 = cs2[L:] - cs2[:-L]
    nb = np.sqrt(np.maximum(s2 - s * s / L, 1e-12))
    r = c / (na * nb)
    i = int(np.argmax(r))
    return i / SR, float(r[i])


wavlar = []
for w in sorted(glob.glob(f"{K}/*/*.WAV")):
    j = json.loads(ffprobe(w, ["-show_entries", "format=duration:format_tags=time_reference", "-of", "json"]))["format"]
    no = int(os.path.basename(w)[:5])
    wavlar.append(dict(ad=os.path.basename(w), yol=w, verici=1 if no < 57 else 2, bas=int(j["tags"]["time_reference"]) / 48000,
                       sure=float(j["duration"]), bolum=os.path.basename(os.path.dirname(w))))
uzun = [w for w in wavlar if w["sure"] > 60]


def olc(yol, kanal, nokta, tahmin, pencere=45, pay=180):
    """Klipteki 'nokta' sn'den başlayan parçayı, o anı kapsayan her uzun WAV'da ara."""
    t = tahmin + nokta
    a = ses(yol, nokta, pencere, kanal)
    sonuc = []
    for w in uzun:
        b0 = max(t - pay, w["bas"]); b1 = min(t + pencere + pay, w["bas"] + w["sure"])
        if b1 - b0 < pencere + 10:
            continue
        b = ses(w["yol"], b0 - w["bas"], b1 - b0)
        lag, r = ncc(a, b)
        if lag is not None:
            sonuc.append(dict(wav=w["ad"], verici=w["verici"], bas=b0 + lag - nokta, r=r))
    return sonuc


out = dict(wav=[{k: v for k, v in w.items() if k != "yol"} for w in wavlar], klip=[])
for x in sorted(glob.glob(f"{K}/*/C*.MP4")):
    ad = os.path.basename(x)[:-4]
    xml = open(x[:-4] + "M01.XML", encoding="utf-8", errors="ignore").read()
    m = re.search(r'CreationDate value="\d{4}-\d\d-\d\dT(\d\d):(\d\d):(\d\d)', xml)
    tah = int(m[1]) * 3600 + int(m[2]) * 60 + int(m[3])
    sure = float(ffprobe(x, ["-show_entries", "format=duration", "-of", "csv=p=0"]).strip())
    r = dict(ad=ad, kamera="A" if ad.startswith("C19") else "B", bolum=os.path.basename(os.path.dirname(x)),
             sure=sure, xml_saat=tah, olcum=[])
    noktalar = [n for n in (30, sure / 2, sure - 75) if 0 <= n < sure - 50] if sure > 120 else []
    for kanal in (0, 1):
        for n in noktalar:
            for o in olc(x, kanal, n, tah):
                r["olcum"].append(dict(kanal="LR"[kanal], nokta=round(n, 1), **o))
    out["klip"].append(r)
    ozet = {}
    for o in r["olcum"]:
        ozet.setdefault((o["kanal"], o["verici"]), []).append((round(o["bas"], 2), round(o["r"], 2)))
    print(ad, r["kamera"], {f"{k[0]}-v{k[1]}": v for k, v in ozet.items()}, flush=True)
json.dump(out, open(OUT, "w"), ensure_ascii=False, indent=1)
print("yazıldı")
