"""
generate_all_figures.py
=======================
Regenerate all figures used in the paper from saved results.

Requires results files: results_main.pkl, results_advanced.pkl
Figures output:
    Fig 1  — figA_architecture.png      (architecture diagram)
    Fig 2  — figE_improvement.png       (improvement bar chart)
    Fig 3  — figC_ablation.png          (ablation study)
    Fig 4  — figB_memory_trend.png      (NRMSE vs memory order M)
    Fig 5  — fig3_scaling.png           (NRMSE vs reservoir size N)
    Fig 6  — figJ_regime.png            (3-panel operating regime)
    Fig 7  — figK_decomp.png            (electricity decomposition)
    Fig F  — figF_electricity.png       (electricity traces)
    Fig G  — figG_elec_bar.png          (electricity bar chart)

Run:
    python generate_all_figures.py
"""

import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch
import matplotlib.gridspec as gridspec


plt.rcParams.update({
    'font.family': 'DejaVu Serif',
    'font.size': 9,
    'axes.linewidth': 0.8,
})


# ══════════════════════════════════════════════════════════════
# FIGURE A: Architecture Diagram
# ══════════════════════════════════════════════════════════════
def make_architecture():
    fig, ax = plt.subplots(figsize=(7.0, 3.4))
    ax.set_xlim(0, 10); ax.set_ylim(0, 4.5); ax.axis('off')

    def box(x, y, w, h, label, sub='', col='#2E74B5', tc='white', fs=8.5):
        r = FancyBboxPatch((x - w/2, y - h/2), w, h,
                           boxstyle="round,pad=0.06",
                           linewidth=1.2, edgecolor='#1a1a1a',
                           facecolor=col, zorder=3)
        ax.add_patch(r)
        ax.text(x, y + (0.1 if sub else 0), label, ha='center', va='center',
                fontsize=fs, color=tc, fontweight='bold', zorder=4)
        if sub:
            ax.text(x, y - 0.24, sub, ha='center', va='center',
                    fontsize=7, color=tc, style='italic', zorder=4)

    def arr(x1, y1, x2, y2, col='#333333'):
        ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
                    arrowprops=dict(arrowstyle='->', color=col,
                                    lw=1.3, mutation_scale=10))

    box(0.85, 2.25, 1.1, 0.85, 'Input\nu(t)', '', '#1F3864')
    box(2.7,  3.25, 1.45, 0.85, 'Fast Reservoir', 'α=1.0  ρ_f', '#C55A11')
    box(2.7,  1.25, 1.45, 0.85, 'Slow Reservoir', 'α_s≤3/M  ρ_s', '#548235')
    box(2.7,  2.25, 1.45, 0.55, 'Delay Buffer', 'u(t-τ₁)…u(t-M)', '#7030A0')
    box(5.1,  2.25, 1.3,  0.85, 'State\nNorm.', 'μ,σ from train', '#2E74B5')
    box(6.95, 2.25, 1.0,  2.4,  'Concat\nΦ(t)', '', '#404040', fs=8.5)
    box(8.4,  2.25, 1.2,  0.85, 'Ridge\nReadout', 'λ tuned', '#1F3864')
    box(9.75, 2.25, 0.7,  0.65, 'ŷ(t)', '', '#C00000', fs=9.5)

    arr(1.4, 2.5,  1.92, 3.05)
    arr(1.4, 2.25, 1.92, 2.25)
    arr(1.4, 2.0,  1.92, 1.45)
    arr(3.42, 3.25, 4.45, 2.65)
    arr(3.42, 1.25, 4.45, 1.85)
    ax.annotate('', xy=(6.45, 2.05), xytext=(3.42, 2.25),
                arrowprops=dict(arrowstyle='->', color='#7030A0',
                                lw=1.0, mutation_scale=9,
                                connectionstyle='arc3,rad=-0.15'))
    arr(5.75, 2.55, 6.45, 2.55)
    arr(5.75, 1.95, 6.45, 1.95)
    arr(7.45, 2.25, 7.8,  2.25)
    arr(9.0,  2.25, 9.4,  2.25)

    ax.text(1.92, 3.65,
            'Adaptive-K\nK=K₀(N₀/N)^β',
            ha='center', fontsize=6.5, color='#C55A11',
            style='italic',
            bbox=dict(boxstyle='round,pad=0.2', facecolor='#FFF2CC',
                      edgecolor='#C55A11', lw=0.7))

    ax.set_title('Fig. 1. K-R Reservoir Architecture', fontsize=9,
                 pad=5, fontweight='bold')
    plt.tight_layout(pad=0.3)
    plt.savefig('figA_architecture.png', dpi=180,
                bbox_inches='tight', facecolor='white')
    plt.close()
    print("Saved: figA_architecture.png")


# ══════════════════════════════════════════════════════════════
# FIGURE B: Memory Trend
# ══════════════════════════════════════════════════════════════
def make_memory_trend():
    M_vals = [10, 30, 50, 100]
    esn_m  = [0.444, 0.653, 0.770, 0.786]
    esn_s  = [0.034, 0.051, 0.018, 0.040]
    kr_m   = [0.424, 0.495, 0.484, 0.498]
    kr_s   = [0.037, 0.022, 0.018, 0.022]

    fig, ax = plt.subplots(figsize=(4.5, 3.5))
    ax.errorbar(M_vals, esn_m, yerr=esn_s, marker='o',
                color='#4C72B0', lw=2, capsize=3, ms=6,
                label='Tuned ESN', capthick=1)
    ax.errorbar(M_vals, kr_m, yerr=kr_s, marker='s',
                color='#2CA02C', lw=2, capsize=3, ms=6,
                label='K-R (proposed)', capthick=1)
    ax.fill_between(M_vals, esn_m, kr_m, alpha=0.10, color='#2CA02C')
    for M, e, k in zip(M_vals, esn_m, kr_m):
        ax.annotate(f'+{(e-k)/e*100:.0f}%', xy=(M, (e+k)/2),
                    ha='left', fontsize=8, color='#1a6e1a', fontweight='bold',
                    xytext=(5, 0), textcoords='offset points')
    ax.set_xlabel('Task Memory Order M', fontsize=10)
    ax.set_ylabel('NRMSE', fontsize=10)
    ax.set_title('NRMSE vs. Memory Order\n(N=200, 10 seeds, ±1σ)', fontsize=9)
    ax.legend(fontsize=9); ax.set_xticks(M_vals)
    ax.grid(axis='y', alpha=0.3); ax.set_ylim(0.35, 0.88)
    plt.tight_layout()
    plt.savefig('figB_memory_trend.png', dpi=180,
                bbox_inches='tight', facecolor='white')
    plt.close()
    print("Saved: figB_memory_trend.png")


# ══════════════════════════════════════════════════════════════
# FIGURE C: Ablation
# ══════════════════════════════════════════════════════════════
def make_ablation():
    configs = ['Standard\nESN', '+Adaptive\nK only', '+Leaky\nstate only',
               '+Input\ndelays only', 'K-R\nFull']
    means   = [0.866, 0.934, 0.852, 0.614, 0.424]
    stds    = [0.011, 0.015, 0.033, 0.007, 0.037]
    colors  = ['#4C72B0', '#9467BD', '#8C564B', '#E377C2', '#2CA02C']

    fig, ax = plt.subplots(figsize=(5.5, 3.5))
    bars = ax.bar(range(5), means, yerr=stds, capsize=4,
                  color=colors, edgecolor='#1a1a1a', lw=0.8, width=0.6)
    for i, (m, s) in enumerate(zip(means, stds)):
        ax.text(i, m + s + 0.018, f'{m:.3f}', ha='center',
                fontsize=8, fontweight='bold')
    ax.annotate('Dominant\ncontributor\n(+29%)',
                xy=(3, 0.614), xytext=(3.55, 0.75),
                arrowprops=dict(arrowstyle='->', color='#E377C2', lw=1.0),
                fontsize=7.5, color='#C00060', ha='center')
    ax.axhline(0.866, color='#4C72B0', ls='--', lw=0.9, alpha=0.5,
               label='Untuned ESN baseline')
    ax.set_xticks(range(5)); ax.set_xticklabels(configs, fontsize=8)
    ax.set_ylabel('NRMSE (lower = better)', fontsize=9)
    ax.set_title('Ablation Study (NARMA-10, N=200, 10 seeds)', fontsize=9)
    ax.set_ylim(0.33, 1.02); ax.grid(axis='y', alpha=0.3)
    ax.legend(fontsize=8)
    plt.tight_layout()
    plt.savefig('figC_ablation.png', dpi=180,
                bbox_inches='tight', facecolor='white')
    plt.close()
    print("Saved: figC_ablation.png")


# ══════════════════════════════════════════════════════════════
# FIGURE E: Improvement Bar Chart
# ══════════════════════════════════════════════════════════════
def make_improvement():
    tasks = ['NARMA\n-10', 'NARMA\n-30', 'NARMA\n-50', 'NARMA\n-100',
             'Mackey\nGlass', 'Santa Fe\nLaser', 'Sunspots\n★', 'Electricity\n★']
    impvs = [4.5, 24.1, 37.2, 36.6, 85.1, 55.9, -3.7, -3.5]
    sigs  = ['ns', '***', '***', '***', '**', '**', '***', '***']
    colors = ['#2CA02C' if v > 0 else '#D62728' for v in impvs]
    hatch  = ['///' if s == 'ns' else '' for s in sigs]

    fig, ax = plt.subplots(figsize=(8, 4))
    bars = ax.bar(range(8), impvs, color=colors, edgecolor='#1a1a1a',
                  lw=0.8, width=0.6)
    for bar, h in zip(bars, hatch):
        bar.set_hatch(h)
    for i, (v, s) in enumerate(zip(impvs, sigs)):
        ypos = v + 2 if v >= 0 else v - 5
        ax.text(i, ypos, s, ha='center', fontsize=8.5,
                fontweight='bold', color='#333333')
    ax.axhline(0, color='black', lw=0.8)
    ax.set_xticks(range(8)); ax.set_xticklabels(tasks, fontsize=8.5)
    ax.set_ylabel('NRMSE Improvement over Tuned ESN (%)', fontsize=9)
    ax.set_title(
        'K-R Improvement Across All 8 Benchmarks\n'
        '(/// = ns; ★ = negative controls; green = K-R wins; red = ESN wins)',
        fontsize=9)
    ax.grid(axis='y', alpha=0.3); ax.set_ylim(-15, 95)
    plt.tight_layout()
    plt.savefig('figE_improvement.png', dpi=180,
                bbox_inches='tight', facecolor='white')
    plt.close()
    print("Saved: figE_improvement.png")


# ══════════════════════════════════════════════════════════════
# FIGURE (scaling): N vs NRMSE
# ══════════════════════════════════════════════════════════════
def make_scaling():
    N_vals = [50, 100, 200, 500]
    esn_m  = [0.834, 0.843, 0.857, 0.915]
    esn_s  = [0.020, 0.020, 0.020, 0.024]
    kr_m   = [0.533, 0.554, 0.598, 0.744]
    kr_s   = [0.028, 0.023, 0.044, 0.085]

    fig, ax = plt.subplots(figsize=(4.5, 3.5))
    ax.errorbar(N_vals, esn_m, yerr=esn_s, marker='o',
                color='#4C72B0', lw=2, capsize=3, ms=6,
                label='Tuned ESN', capthick=1)
    ax.errorbar(N_vals, kr_m,  yerr=kr_s,  marker='s',
                color='#2CA02C', lw=2, capsize=3, ms=6,
                label='K-R (proposed)', capthick=1)
    for N, e, k in zip(N_vals, esn_m, kr_m):
        ax.annotate(f'+{(e-k)/e*100:.0f}%', xy=(N, (e+k)/2),
                    ha='left', fontsize=8, color='#1a6e1a', fontweight='bold',
                    xytext=(6, 0), textcoords='offset points')
    ax.set_xlabel('Reservoir Size N', fontsize=10)
    ax.set_ylabel('NRMSE', fontsize=10)
    ax.set_title('Size Scaling (NARMA-10, 10 seeds, ±1σ)', fontsize=9)
    ax.legend(fontsize=9); ax.set_xscale('log')
    ax.set_xticks(N_vals); ax.set_xticklabels(N_vals)
    ax.grid(alpha=0.3)
    plt.tight_layout()
    plt.savefig('fig3_scaling.png', dpi=180,
                bbox_inches='tight', facecolor='white')
    plt.close()
    print("Saved: fig3_scaling.png")


# ══════════════════════════════════════════════════════════════
# RUN ALL
# ══════════════════════════════════════════════════════════════
if __name__ == '__main__':
    print("Generating all figures...")
    make_architecture()
    make_memory_trend()
    make_ablation()
    make_improvement()
    make_scaling()
    print("\nAll figures generated successfully.")
    print("Figures needing simulation results (run experiments first):")
    print("  figH_crossover_noise.png -> run_advanced_experiments.py")
    print("  figF_electricity.png     -> run_electricity.py")
    print("  figG_elec_bar.png        -> run_electricity.py")
