import tkinter as tk
from tkinter import ttk, filedialog, messagebox, simpledialog
import threading
import time
import json
import datetime
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import sounddevice as sd
from typing import List, Tuple, Optional
from scipy import signal

# Part 2: Therapy Programs and Journal
class TherapyProgram:
    def __init__(self, name: str, description: str, stages: List[Tuple[int, int]]):
        self.name = name
        self.description = description
        self.stages = stages  # List of (frequency, duration) tuples

def create_default_programs() -> List[TherapyProgram]:
    return [
        TherapyProgram("Relaxation", "A program to help you relax and reduce stress",
                       [(10, 300), (8, 300), (6, 300), (4, 300)]),
        TherapyProgram("Focus", "Enhance your concentration and mental clarity",
                       [(14, 300), (12, 300), (10, 300), (8, 300)]),
        TherapyProgram("Sleep", "Prepare your mind for a restful sleep",
                       [(7, 600), (5, 600), (3, 600)])
    ]

class SessionEntry:
    def __init__(self, date: datetime.datetime, duration: float, program: str, notes: str):
        self.date = date
        self.duration = duration
        self.program = program
        self.notes = notes

class Journal:
    def __init__(self):
        self.entries: List[SessionEntry] = []

    def add_entry(self, entry: SessionEntry):
        self.entries.append(entry)

    def get_entries(self) -> List[SessionEntry]:
        return self.entries

# Part 3: Core Functions
class BinauralBeatsController:
    def __init__(self, server: Server, ax: plt.Axes, canvas: FigureCanvasTkAgg, root: tk.Tk):
        self.server = server
        self.ax = ax
        self.canvas = canvas
        self.root = root  # Store the root window
        self.is_playing = False
        self.left_sine: Optional[Sine] = None
        self.right_sine: Optional[Sine] = None
        self.env: Optional[Sine] = None
        self.setup_tones()

    def setup_tones(self):
        # Parameters for Binaural Beats
        self.base_freq = 200  # Base frequency in Hz
        self.theta_freq = 6   # Theta rhythm frequency in Hz

        if self.server is not None and self.server.getIsBooted():
            # Create Sine wave generators for left and right ears
            self.left_sine = Sine(freq=self.base_freq, mul=0.5)
            self.right_sine = Sine(freq=self.base_freq + self.theta_freq, mul=0.5)

            # Envelope modulation for smooth volume changes
            self.env = Sine(freq=0.1, mul=0.1, add=0.9)
            self.left_sine.mul = self.env
            self.right_sine.mul = self.env
        else:
            print("Server not booted. Cannot create audio objects.")

    def change_frequency(self, base: float, theta: float):
        if self.left_sine is not None and self.right_sine is not None:
            self.left_sine.freq = base
            self.right_sine.freq = base + theta

    def change_volume(self, volume: float):
        if self.env is not None:
            self.env.mul = volume * 0.1
            self.env.add = 0.9

    def start(self):
        if not self.is_playing and self.left_sine is not None and self.right_sine is not None:
            # Output to left and right channels
            self.left_sine.out(0)
            self.right_sine.out(1)
            self.is_playing = True
            self.update_plot()

    def stop(self):
        if self.is_playing and self.left_sine is not None and self.right_sine is not None:
            self.left_sine.stop()
            self.right_sine.stop()
            self.is_playing = False
            self.ax.clear()
            self.canvas.draw()

    def update_plot(self):
        self.ax.clear()
        t = np.linspace(0, 0.1, 1000)  # 100 ms window
        if self.left_sine is not None and self.right_sine is not None:
            freq_left = self.left_sine.freq.get()
            freq_right = self.right_sine.freq.get()
            left_wave = np.sin(2 * np.pi * freq_left * t)
            right_wave = np.sin(2 * np.pi * freq_right * t)
            self.ax.plot(t, left_wave, label='Left', color='blue')
            self.ax.plot(t, right_wave, label='Right', color='red')
            self.ax.set_title('Binaural Beat Waveforms')
            self.ax.set_xlabel('Time (s)')
            self.ax.set_ylabel('Amplitude')
            self.ax.legend()
            self.ax.set_ylim(-1, 1)
        self.canvas.draw()
        if self.is_playing:
            self.root.after(100, self.update_plot)  # Update every 100 ms using root

# Part 5: Tomatis Method
class TomatisMethodController:
    def __init__(self, ax: plt.Axes, canvas: FigureCanvasTkAgg, root: tk.Tk):
        self.ax = ax
        self.canvas = canvas
        self.root = root
        self.is_playing = False
        self.left_freq = 200
        self.right_freq = 800
        self.left_q = 1.0
        self.right_q = 1.0
        self.left_volume = 0.3
        self.right_volume = 0.3
        self.stream = None
        self.sample_rate = 44100

    def create_bandpass_filter(self, freq, q):
        nyq = 0.5 * self.sample_rate
        low = freq / nyq
        high = freq / nyq
        b, a = signal.butter(2, [low, high], btype='band')
        return b, a

    def generate_tomatis_sound(self, t):
        left_noise = np.random.normal(0, 1, len(t))
        right_noise = np.random.normal(0, 1, len(t))
        
        b_left, a_left = self.create_bandpass_filter(self.left_freq, self.left_q)
        b_right, a_right = self.create_bandpass_filter(self.right_freq, self.right_q)
        
        left_filtered = signal.lfilter(b_left, a_left, left_noise)
        right_filtered = signal.lfilter(b_right, a_right, right_noise)
        
        left_output = left_filtered * self.left_volume
        right_output = right_filtered * self.right_volume
        
        return np.column_stack((left_output, right_output))

    def start(self):
        if not self.is_playing:
            def callback(outdata, frames, time, status):
                if status:
                    print(status)
                t = (self.stream.time + np.arange(frames)) / self.sample_rate
                outdata[:] = self.generate_tomatis_sound(t)

            self.stream = sd.OutputStream(
                channels=2, callback=callback, samplerate=self.sample_rate)
            self.stream.start()
            self.is_playing = True
            self.update_plot()

    def stop(self):
        if self.is_playing:
            self.stream.stop()
            self.stream.close()
            self.is_playing = False
            self.ax.clear()
            self.canvas.draw()

    def update_settings(self, left_freq, right_freq, left_q, right_q, left_volume, right_volume):
        self.left_freq = left_freq
        self.right_freq = right_freq
        self.left_q = left_q
        self.right_q = right_q
        self.left_volume = left_volume
        self.right_volume = right_volume

    def update_plot(self):
        self.ax.clear()
        t = np.linspace(0, 0.1, 1000)  # 100 ms window
        sound = self.generate_tomatis_sound(t)
        self.ax.plot(t, sound[:, 0], label='Left', color='blue')
        self.ax.plot(t, sound[:, 1], label='Right', color='red')
        self.ax.set_title('Tomatis Method Waveforms')
        self.ax.set_xlabel('Time (s)')
        self.ax.set_ylabel('Amplitude')
        self.ax.legend()
        self.ax.set_ylim(-1, 1)
        self.canvas.draw()
        if self.is_playing:
            self.root.after(100, self.update_plot)

class Application(tk.Tk):
    def __init__(self, programs: List[TherapyProgram], journal: Journal):
        super().__init__()
        self.programs = programs
        self.journal = journal
        self.title("Advanced Binaural Beats and Tomatis Method Controller")
        self.geometry("1000x700")

        # Initialize Matplotlib Figure
        self.fig, self.ax = plt.subplots(figsize=(6, 4))
        self.canvas = FigureCanvasTkAgg(self.fig, master=self)
        self.canvas_widget = self.canvas.get_tk_widget()

        # Initialize Controllers
        self.binaural_controller = BinauralBeatsController(self.ax, self.canvas, self)
        self.tomatis_controller = TomatisMethodController(self.ax, self.canvas, self)

        # Create notebook for tabs
        notebook = ttk.Notebook(self)
        notebook.pack(fill=tk.BOTH, expand=True)

        # Binaural Beats Tab
        self.create_binaural_tab(notebook)

        # Tomatis Method Tab
        self.create_tomatis_tab(notebook)

        # Session Journal Tab
        self.create_journal_tab(notebook)

        # Settings Tab
        self.create_settings_tab(notebook)

    # ... (предыдущие методы остаются без изменений)

    def create_tomatis_tab(self, notebook: ttk.Notebook):
        tomatis_tab = ttk.Frame(notebook)
        notebook.add(tomatis_tab, text='Tomatis Method')

        tomatis_settings_frame = ttk.LabelFrame(tomatis_tab, text="Tomatis Method Settings")
        tomatis_settings_frame.pack(pady=10, padx=10, fill="both", expand=True)

        # Left Ear Settings
        left_ear_frame = ttk.LabelFrame(tomatis_settings_frame, text="Left Ear Settings")
        left_ear_frame.pack(pady=5, padx=5, fill="x")

        # Filter Frequency
        ttk.Label(left_ear_frame, text="Filter Frequency (Hz):").grid(row=0, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_left_freq_entry = ttk.Entry(left_ear_frame, width=10)
        self.tomatis_left_freq_entry.insert(0, "200")
        self.tomatis_left_freq_entry.grid(row=0, column=1, pady=5, padx=5)

        # Filter Q Factor
        ttk.Label(left_ear_frame, text="Filter Q Factor:").grid(row=1, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_left_q_entry = ttk.Entry(left_ear_frame, width=10)
        self.tomatis_left_q_entry.insert(0, "1.0")
        self.tomatis_left_q_entry.grid(row=1, column=1, pady=5, padx=5)

        # Volume
        ttk.Label(left_ear_frame, text="Volume:").grid(row=2, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_left_volume_scale = ttk.Scale(left_ear_frame, from_=0.0, to=1.0, orient=tk.HORIZONTAL)
        self.tomatis_left_volume_scale.set(0.3)
        self.tomatis_left_volume_scale.grid(row=2, column=1, pady=5, padx=5, sticky="we")

        # Right Ear Settings
        right_ear_frame = ttk.LabelFrame(tomatis_settings_frame, text="Right Ear Settings")
        right_ear_frame.pack(pady=5, padx=5, fill="x")

        # Filter Frequency
        ttk.Label(right_ear_frame, text="Filter Frequency (Hz):").grid(row=0, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_right_freq_entry = ttk.Entry(right_ear_frame, width=10)
        self.tomatis_right_freq_entry.insert(0, "800")
        self.tomatis_right_freq_entry.grid(row=0, column=1, pady=5, padx=5)

        # Filter Q Factor
        ttk.Label(right_ear_frame, text="Filter Q Factor:").grid(row=1, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_right_q_entry = ttk.Entry(right_ear_frame, width=10)
        self.tomatis_right_q_entry.insert(0, "1.0")
        self.tomatis_right_q_entry.grid(row=1, column=1, pady=5, padx=5)

        # Volume
        ttk.Label(right_ear_frame, text="Volume:").grid(row=2, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_right_volume_scale = ttk.Scale(right_ear_frame, from_=0.0, to=1.0, orient=tk.HORIZONTAL)
        self.tomatis_right_volume_scale.set(0.3)
        self.tomatis_right_volume_scale.grid(row=2, column=1, pady=5, padx=5, sticky="we")

        # Control Buttons
        control_frame = ttk.Frame(tomatis_settings_frame)
        control_frame.pack(pady=10)

        ttk.Button(control_frame, text="Update Settings", command=self.update_tomatis_settings).grid(row=0, column=0, padx=5, pady=5)
        ttk.Button(control_frame, text="Start Tomatis", command=self.start_tomatis).grid(row=0, column=1, padx=5, pady=5)
        ttk.Button(control_frame, text="Stop Tomatis", command=self.stop_tomatis).grid(row=0, column=2, padx=5, pady=5)

    def update_tomatis_settings(self):
        try:
            left_freq = float(self.tomatis_left_freq_entry.get())
            right_freq = float(self.tomatis_right_freq_entry.get())
            left_q = float(self.tomatis_left_q_entry.get())
            right_q = float(self.tomatis_right_q_entry.get())
            left_volume = self.tomatis_left_volume_scale.get()
            right_volume = self.tomatis_right_volume_scale.get()
            
            self.tomatis_controller.update_settings(left_freq, right_freq, left_q, right_q, left_volume, right_volume)
            messagebox.showinfo("Success", "Tomatis settings updated successfully.")
        except ValueError as e:
            messagebox.showerror("Invalid Input", f"Invalid input: {e}")

    def start_tomatis(self):
        self.tomatis_controller.start()

    def stop_tomatis(self):
        self.tomatis_controller.stop()

# Part 7: GUI Setup
class Application(tk.Tk):
    def __init__(self, server: Server, programs: List[TherapyProgram], journal: Journal):
        super().__init__()
        self.server = server
        self.programs = programs
        self.journal = journal
        self.title("Advanced Binaural Beats and Tomatis Method Controller")
        self.geometry("1000x700")

        # Initialize Matplotlib Figure
        self.fig, self.ax = plt.subplots(figsize=(6, 4))
        self.canvas = FigureCanvasTkAgg(self.fig, master=self)
        self.canvas_widget = self.canvas.get_tk_widget()

        # Initialize Controllers
        self.binaural_controller = BinauralBeatsController(server, self.ax, self.canvas, self)  # Pass self (root)
        self.tomatis_controller = TomatisMethodController(server)

        # Create notebook for tabs
        notebook = ttk.Notebook(self)
        notebook.pack(fill=tk.BOTH, expand=True)

        # Binaural Beats Tab
        self.create_binaural_tab(notebook)

        # Tomatis Method Tab
        self.create_tomatis_tab(notebook)

        # Session Journal Tab
        self.create_journal_tab(notebook)

        # Settings Tab
        self.create_settings_tab(notebook)

    def create_binaural_tab(self, notebook: ttk.Notebook):
        binaural_tab = ttk.Frame(notebook)
        notebook.add(binaural_tab, text='Binaural Beats')

        # Binaural Controls
        controls_frame = ttk.LabelFrame(binaural_tab, text="Controls")
        controls_frame.pack(pady=10, padx=10, fill="x")

        # Base Frequency
        ttk.Label(controls_frame, text="Base Frequency (Hz):").grid(row=0, column=0, sticky="w", pady=5, padx=5)
        self.base_freq_entry = ttk.Entry(controls_frame, width=10)
        self.base_freq_entry.insert(0, "200")
        self.base_freq_entry.grid(row=0, column=1, pady=5, padx=5)

        # Theta Frequency
        ttk.Label(controls_frame, text="Theta Frequency (Hz):").grid(row=1, column=0, sticky="w", pady=5, padx=5)
        self.theta_freq_entry = ttk.Entry(controls_frame, width=10)
        self.theta_freq_entry.insert(0, "6")
        self.theta_freq_entry.grid(row=1, column=1, pady=5, padx=5)

        # Volume
        ttk.Label(controls_frame, text="Volume:").grid(row=2, column=0, sticky="w", pady=5, padx=5)
        self.volume_scale = ttk.Scale(controls_frame, from_=0.0, to=1.0, orient=tk.HORIZONTAL, command=self.on_binaural_volume_change)
        self.volume_scale.set(0.5)
        self.volume_scale.grid(row=2, column=1, pady=5, padx=5, sticky="we")

        # Update and Control Buttons
        button_frame = ttk.Frame(controls_frame)
        button_frame.grid(row=3, column=0, columnspan=2, pady=10)

        ttk.Button(button_frame, text="Update Parameters", command=self.update_binaural_params).grid(row=0, column=0, padx=5)
        ttk.Button(button_frame, text="Start", command=self.binaural_controller.start).grid(row=0, column=1, padx=5)
        ttk.Button(button_frame, text="Stop", command=self.binaural_controller.stop).grid(row=0, column=2, padx=5)

        # Therapy Programs
        therapy_frame = ttk.LabelFrame(binaural_tab, text="Therapy Programs")
        therapy_frame.pack(pady=10, padx=10, fill="x")

        ttk.Label(therapy_frame, text="Select Program:").grid(row=0, column=0, sticky="w", pady=5, padx=5)
        self.program_dropdown = ttk.Combobox(therapy_frame, values=[p.name for p in self.programs], state="readonly")
        self.program_dropdown.set(self.programs[0].name)
        self.program_dropdown.grid(row=0, column=1, pady=5, padx=5)

        ttk.Button(therapy_frame, text="Run Program", command=self.run_therapy_program).grid(row=0, column=2, pady=5, padx=5)

        # Visualization
        self.canvas_widget.pack(pady=10)  # Matplotlib plot

    def on_binaural_volume_change(self, val: str):
        volume = float(val)
        self.binaural_controller.change_volume(volume)

    def update_binaural_params(self):
        try:
            base = float(self.base_freq_entry.get())
            theta = float(self.theta_freq_entry.get())
            volume = float(self.volume_scale.get())
            self.binaural_controller.change_frequency(base, theta)
            self.binaural_controller.change_volume(volume)
        except ValueError as e:
            messagebox.showerror("Invalid Input", f"Invalid input: {e}")

    def create_tomatis_tab(self, notebook: ttk.Notebook):
        tomatis_tab = ttk.Frame(notebook)
        notebook.add(tomatis_tab, text='Tomatis Method')

        tomatis_settings_frame = ttk.LabelFrame(tomatis_tab, text="Tomatis Method Settings")
        tomatis_settings_frame.pack(pady=10, padx=10, fill="both", expand=True)

        # Left Ear Settings
        left_ear_frame = ttk.LabelFrame(tomatis_settings_frame, text="Left Ear Settings")
        left_ear_frame.pack(pady=5, padx=5, fill="x")

        # Waveform
        ttk.Label(left_ear_frame, text="Waveform:").grid(row=0, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_left_waveform = ttk.Combobox(left_ear_frame, values=["Sine", "Triangle", "Sawtooth", "Square"], state="readonly")
        self.tomatis_left_waveform.set("Sine")
        self.tomatis_left_waveform.grid(row=0, column=1, pady=5, padx=5)

        # Filter Frequency
        ttk.Label(left_ear_frame, text="Filter Frequency (Hz):").grid(row=1, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_left_freq_entry = ttk.Entry(left_ear_frame, width=10)
        self.tomatis_left_freq_entry.insert(0, "200")
        self.tomatis_left_freq_entry.grid(row=1, column=1, pady=5, padx=5)

        # Filter Q Factor
        ttk.Label(left_ear_frame, text="Filter Q Factor:").grid(row=2, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_left_q_entry = ttk.Entry(left_ear_frame, width=10)
        self.tomatis_left_q_entry.insert(0, "1.0")
        self.tomatis_left_q_entry.grid(row=2, column=1, pady=5, padx=5)

        # Volume
        ttk.Label(left_ear_frame, text="Volume:").grid(row=3, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_left_volume_scale = ttk.Scale(left_ear_frame, from_=0.0, to=1.0, orient=tk.HORIZONTAL, command=self.on_tomatis_left_volume_change)
        self.tomatis_left_volume_scale.set(0.3)
        self.tomatis_left_volume_scale.grid(row=3, column=1, pady=5, padx=5, sticky="we")

        # Right Ear Settings
        right_ear_frame = ttk.LabelFrame(tomatis_settings_frame, text="Right Ear Settings")
        right_ear_frame.pack(pady=5, padx=5, fill="x")

        # Waveform
        ttk.Label(right_ear_frame, text="Waveform:").grid(row=0, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_right_waveform = ttk.Combobox(right_ear_frame, values=["Sine", "Triangle", "Sawtooth", "Square"], state="readonly")
        self.tomatis_right_waveform.set("Sine")
        self.tomatis_right_waveform.grid(row=0, column=1, pady=5, padx=5)

        # Filter Frequency
        ttk.Label(right_ear_frame, text="Filter Frequency (Hz):").grid(row=1, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_right_freq_entry = ttk.Entry(right_ear_frame, width=10)
        self.tomatis_right_freq_entry.insert(0, "800")
        self.tomatis_right_freq_entry.grid(row=1, column=1, pady=5, padx=5)

        # Filter Q Factor
        ttk.Label(right_ear_frame, text="Filter Q Factor:").grid(row=2, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_right_q_entry = ttk.Entry(right_ear_frame, width=10)
        self.tomatis_right_q_entry.insert(0, "1.0")
        self.tomatis_right_q_entry.grid(row=2, column=1, pady=5, padx=5)

        # Volume
        ttk.Label(right_ear_frame, text="Volume:").grid(row=3, column=0, sticky="w", pady=5, padx=5)
        self.tomatis_right_volume_scale = ttk.Scale(right_ear_frame, from_=0.0, to=1.0, orient=tk.HORIZONTAL, command=self.on_tomatis_right_volume_change)
        self.tomatis_right_volume_scale.set(0.3)
        self.tomatis_right_volume_scale.grid(row=3, column=1, pady=5, padx=5, sticky="we")

        # Control Buttons
        control_frame = ttk.Frame(tomatis_settings_frame)
        control_frame.pack(pady=10)

        ttk.Button(control_frame, text="Update Settings", command=self.update_tomatis_settings).grid(row=0, column=0, padx=5, pady=5)
        ttk.Button(control_frame, text="Start Tomatis", command=self.start_tomatis).grid(row=0, column=1, padx=5, pady=5)
        ttk.Button(control_frame, text="Stop Tomatis", command=self.stop_tomatis).grid(row=0, column=2, padx=5, pady=5)

    def on_tomatis_left_volume_change(self, val: str):
        volume = float(val)
        # React to left ear volume change
        if self.tomatis_controller.left_sound is not None:
            self.tomatis_controller.left_sound.mul = volume

    def on_tomatis_right_volume_change(self, val: str):
        volume = float(val)
        # React to right ear volume change
        if self.tomatis_controller.right_sound is not None:
            self.tomatis_controller.right_sound.mul = volume

    def run_therapy_program(self):
        program_name = self.program_dropdown.get()
        program = next((p for p in self.programs if p.name == program_name), None)
        if program is None:
            messagebox.showerror("Error", "Selected program not found.")
            return

        def run_program():
            total_duration = 0
            for theta, duration in program.stages:
                self.binaural_controller.change_frequency(self.binaural_controller.base_freq, theta)
                time.sleep(duration)
                total_duration += duration
            # After program ends, reset frequencies and save session
            self.binaural_controller.change_frequency(self.binaural_controller.base_freq, self.binaural_controller.theta_freq)
            # Schedule save_session to be called in the main thread
            self.root.after(0, lambda: self.save_session(total_duration / 60, program.name))  # duration in minutes

        threading.Thread(target=run_program, daemon=True).start()

    def save_session(self, duration: float, program: str):
        notes = simpledialog.askstring("Session Notes", "How do you feel after this session?")
        if notes is None:  # User cancelled the dialog
            notes = ""
        entry = SessionEntry(datetime.datetime.now(), duration, program, notes)
        self.journal.add_entry(entry)

    def create_journal_tab(self, notebook: ttk.Notebook):
        journal_tab = ttk.Frame(notebook)
        notebook.add(journal_tab, text='Session Journal')

        tk.Button(journal_tab, text="View Journal", command=self.view_journal).pack(pady=10)

    def view_journal(self):
        journal_window = tk.Toplevel(self)
        journal_window.title("Session Journal")
        journal_window.geometry("600x500")

        # Create a scrollable frame
        canvas = tk.Canvas(journal_window)
        scrollbar = ttk.Scrollbar(journal_window, orient="vertical", command=canvas.yview)
        scrollable_frame = ttk.Frame(canvas)

        scrollable_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(
                scrollregion=canvas.bbox("all")
            )
        )

        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)

        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

        for entry in self.journal.get_entries():
            entry_frame = ttk.Frame(scrollable_frame, relief=tk.RIDGE, borderwidth=1)
            entry_frame.pack(padx=10, pady=5, fill="x")

            date_label = ttk.Label(entry_frame, text=f"Date: {entry.date.strftime('%Y-%m-%d %H:%M:%S')}", font=("Helvetica", 10, "bold"))
            date_label.pack(anchor="w", padx=5, pady=2)

            program_label = ttk.Label(entry_frame, text=f"Program: {entry.program}", font=("Helvetica", 10))
            program_label.pack(anchor="w", padx=5, pady=2)

            duration_label = ttk.Label(entry_frame, text=f"Duration: {entry.duration:.2f} minutes", font=("Helvetica", 10))
            duration_label.pack(anchor="w", padx=5, pady=2)

            notes_label = ttk.Label(entry_frame, text=f"Notes: {entry.notes}", font=("Helvetica", 10), wraplength=550)
            notes_label.pack(anchor="w", padx=5, pady=2)

            separator = ttk.Separator(scrollable_frame, orient='horizontal')
            separator.pack(fill='x', padx=10, pady=5)

    def create_settings_tab(self, notebook: ttk.Notebook):
        settings_tab = ttk.Frame(notebook)
        notebook.add(settings_tab, text='Settings')

        settings_frame = ttk.Frame(settings_tab)
        settings_frame.pack(pady=20, padx=20)

        ttk.Button(settings_frame, text="Save Settings", command=self.save_settings, width=20).pack(pady=10)
        ttk.Button(settings_frame, text="Load Settings", command=self.load_settings, width=20).pack(pady=10)

    def save_settings(self):
        settings = {
            "binaural_beats": {
                "base_freq": self.base_freq_entry.get(),
                "theta_freq": self.theta_freq_entry.get(),
                "volume": self.volume_scale.get()
            },
            "tomatis_method": {
                "left_ear": {
                    "waveform": self.tomatis_left_waveform.get(),
                    "filter_freq": self.tomatis_left_freq_entry.get(),
                    "filter_q": self.tomatis_left_q_entry.get(),
                    "volume": self.tomatis_left_volume_scale.get()
                },
                "right_ear": {
                    "waveform": self.tomatis_right_waveform.get(),
                    "filter_freq": self.tomatis_right_freq_entry.get(),
                    "filter_q": self.tomatis_right_q_entry.get(),
                    "volume": self.tomatis_right_volume_scale.get()
                }
            }
        }
        file_path = filedialog.asksaveasfilename(defaultextension=".json",
                                                 filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
                                                 title="Save Settings")
        if file_path:
            try:
                with open(file_path, 'w') as f:
                    json.dump(settings, f, indent=4)
                messagebox.showinfo("Success", "Settings saved successfully.")
            except Exception as e:
                messagebox.showerror("Error", f"Failed to save settings: {e}")

    def load_settings(self):
        file_path = filedialog.askopenfilename(defaultextension=".json",
                                               filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
                                               title="Load Settings")
        if file_path:
            try:
                with open(file_path, 'r') as f:
                    settings = json.load(f)
                # Binaural Beats Settings
                binaural = settings.get("binaural_beats", {})
                self.base_freq_entry.delete(0, tk.END)
                self.base_freq_entry.insert(0, binaural.get("base_freq", 200))
                self.theta_freq_entry.delete(0, tk.END)
                self.theta_freq_entry.insert(0, binaural.get("theta_freq", 6))
                self.volume_scale.set(float(binaural.get("volume", 0.5)))
                self.binaural_controller.change_frequency(float(binaural.get("base_freq", 200)), float(binaural.get("theta_freq", 6)))
                self.binaural_controller.change_volume(float(binaural.get("volume", 0.5)))

                # Tomatis Method Settings
                tomatis = settings.get("tomatis_method", {})
                left_ear = tomatis.get("left_ear", {})
                self.tomatis_left_waveform.set(left_ear.get("waveform", "Sine"))
                self.tomatis_left_freq_entry.delete(0, tk.END)
                self.tomatis_left_freq_entry.insert(0, left_ear.get("filter_freq", 200))
                self.tomatis_left_q_entry.delete(0, tk.END)
                self.tomatis_left_q_entry.insert(0, left_ear.get("filter_q", 1.0))
                self.tomatis_left_volume_scale.set(float(left_ear.get("volume", 0.3)))

                right_ear = tomatis.get("right_ear", {})
                self.tomatis_right_waveform.set(right_ear.get("waveform", "Sine"))
                self.tomatis_right_freq_entry.delete(0, tk.END)
                self.tomatis_right_freq_entry.insert(0, right_ear.get("filter_freq", 800))
                self.tomatis_right_q_entry.delete(0, tk.END)
                self.tomatis_right_q_entry.insert(0, right_ear.get("filter_q", 1.0))
                self.tomatis_right_volume_scale.set(float(right_ear.get("volume", 0.3)))

                messagebox.showinfo("Success", "Settings loaded successfully.")
            except Exception as e:
                messagebox.showerror("Error", f"Failed to load settings: {e}")

    def start_tomatis(self):
        settings = {
            "left_ear": {
                "waveform": self.tomatis_left_waveform.get(),
                "filter_freq": self.tomatis_left_freq_entry.get(),
                "filter_q": self.tomatis_left_q_entry.get(),
                "volume": self.tomatis_left_volume_scale.get()
            },
            "right_ear": {
                "waveform": self.tomatis_right_waveform.get(),
                "filter_freq": self.tomatis_right_freq_entry.get(),
                "filter_q": self.tomatis_right_q_entry.get(),
                "volume": self.tomatis_right_volume_scale.get()
            }
        }
        self.tomatis_controller.start(settings)

    def stop_tomatis(self):
        self.tomatis_controller.stop()

    def update_tomatis_settings(self):
        settings = {
            "left_ear": {
                "waveform": self.tomatis_left_waveform.get(),
                "filter_freq": self.tomatis_left_freq_entry.get(),
                "filter_q": self.tomatis_left_q_entry.get(),
                "volume": self.tomatis_left_volume_scale.get()
            },
            "right_ear": {
                "waveform": self.tomatis_right_waveform.get(),
                "filter_freq": self.tomatis_right_freq_entry.get(),
                "filter_q": self.tomatis_right_q_entry.get(),
                "volume": self.tomatis_right_volume_scale.get()
            }
        }
        self.tomatis_controller.update_settings(settings)

# Part 8: Main Loop and Cleanup
# Main function
def main():
    programs = create_default_programs()
    journal = Journal()
    app = Application(programs, journal)
    app.mainloop()

if __name__ == "__main__":
    main() проверь ошибки весь код перепечатай