#!/usr/bin/env python3
"""
IT³ Framework v6.16 — Appendix D Visualization Generator
=========================================================
Generates publication-quality figure for:
- Appendix D: Wave-Particle Duality of the Macroscopic Vacuum
  and the Hydrogen Analogy

Author: Victor Logvinovich
Date: July 2026
"""

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.patches import FancyBboxPatch, Circle, FancyArrowPatch
from matplotlib.gridspec import GridSpec
import warnings
warnings.filterwarnings('ignore')

# ============================================================================
# GLOBAL STYLE CONFIGURATION
# ============================================================================
plt.style.use('dark_background')
plt.rcParams.update({
    'figure.facecolor': '#0a0a12',
    'axes.facecolor': '#0a0a12',
    'axes.edgecolor': '#3a3a4a',
    'axes.labelcolor': '#e0e0e0',
    'xtick.color': '#e0e0e0',
    'ytick.color': '#e0e0e0',
    'text.color': '#e0e0e0',
    'font.family': 'serif',
    'font.size': 11,
    'figure.dpi': 300,
    'savefig.dpi': 300,
    'savefig.facecolor': '#0a0a12',
    'savefig.bbox': 'tight',
})

COLORS = {
    'nucleus': '#FFD700',
    'electron': '#00BFFF',
    'wave_func': '#8A2BE2',
    'bound': '#00FF7F',
    'free': '#FF4500',
    'scattered': '#FF69B4',
    'lattice': '#4169E1',
    'grid': '#2a2a3a',
    'text': '#e0e0e0',
    'highlight': '#FFD700',
}

def plot_wave_particle_duality():
    fig = plt.figure(figsize=(18, 16))
    gs = GridSpec(2, 2, figure=fig, hspace=0.35, wspace=0.3)
    
    # ========== PANEL 1: Scale Invariance ==========
    ax1 = fig.add_subplot(gs[0, 0])
    
    atom_center_x, atom_center_y = 0.25, 0.5
    
    proton = Circle((atom_center_x, atom_center_y), 0.02, facecolor=COLORS['nucleus'], edgecolor='white', linewidth=2, alpha=0.9)
    ax1.add_patch(proton)
    ax1.text(atom_center_x, atom_center_y - 0.04, r'$p^+$', ha='center', va='top', fontsize=11, color=COLORS['nucleus'], fontweight='bold')
    
    electron_shell = Circle((atom_center_x, atom_center_y), 0.15, fill=False, edgecolor=COLORS['electron'], linewidth=2, linestyle='--', alpha=0.7)
    ax1.add_patch(electron_shell)
    
    electron = Circle((atom_center_x + 0.15, atom_center_y), 0.015, facecolor=COLORS['electron'], edgecolor='white', linewidth=1.5, alpha=0.9)
    ax1.add_patch(electron)
    ax1.text(atom_center_x + 0.15, atom_center_y + 0.025, r'$e^-$', ha='center', va='bottom', fontsize=11, color=COLORS['electron'], fontweight='bold')
    
    ax1.text(atom_center_x, 0.85, 'HYDROGEN ATOM', ha='center', va='center', fontsize=12, fontweight='bold', color=COLORS['electron'])
    ax1.text(atom_center_x, 0.78, r'$a_0 = 5.29 \times 10^{-11}$ m', ha='center', va='center', fontsize=10, color=COLORS['text'])
    ax1.text(atom_center_x, 0.73, r'$r_p = 0.84 \times 10^{-15}$ m', ha='center', va='center', fontsize=10, color=COLORS['text'])
    
    ax1.annotate('', xy=(atom_center_x + 0.18, atom_center_y + 0.08), xytext=(atom_center_x, atom_center_y + 0.08), arrowprops=dict(arrowstyle='<->', color=COLORS['highlight'], lw=2))
    ax1.text(atom_center_x + 0.09, atom_center_y + 0.10, r'$a_0/r_p \approx 6.3 \times 10^4$', ha='center', va='bottom', fontsize=11, color=COLORS['highlight'], fontweight='bold')
    
    ss_center_x, ss_center_y = 0.75, 0.5
    
    sun = Circle((ss_center_x, ss_center_y), 0.02, facecolor=COLORS['nucleus'], edgecolor='white', linewidth=2, alpha=0.9)
    ax1.add_patch(sun)
    ax1.text(ss_center_x, ss_center_y - 0.04, r'$\odot$', ha='center', va='top', fontsize=14, color=COLORS['nucleus'], fontweight='bold')
    
    kuiper_belt = Circle((ss_center_x, ss_center_y), 0.15, fill=False, edgecolor=COLORS['bound'], linewidth=2, linestyle='--', alpha=0.7)
    ax1.add_patch(kuiper_belt)
    
    pluto = Circle((ss_center_x + 0.15, ss_center_y), 0.012, facecolor=COLORS['bound'], edgecolor='white', linewidth=1.5, alpha=0.9)
    ax1.add_patch(pluto)
    ax1.text(ss_center_x + 0.15, ss_center_y + 0.025, 'Pluto', ha='center', va='bottom', fontsize=9, color=COLORS['bound'], fontweight='bold')
    
    ax1.text(ss_center_x, 0.85, 'MACROSCOPIC ATOM', ha='center', va='center', fontsize=12, fontweight='bold', color=COLORS['bound'])
    ax1.text(ss_center_x, 0.78, r'$R_{KB} = 46.77$ AU', ha='center', va='center', fontsize=10, color=COLORS['text'])
    ax1.text(ss_center_x, 0.73, r'$R_\odot = 6.96 \times 10^5$ km', ha='center', va='center', fontsize=10, color=COLORS['text'])
    
    ax1.annotate('', xy=(ss_center_x + 0.18, ss_center_y + 0.08), xytext=(ss_center_x, ss_center_y + 0.08), arrowprops=dict(arrowstyle='<->', color=COLORS['highlight'], lw=2))
    ax1.text(ss_center_x + 0.09, ss_center_y + 0.10, r'$R_{KB}/R_\odot \approx 1.0 \times 10^4$', ha='center', va='bottom', fontsize=11, color=COLORS['highlight'], fontweight='bold')
    
    ax1.text(0.5, 0.15, 'SCALE INVARIANCE ACROSS 40+ ORDERS OF MAGNITUDE', ha='center', va='center', fontsize=11, fontweight='bold', color=COLORS['highlight'], bbox=dict(boxstyle='round', facecolor='#1a1a2e', edgecolor=COLORS['highlight'], alpha=0.8))
    ax1.text(0.5, 0.08, 'Factor ~6.3 difference reflects different packing efficiencies\nof quantum electromagnetic vs. gravitational bound systems', ha='center', va='center', fontsize=9, color=COLORS['text'], style='italic')
    
    ax1.set_xlim(0, 1); ax1.set_ylim(0, 1); ax1.axis('off')
    ax1.set_title('Scale Invariance: Hydrogen Atom ↔ Solar System', fontsize=14, fontweight='bold', pad=15)
    
    # ========== PANEL 2: Fragmented Wave Function ==========
    ax2 = fig.add_subplot(gs[0, 1])
    sun_center = Circle((0.5, 0.5), 0.05, facecolor=COLORS['nucleus'], edgecolor='white', linewidth=2, alpha=0.9)
    ax2.add_patch(sun_center)
    ax2.text(0.5, 0.5, r'$\odot$', ha='center', va='center', fontsize=18, color='#0a0a12', fontweight='bold')
    
    for n in range(1, 8):
        r_display = 0.1 + 0.08 * n
        alpha = 0.3 + 0.1 * (7 - n)
        ring = Circle((0.5, 0.5), r_display, fill=False, edgecolor=COLORS['wave_func'], linewidth=1.5, alpha=alpha, linestyle='--')
        ax2.add_patch(ring)
        if n == 2:
            ring.set_linewidth(3)
            ring.set_alpha(0.9)
            ring.set_edgecolor(COLORS['bound'])
            ax2.text(0.5 + r_display + 0.02, 0.5, r'$S_n=2$' + '\n(Valence)', fontsize=10, color=COLORS['bound'], fontweight='bold', va='center')
    
    np.random.seed(42)
    n_objects_s2 = 500
    r_s2 = 0.1 + 0.08 * 2
    theta_s2 = np.random.uniform(0, 2*np.pi, n_objects_s2)
    r_jitter_s2 = np.random.normal(0, 0.015, n_objects_s2)
    ax2.scatter(0.5 + (r_s2 + r_jitter_s2) * np.cos(theta_s2), 0.5 + (r_s2 + r_jitter_s2) * np.sin(theta_s2), c=COLORS['bound'], s=8, alpha=0.7, edgecolors='white', linewidth=0.3)
    
    for n in [1, 3, 4, 5, 6]:
        n_objects = int(500 * 0.05)
        r_n = 0.1 + 0.08 * n
        theta_n = np.random.uniform(0, 2*np.pi, n_objects)
        r_jitter_n = np.random.normal(0, 0.02, n_objects)
        ax2.scatter(0.5 + (r_n + r_jitter_n) * np.cos(theta_n), 0.5 + (r_n + r_jitter_n) * np.sin(theta_n), c=COLORS['wave_func'], s=6, alpha=0.5, edgecolors='white', linewidth=0.2)
    
    ax2.text(0.5, 0.05, r'1.5M objects = fragmented macroscopic wave function $|\psi|^2$' + '\n' + r'$S_n=2$ captures 71.46% of outer system (Bohr radius analogue)', ha='center', va='center', fontsize=10, color=COLORS['text'], bbox=dict(boxstyle='round', facecolor='#1a1a2e', edgecolor=COLORS['wave_func'], alpha=0.8))
    ax2.set_xlim(0, 1); ax2.set_ylim(0, 1); ax2.axis('off')
    ax2.set_title('Delocalized Macroscopic Wave Function', fontsize=14, fontweight='bold', pad=15)
    
    # ========== PANEL 3: Bound vs Free States ==========
    ax3 = fig.add_subplot(gs[1, 0])
    np.random.seed(123)
    e_bound = np.random.beta(2, 5, 10000)
    e_free = 1 + np.random.exponential(0.3, 500)
    
    bins = np.linspace(0, 2, 50)
    ax3.hist(e_bound, bins=bins, alpha=0.7, color=COLORS['bound'], label=r'Bound States ($e < 1$)' + '\n' + r'Quantized on $S_n$ floors', density=True)
    ax3.hist(e_free, bins=bins, alpha=0.7, color=COLORS['free'], label=r'Free States ($e \geq 1$)' + '\nContinuous spectrum', density=True)
    
    ax3.axvline(x=1.0, color=COLORS['highlight'], linestyle='--', linewidth=3, alpha=0.9)
    ax3.text(1.02, 0.9, 'IONIZATION\nLIMIT', fontsize=10, color=COLORS['highlight'], fontweight='bold', va='top')
    
    ax3.text(0.3, 0.85, 'DISCRETE\nSPECTRUM', fontsize=11, ha='center', va='center', color=COLORS['bound'], fontweight='bold', bbox=dict(boxstyle='round', facecolor='#1a1a2e', edgecolor=COLORS['bound'], alpha=0.8))
    ax3.text(1.5, 0.85, 'CONTINUOUS\nSPECTRUM', fontsize=11, ha='center', va='center', color=COLORS['free'], fontweight='bold', bbox=dict(boxstyle='round', facecolor='#1a1a2e', edgecolor=COLORS['free'], alpha=0.8))
    
    ax3.set_xlabel('Orbital Eccentricity (e)', fontsize=12)
    ax3.set_ylabel('Density', fontsize=12)
    ax3.set_title('Bound vs Free States: The Ionization Limit', fontsize=14, fontweight='bold', pad=15)
    ax3.legend(fontsize=10, loc='upper right', facecolor='#1a1a2e', edgecolor='#3a3a4a')
    ax3.grid(True, alpha=0.2, color=COLORS['grid'])
    ax3.set_xlim(0, 2)
    
    # ========== PANEL 4: Macroscopic Rutherford Scattering ==========
    ax4 = fig.add_subplot(gs[1, 1])
    lattice_radius = 0.35
    for i in range(12):
        angle = i * 30 * np.pi / 180
        ax4.add_patch(Circle((0.5 + lattice_radius * np.cos(angle), 0.5 + lattice_radius * np.sin(angle)), 0.02, facecolor=COLORS['lattice'], edgecolor='white', linewidth=1.5, alpha=0.6))
    
    ax4.add_patch(Circle((0.5, 0.5), 0.04, facecolor=COLORS['nucleus'], edgecolor='white', linewidth=2, alpha=0.9))
    ax4.text(0.5, 0.5, r'$\odot$', ha='center', va='center', fontsize=16, color='#0a0a12', fontweight='bold')
    
    np.random.seed(456)
    for i in range(5):
        theta_in = np.random.uniform(0, 2*np.pi)
        impact_param = np.random.uniform(0.1, 0.3)
        t = np.linspace(-3, 3, 100)
        x_traj = 0.5 + impact_param * np.cosh(t) * np.cos(theta_in) - 0.2 * np.sinh(t) * np.sin(theta_in)
        y_traj = 0.5 + impact_param * np.cosh(t) * np.sin(theta_in) + 0.2 * np.sinh(t) * np.cos(theta_in)
        mask = (x_traj > 0) & (x_traj < 1) & (y_traj > 0) & (y_traj < 1)
        ax4.plot(x_traj[mask], y_traj[mask], color=COLORS['free'], linewidth=2, alpha=0.7)
        if mask.any():
            ax4.plot(x_traj[mask][0], y_traj[mask][0], 'o', color=COLORS['free'], markersize=6, alpha=0.9)
            ax4.plot(x_traj[mask][-1], y_traj[mask][-1], 'o', color=COLORS['free'], markersize=6, alpha=0.9)
    
    for i in range(8):
        angle = np.random.uniform(0, 2*np.pi)
        r = np.random.uniform(0.25, 0.4)
        ax4.plot(0.5 + r * np.cos(angle), 0.5 + r * np.sin(angle), 's', color=COLORS['bound'], markersize=8, alpha=0.8, markeredgecolor='white', markeredgewidth=1)
    
    ax4.text(0.5, 0.05, r'Hyperbolic comets ($e \geq 1$) scatter off topological lattice' + '\nanalogous to Rutherford scattering of free electrons', ha='center', va='center', fontsize=10, color=COLORS['text'], bbox=dict(boxstyle='round', facecolor='#1a1a2e', edgecolor=COLORS['free'], alpha=0.8))
    
    ax4.set_xlim(0, 1); ax4.set_ylim(0, 1); ax4.axis('off')
    ax4.set_title('Macroscopic Rutherford Scattering', fontsize=14, fontweight='bold', pad=15)
    
    fig.suptitle('Appendix D: Wave-Particle Duality of the Macroscopic Vacuum\nThe Hydrogen Analogy and Scale-Invariant Topology', fontsize=18, fontweight='bold', color='white', y=0.98)
    
    plt.savefig('appendix_d_wave_particle_duality.jpg', dpi=300, bbox_inches='tight', facecolor='#0a0a12')
    plt.close()
    print("[✓] Saved: appendix_d_wave_particle_duality.jpg")

if __name__ == "__main__":
    plot_wave_particle_duality()