#!/usr/bin/env python3
"""
Beautiful 8D Chromatic Visualization with Image Display
Renders matplotlib to images, displays in PIL window (guaranteed to work!)
"""

import re
import numpy as np
import matplotlib
matplotlib.use('Agg')  # Non-interactive backend for rendering
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from collections import deque
import time
import io
from PIL import Image, ImageTk
import tkinter as tk

LOG_FILE = "../logs/peer1.log"

class Monitor:
    def __init__(self):
        self.positions = deque(maxlen=1000)
        self.last_pos = 0
        self.omega = 0
        self.evol = 0
        self.skip = 0

    def update(self):
        try:
            with open(LOG_FILE, 'r', encoding='utf-8', errors='ignore') as f:
                f.seek(self.last_pos)
                lines = f.readlines()
                self.last_pos = f.tell()

                dims = {}
                for line in lines:
                    if 'Ω:' in line and 'Evolution:' in line:
                        try:
                            self.omega = float(line.split('Ω:')[1].split()[0])
                            self.evol = int(line.split('Evolution:')[1].split('│')[0].strip())
                        except:
                            pass

                    for i in range(1, 9):
                        if f'  D{i}:' in line:
                            try:
                                dims[i-1] = float(line.split(':')[1].split('[')[0].strip())
                            except:
                                pass

                    if len(dims) == 8:
                        self.skip += 1
                        if self.skip % 3 == 0:
                            self.positions.append(tuple(dims[i] for i in range(8)))
                        dims = {}
        except:
            pass

def render_frame(monitor, azim):
    """Render beautiful 3D frame to PIL Image"""
    if len(monitor.positions) < 15:
        return None

    # Process data
    arr = np.array(list(monitor.positions), dtype=np.float64)
    arr = np.log10(np.maximum(np.abs(arr), 1e-200))

    # Normalize
    for i in range(8):
        col = arr[:, i]
        vmin, vmax = col.min(), col.max()
        if vmax > vmin:
            arr[:, i] = (col - vmin) / (vmax - vmin)

    # Musical harmony projection
    x = arr[:, 0] * 0.6 + arr[:, 7] * 0.4  # Fundamental + Octave
    y = arr[:, 2] * 0.5 + arr[:, 5] * 0.5  # Thirds + Sixths
    z = arr[:, 4] * 0.5 + arr[:, 3] * 0.5  # Fifths + Fourths

    # Create beautiful figure
    fig = plt.figure(figsize=(14, 10), dpi=100)
    fig.patch.set_facecolor('#050510')
    ax = fig.add_subplot(111, projection='3d')
    ax.set_facecolor('#0a0a1a')

    # Plot with plasma gradient
    colors = plt.cm.plasma(np.linspace(0, 1, len(x)))
    ax.scatter(x, y, z, c=colors, s=6, alpha=0.8, edgecolors='white', linewidths=0.1, depthshade=True)

    # Draw trace lines for depth perception
    if len(x) > 100:
        step = max(1, len(x) // 100)
        ax.plot(x[::step], y[::step], z[::step], 'cyan', alpha=0.15, linewidth=0.5)

    # Styling
    ax.set_xlim(0, 1)
    ax.set_ylim(0, 1)
    ax.set_zlim(0, 1)
    ax.set_xlabel('Fundamental + Octave (D1+D8)', color='cyan', fontsize=11, labelpad=10)
    ax.set_ylabel('Harmony (D3+D6)', color='cyan', fontsize=11, labelpad=10)
    ax.set_zlabel('Resonance (D4+D5)', color='cyan', fontsize=11, labelpad=10)
    ax.tick_params(colors='cyan', labelsize=8)
    ax.grid(True, alpha=0.12, color='cyan', linestyle='--')

    # Beautiful title
    omega_status = "SATURATED" if monitor.omega >= 999 else "GROWING"
    omega_color = '#ff3366' if monitor.omega >= 999 else '#33ff88'
    fig.suptitle(f'8-Dimensional Chromatic Strange Attractor | Musical Harmony Projection',
                fontsize=15, fontweight='bold', color='white', y=0.98)

    title_text = f'Ω = {monitor.omega:.1f} ({omega_status}) | Evolution: {monitor.evol:,} | Trail: {len(monitor.positions)} points | Azimuth: {azim}°'
    ax.text2D(0.5, 0.95, title_text, transform=fig.transFigure,
             ha='center', fontsize=11, color=omega_color,
             bbox=dict(boxstyle='round,pad=0.5', facecolor='black', alpha=0.8, edgecolor=omega_color))

    # Color legend
    legend_text = "D1:Red D2:Orange D3:Yellow D4:Green D5:Cyan D6:Blue D7:Indigo D8:Violet"
    ax.text2D(0.5, 0.02, legend_text, transform=fig.transFigure,
             ha='center', fontsize=9, color='white',
             bbox=dict(boxstyle='round', facecolor='black', alpha=0.7))

    # Set view angle
    ax.view_init(elev=25, azim=azim)

    # Render to image
    buf = io.BytesIO()
    plt.tight_layout(rect=[0, 0.03, 1, 0.96])
    plt.savefig(buf, format='png', facecolor=fig.get_facecolor(), dpi=100)
    plt.close(fig)

    buf.seek(0)
    return Image.open(buf)

class Viewer:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("🌈 8D Chromatic Attractor - Real-Time")
        self.root.configure(bg='black')

        # Full screen canvas
        self.canvas = tk.Canvas(self.root, width=1400, height=1000, bg='black', highlightthickness=0)
        self.canvas.pack()

        self.monitor = Monitor()
        self.azim = 0
        self.frame = 0
        self.running = True

        self.root.protocol("WM_DELETE_WINDOW", self.on_close)

        print("\n" + "="*70)
        print("  🌈 BEAUTIFUL 8D CHROMATIC VISUALIZATION")
        print("  Window will appear in 1 second...")
        print("="*70 + "\n")

        # Start update loop
        self.root.after(1000, self.update)

    def update(self):
        if not self.running:
            return

        try:
            # Update data
            self.monitor.update()

            # Render frame
            img = render_frame(self.monitor, self.azim)

            if img:
                # Display
                photo = ImageTk.PhotoImage(img)
                self.canvas.delete("all")
                self.canvas.create_image(700, 500, image=photo)
                self.canvas.image = photo  # Keep reference

                # Rotate
                self.azim = (self.azim + 2) % 360

                # Progress
                if self.frame % 10 == 0:
                    print(f"▶ Frame {self.frame}: {len(self.monitor.positions)} pts | Ω={self.monitor.omega:.0f} | Az={self.azim}°")
            else:
                # Waiting for data
                if self.frame % 20 == 0:
                    print(f"⏳ Waiting for data... {len(self.monitor.positions)}/15 pts")

            self.frame += 1

            # Schedule next update
            self.root.after(200, self.update)  # 5 FPS

        except Exception as e:
            print(f"Error: {e}")
            self.root.after(200, self.update)

    def on_close(self):
        self.running = False
        self.root.destroy()
        print("\n✅ Visualization closed")

    def run(self):
        print("✅ Window should be visible NOW!")
        print("   Beautiful 3D chromatic attractor rendering...")
        print("   Close window or Ctrl+C to stop\n")
        try:
            self.root.mainloop()
        except KeyboardInterrupt:
            print("\n✅ Stopped by user")

if __name__ == "__main__":
    viewer = Viewer()
    viewer.run()
