import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

class FractalLiveHDGL_VM:
    def __init__(self):
        # D registers
        self.D = np.array([1.,2.,3.,4.,5.,6.,7.,8.,1.], dtype=float)
        self.P = np.zeros(8)
        self.P[4:] = [6.8541019662, 11.09016994, 17.94427191, 29.03444654]
        self.void = 0.0
        self.omega = 1.0
        self.upper_phi_phi = 2.6180339887
        self.phi = 1.6180339887
        self.weights = np.ones(8)/8

        # Fractal recursion parameters
        self.max_depth = 3
        self.blend_base = 0.05

        # Program
        self.program = [0,1,2,3,1,0,2,3,1,0]
        self.ip = 0

        # History for plotting
        self.history_D = []
        self.history_void = []
        self.history_omega = []

    # ---------------------------
    # Harmonic superposition
    # ---------------------------
    def harmonic_superpose(self, blend_factor=0.05):
        D_prev = self.D.copy()
        weighted_sum = np.sum(D_prev[:8] * self.weights)
        for i in range(len(self.D)):
            p_val = self.P[i] if i < len(self.P) else 0
            phi_val = self.phi ** i
            self.D[i] = D_prev[i] + blend_factor * (phi_val*D_prev[i] + self.upper_phi_phi*p_val + weighted_sum + self.omega)
        self.void += np.mean(self.D) * blend_factor
        self.omega += 0.01 * blend_factor

    # ---------------------------
    # Fractal recursive execution
    # ---------------------------
    def fractal_step(self, depth=0, decay=1.0):
        blend_factor = self.blend_base * (decay ** depth)
        self.harmonic_superpose(blend_factor)

        # Record history
        self.history_D.append(self.D.copy())
        self.history_void.append(self.void)
        self.history_omega.append(self.omega)

        if depth < self.max_depth:
            # Fibonacci sub-steps for recursion
            fib_sub = [1,1,2,3,5,8,13,21,34]
            num_sub = int(fib_sub[depth % len(fib_sub)] * 0.5)
            for _ in range(num_sub):
                self.fractal_step(depth=depth+1, decay=decay*0.5)

    # ---------------------------
    # Single program step
    # ---------------------------
    def step(self):
        if self.ip >= len(self.program):
            return False
        instr = self.program[self.ip]
        self.fractal_step()
        self.ip += 1
        return True

    # ---------------------------
    # Live animation
    # ---------------------------
    def run_live(self):
        fig, (ax1, ax2) = plt.subplots(2,1, figsize=(10,6))
        ax1.set_title("D Registers Evolution")
        ax1.set_xlabel("Step")
        ax1.set_ylabel("D Value")
        ax2.set_title("Void & Omega")
        ax2.set_xlabel("Step")
        ax2.set_ylabel("Value")

        D_lines = [ax1.plot([],[], label=f"D{i+1}")[0] for i in range(len(self.D))]
        void_line, = ax2.plot([], [], label="Void")
        omega_line, = ax2.plot([], [], label="Omega")
        ax1.legend()
        ax2.legend()

        def update(frame):
            if not self.step():
                return D_lines + [void_line, omega_line]

            for i, line in enumerate(D_lines):
                line.set_data(range(len(self.history_D)), [h[i] for h in self.history_D])
            void_line.set_data(range(len(self.history_void)), self.history_void)
            omega_line.set_data(range(len(self.history_omega)), self.history_omega)

            ax1.relim(); ax1.autoscale_view()
            ax2.relim(); ax2.autoscale_view()
            return D_lines + [void_line, omega_line]

        anim = FuncAnimation(fig, update, frames=500, interval=50, blit=False)
        plt.show()


# ---------------------------
# Run example
# ---------------------------
if __name__ == "__main__":
    vm = FractalLiveHDGL_VM()
    vm.run_live()
