import numpy as np
import matplotlib.pyplot as plt

class HDGL_VM:
    def __init__(self, blend_factor=0.05):
        # Core state
        self.D = np.array([1.,2.,3.,4.,5.,6.,7.,8.,1.])
        self.P = np.zeros(8); self.P[4:] = [6.8541, 11.0901, 17.9442, 29.0344]
        self.void = 0.0; self.omega = 1.0
        self.phi_phi = 2.6180339887; self.phi = 1.6180339887
        self.weights = np.ones(8)/8; self.ip = 0
        self.blend_factor = blend_factor

        # History
        self.history_D = []; self.history_void = []; self.history_omega = []

        # Filesystem
        self.fs = {}; self.init_filesystem()

        # Plotting
        self.fig, (self.ax1, self.ax2) = plt.subplots(2,1, figsize=(10,6))
        self.lines_D = [self.ax1.plot([],[],label=f"D{i+1}")[0] for i in range(len(self.D))]
        self.line_void, = self.ax2.plot([],[],label="Void")
        self.line_omega, = self.ax2.plot([],[],label="Omega")
        self.ax1.set_title("D Registers"); self.ax2.set_title("Void & Omega")
        self.ax1.set_xlabel("Step"); self.ax2.set_xlabel("Step")
        self.ax1.set_ylabel("Value"); self.ax2.set_ylabel("Value")
        self.ax1.legend(); self.ax2.legend()
        plt.ion(); plt.show()

    # -------------------------
    # Step & Run
    # -------------------------
    def step(self):
        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] + self.blend_factor*(phi_val*D_prev[i] + self.phi_phi*p_val + weighted_sum + self.omega)
        fractal_factor = np.mean(self.D)*0.1
        self.D += fractal_factor*np.sin(np.arange(len(self.D))*self.phi)
        self.void += np.mean(self.D)*self.blend_factor
        self.omega += 0.01*self.blend_factor
        self.ip += 1
        self.history_D.append(self.D.copy())
        self.history_void.append(self.void)
        self.history_omega.append(self.omega)
        self.update_plot()

    def run(self, steps=None):
        count=0
        while steps is None or count<steps:
            self.step()
            count+=1

    # -------------------------
    # VM Bootloader
    # -------------------------
    def boot(self):
        grub_cfg = self.fs.get('/boot', {}).get('grub.cfg')
        if not grub_cfg: return self.run(steps=10)
        menu = grub_cfg['menu']
        while True:
            self.print("HDGL VM Bootloader:")
            for idx, entry in enumerate(menu,1): self.print(f"{idx}) {entry[0]}")
            choice = self.input("Select option: ").strip()
            if choice.isdigit() and 1 <= int(choice) <= len(menu):
                cmd = menu[int(choice)-1][1]
                break
            self.print("Invalid choice, try again...")
        if cmd=='run_standard_kernel': self.run(steps=10)
        elif cmd=='run_fractal_kernel': [self.step() for _ in range(10)]
        elif cmd=='run_python_shell': self.python_shell()

    # -------------------------
    # REPL (VM-managed)
    # -------------------------
    def repl(self):
        self.print("HDGL VM REPL. Type 'help'.")
        while True:
            cmd = self.input("HDGL> ").strip()
            if not cmd: continue
            cmd_lower = cmd.lower()
            if cmd_lower in ['quit','exit']: break
            elif cmd_lower=='help':
                self.print("Commands: step [n], run [n], state, reset, python, fs <cmd>, help, exit")
            elif cmd_lower.startswith('step'):
                n=int(cmd.split()[1]) if len(cmd.split())>1 else 1
                [self.step() for _ in range(n)]; self.show_state()
            elif cmd_lower.startswith('run'):
                parts=cmd.split(); n=int(parts[1]) if len(parts)>1 else None
                self.run(steps=n); self.show_state()
            elif cmd_lower=='state': self.show_state()
            elif cmd_lower=='reset': self.__init__(blend_factor=self.blend_factor); self.print("VM reset.")
            elif cmd_lower=='python': self.python_shell()
            elif cmd_lower.startswith('fs'): self.fs_command(cmd)
            else: self.print("Unknown command. Type 'help'.")

    # -------------------------
    # Fully VM-safe Python sandbox
    # -------------------------
    def python_shell(self):
        self.print("Entering VM-safe Python sandbox. Type 'exit' to quit.")
        local_ns = {
            'D': self.D,
            'VOID': self.void,
            'OMEGA': self.omega,
            'P': self.P,
            'phi': self.phi,
            'phi_phi': self.phi_phi,
            'step': self.step,
            'run': self.run,
            'reset': self.reset,
            'fs_ls': self.fs_ls,
            'fs_cat': self.fs_cat,
            'fs_write': self.fs_write,
            'fs_rm': self.fs_rm
        }
        while True:
            cmd = self.input("HDGL-PY> ").strip()
            if cmd.lower() in ['exit','quit']: break
            try:
                exec(cmd, {"__builtins__": {}}, local_ns)
                self.D = local_ns['D']; self.void=local_ns['VOID']; self.omega=local_ns['OMEGA']; self.P=local_ns['P']
                self.update_plot()
            except Exception as e:
                self.print(f"Error: {e}")

    # -------------------------
    # Filesystem helpers
    # -------------------------
    def fs_command(self, cmd):
        parts=cmd.split()
        if len(parts)<2: return self.print("FS commands: ls, cat <file>, write <file>, rm <file>, echo <file> 'text'")
        fs_cmd=parts[1].lower()
        if fs_cmd=='ls': self.print(self.fs_ls())
        elif fs_cmd=='cat' and len(parts)>=3: self.print(self.fs_cat(parts[2]))
        elif fs_cmd=='rm' and len(parts)>=3: self.print(self.fs_rm(parts[2]))
        elif fs_cmd=='echo' and len(parts)>=4: self.fs_write(parts[2], " ".join(parts[3:])); self.print(f"Written to {parts[2]}")
        else: self.print("Unknown FS command.")

    # -------------------------
    # Utility I/O (VM-controlled)
    # -------------------------
    def input(self, prompt=""): return input(prompt)
    def print(self, *args): print(*args)
    def show_state(self): self.print(f"D:{np.round(self.D,5)} Void:{self.void:.5f} Omega:{self.omega:.5f}")
    def update_plot(self):
        for i,line in enumerate(self.lines_D): line.set_data(range(len(self.history_D)), [h[i] for h in self.history_D])
        self.line_void.set_data(range(len(self.history_void)), self.history_void)
        self.line_omega.set_data(range(len(self.history_omega)), self.history_omega)
        self.ax1.relim(); self.ax1.autoscale_view()
        self.ax2.relim(); self.ax2.autoscale_view()
        self.fig.canvas.draw(); self.fig.canvas.flush_events()

    # -------------------------
    # Filesystem init
    # -------------------------
    def init_filesystem(self):
        self.fs['/boot']={
            'grub.cfg':{
                'menu':[
                    ('Standard HDGL VM','run_standard_kernel'),
                    ('Fractal HDGL VM','run_fractal_kernel'),
                    ('Python Sandbox Only','run_python_shell')
                ]
            },
            'readme.txt':"Welcome to HDGL Analog VM"
        }

    def fs_ls(self): return list(self.fs.keys())
    def fs_cat(self, filename): return self.fs.get(filename,f"File '{filename}' not found.")
    def fs_write(self, filename, content): self.fs[filename]=content; return f"Written to {filename}"
    def fs_rm(self, filename): return f"Deleted {filename}" if self.fs.pop(filename,None) else f"File '{filename}' not found."

# -------------------------
# Launch VM fully self-contained
# -------------------------
if __name__=="__main__":
    vm = HDGL_VM()
    vm.boot()
    vm.repl()
