# -*- coding: utf-8 -*-
"""
Experimentul 2 — Propunerea de comunicare supraluminala din superluminal.pdf
(Lee Wen Wu, 2021, sectiunea 4: "Modification to the DCQE experiment").

Alice primeste fotonii semnal, Bob primeste fotonii idler pe un ecran D0 indepartat.
Pentru fiecare bit, Alice alege:
  bit "0" -> masoara which-slit (D3/D4)            (sectiunea 4.1)
  bit "1" -> combina caile spre un singur detector D5 (sectiunea 4.3, Fig. 4)

Doua modele de simulare:
  * "QM standard" — calculul corect (teorema de ne-semnalizare): distributia
    marginala a lui Bob este MEREU p(x) = (|Psi_A|^2 + |Psi_B|^2)/2 (Eq. 30/32);
    media pe fazele aleatoare phi_s(u0) ale colapsului partial sterge franjele.
  * "Colaps partial (ipoteza lucrarii)" — presupunerea din lucrare ca phi_s(u0)
    este constanta pentru toti fotonii (Eq. 51-53), deci pentru bit "1" Bob ar
    vedea franje p(x) ~ |Psi_A + Psi_B|^2 si mesajul ar trece mai repede ca lumina.

Simularea arata de ce propunerea esueaza in QM standard si ce ar fi trebuit
sa se intample daca ipoteza lucrarii ar fi fost corecta.
"""
import sys
import numpy as np
import tkinter as tk
from tkinter import ttk
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

from phys_comun import amplitudini_fante, esantioneaza, contrast_franje, perioada_franje

XMAX = 12.0
NX = 2001
NBIN = 97
MAXBITI = 8
PRAG = 0.15  # prag de contrast pentru decodarea unui "1"


class AppWenWu:
    def __init__(self, root):
        self.root = root
        root.title("Exp. 2 — Comunicare supraluminala? (Lee Wen Wu 2021, DCQE modificat)")
        self.rng = np.random.default_rng()
        self.x = np.linspace(-XMAX, XMAX, NX)
        self.bins = np.linspace(-XMAX, XMAX, NBIN + 1)
        self.centre = 0.5 * (self.bins[:-1] + self.bins[1:])
        self.psiA, self.psiB = amplitudini_fante(self.x)
        self.p_incoerent = (np.abs(self.psiA) ** 2 + np.abs(self.psiB) ** 2) / 2
        pc = np.abs(self.psiA + self.psiB) ** 2
        self.p_coerent = pc / np.trapezoid(pc, self.x)
        self.per = perioada_franje()
        self.activ = False

        ctl = ttk.Frame(root, padding=8)
        ctl.pack(side=tk.LEFT, fill=tk.Y)
        ttk.Label(ctl, text="Alice -> Bob\n(fotoni intricati SPDC)",
                  font=("Segoe UI", 12, "bold")).pack(pady=(0, 8))

        ttk.Label(ctl, text=f"Mesaj binar (max {MAXBITI} biti):").pack(anchor="w")
        self.v_msg = tk.StringVar(value="1011")
        ttk.Entry(ctl, textvariable=self.v_msg, width=14).pack(anchor="w", pady=2)

        ttk.Label(ctl, text="Fotoni pe bit:").pack(anchor="w", pady=(8, 0))
        self.v_n = tk.IntVar(value=4000)
        ttk.Scale(ctl, from_=500, to=20000, variable=self.v_n).pack(fill=tk.X)

        ttk.Label(ctl, text="Model fizic:").pack(anchor="w", pady=(8, 0))
        self.v_model = tk.StringVar(value="qm")
        ttk.Radiobutton(ctl, text="QM standard (corect, no-signaling)",
                        variable=self.v_model, value="qm").pack(anchor="w")
        ttk.Radiobutton(ctl, text="Colaps partial (ipoteza lucrarii)",
                        variable=self.v_model, value="lucrare").pack(anchor="w")

        self.btn = ttk.Button(ctl, text="Transmite mesajul", command=self.transmite)
        self.btn.pack(fill=tk.X, pady=10)

        self.lbl_tx = ttk.Label(ctl, text="TX: -", font=("Consolas", 12))
        self.lbl_tx.pack(anchor="w")
        self.lbl_rx = ttk.Label(ctl, text="RX: -", font=("Consolas", 12))
        self.lbl_rx.pack(anchor="w")
        self.lbl_err = ttk.Label(ctl, text="", font=("Consolas", 10))
        self.lbl_err.pack(anchor="w", pady=(4, 8))

        ttk.Label(ctl, wraplength=220, justify="left", foreground="#555", text=(
            "Bob decodeaza fiecare fereastra de timp dupa contrastul franjelor "
            f"(prag {PRAG}).\n\n"
            "In modelul QM standard mesajul NU trece: distributia lui Bob este "
            "identica indiferent ce face Alice (Eq. 30 = Eq. 32 = Eq. 41). "
            "In ipoteza lucrarii (faza colapsului partial constanta), bitul '1' "
            "ar produce franje vizibile -> semnal supraluminal. Experimentele "
            "reale confirma QM standard.")).pack(anchor="w")

        self.fig = Figure(figsize=(9.5, 7), dpi=100)
        self.canvas = FigureCanvasTkAgg(self.fig, master=root)
        self.canvas.get_tk_widget().pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

        self.pregateste_axe([])
        if "--test" in sys.argv:
            self.transmite()
            root.after(3500, root.destroy)

    # ------------------------------------------------------------------
    def pregateste_axe(self, biti):
        self.fig.clear()
        n = max(1, len(biti))
        gs = self.fig.add_gridspec(2, n, hspace=0.45, wspace=0.35,
                                   height_ratios=[1.2, 1])
        self.ax_teorie = self.fig.add_subplot(gs[0, :])
        self.ax_teorie.plot(self.x, self.p_incoerent, "k", lw=1.5,
                            label="QM standard: ce vede Bob mereu")
        self.ax_teorie.plot(self.x, self.p_coerent, "r--", lw=1.2,
                            label="ipoteza lucrarii, bit '1': |Psi_A+Psi_B|^2")
        self.ax_teorie.legend(fontsize=8)
        self.ax_teorie.set_title("Distributii teoretice pe ecranul D0 al lui Bob")
        self.ax_bit = [self.fig.add_subplot(gs[1, i]) for i in range(n)]
        for axx in self.ax_bit:
            axx.set_xticks([])
            axx.set_yticks([])
        self.canvas.draw_idle()

    def transmite(self):
        if self.activ:
            return
        msg = "".join(c for c in self.v_msg.get() if c in "01")[:MAXBITI]
        if not msg:
            msg = "1"
        self.v_msg.set(msg)
        self.biti = msg
        self.rx = []
        self.pregateste_axe(msg)
        self.activ = True
        self.idx_bit = 0
        self.lbl_tx.config(text=f"TX: {msg}")
        self.lbl_rx.config(text="RX: ")
        self.lbl_err.config(text="")
        self.bit_curent_counts = np.zeros(NBIN)
        self.bit_curent_n = 0
        self.root.after(10, self.pas)

    def pas(self):
        if not self.activ:
            return
        bit = self.biti[self.idx_bit]
        model = self.v_model.get()
        if bit == "1" and model == "lucrare":
            pdf = self.p_coerent      # franje (colaps partial cu faza constanta)
        else:
            pdf = self.p_incoerent    # fara franje (QM standard / which-slit)
        nt = int(self.v_n.get())
        chunk = min(800, nt - self.bit_curent_n)
        x0, _ = esantioneaza(self.x, pdf, chunk, self.rng)
        self.bit_curent_counts += np.histogram(x0, self.bins)[0]
        self.bit_curent_n += chunk

        axx = self.ax_bit[self.idx_bit]
        axx.clear()
        axx.fill_between(self.centre, self.bit_curent_counts, step="mid",
                         color="#1f77b4")
        axx.set_title(f"bit TX='{bit}'", fontsize=9)
        axx.set_xticks([])
        axx.set_yticks([])

        if self.bit_curent_n >= nt:
            c = contrast_franje(self.bit_curent_counts, self.per, self.centre)
            decodat = "1" if c > PRAG else "0"
            self.rx.append(decodat)
            ok = decodat == bit
            axx.set_xlabel(f"C={c:.2f} -> RX='{decodat}'",
                           color=("green" if ok else "red"), fontsize=9)
            self.lbl_rx.config(text=f"RX: {''.join(self.rx)}")
            self.idx_bit += 1
            self.bit_curent_counts = np.zeros(NBIN)
            self.bit_curent_n = 0
            if self.idx_bit >= len(self.biti):
                self.activ = False
                err = sum(a != b for a, b in zip(self.biti, self.rx))
                self.lbl_err.config(
                    text=f"erori: {err}/{len(self.biti)}"
                         + ("  -> mesajul NU a trecut" if err else "  -> mesaj transmis!"))
        self.canvas.draw_idle()
        if self.activ:
            self.root.after(15, self.pas)


if __name__ == "__main__":
    root = tk.Tk()
    AppWenWu(root)
    root.mainloop()
