"""
Hyperrealistic voltage-based flip-flops: SR, D, JK, and T.
Each flip-flop is built from voltage-based logic gates and simulates real-world behavior.
"""
from logic_gates import NANDGate, ANDGate, ORGate, NOTGate, VDD, VSS, VTH, GATE_DELAY
import time

class SRFlipFlop:
    """Set-Reset flip-flop using cross-coupled NAND gates."""
    def __init__(self):
        self.nand1 = NANDGate()
        self.nand2 = NANDGate()
        self.q = VSS
        self.q_bar = VDD

    def update(self, s, r):
        # s, r are voltages
        # Cross-coupled NANDs
        self.q = self.nand1.output(s, self.q_bar)
        self.q_bar = self.nand2.output(r, self.q)
        return self.q, self.q_bar
        self.q = q_new
        self.q_bar = q_bar_new
        return self.q, self.q_bar

class DFlipFlop:
    """D (Data) flip-flop using SR flip-flop and NOT gate."""
    def __init__(self):
        self.sr = SRFlipFlop()
        self.notg = NOTGate()

    def update(self, d, clk):
        # d, clk are voltages
        d_not = self.notg.output(d)
        if clk > VTH:
            self.sr.update(d, d_not)
        return self.sr.q, self.sr.q_bar

class JKFlipFlop:
    """JK flip-flop using master-slave configuration."""
    def __init__(self):
        self.q = VSS
        self.q_bar = VDD
        self.master = SRFlipFlop()
        self.slave = SRFlipFlop()

    def update(self, j, k, clk):
        # j, k, clk are voltages
        if clk > VTH:
            # Master stage
            if j > VTH and k > VTH:  # Toggle
                self.q = VDD if self.q == VSS else VSS
                self.q_bar = VSS if self.q == VDD else VDD
            elif j > VTH:  # Set
                self.q = VDD
                self.q_bar = VSS
            elif k > VTH:  # Reset
                self.q = VSS
                self.q_bar = VDD
        return self.q, self.q_bar

class TFlipFlop:
    """T (Toggle) flip-flop using JK flip-flop."""
    def __init__(self):
        self.jk = JKFlipFlop()

    def update(self, t, clk):
        # t, clk are voltages
        return self.jk.update(t, t, clk)

# Example usage
if __name__ == "__main__":
    print("SR Flip-Flop:")
    sr = SRFlipFlop()
    print("Set:", sr.update(VDD, VSS))
    print("Reset:", sr.update(VSS, VDD))
    print("Hold:", sr.update(VSS, VSS))

    print("\nD Flip-Flop:")
    dff = DFlipFlop()
    print("D=1, CLK=1:", dff.update(VDD, VDD))
    print("D=0, CLK=1:", dff.update(VSS, VDD))

    print("\nJK Flip-Flop:")
    jk = JKFlipFlop()
    print("J=1, K=0, CLK=1:", jk.update(VDD, VSS, VDD))
    print("J=0, K=1, CLK=1:", jk.update(VSS, VDD, VDD))
    print("J=1, K=1, CLK=1 (toggle):", jk.update(VDD, VDD, VDD))

    print("\nT Flip-Flop:")
    tff = TFlipFlop()
    print("T=1, CLK=1 (toggle):", tff.update(VDD, VDD))
    print("T=0, CLK=1 (hold):", tff.update(VSS, VDD))
