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

class HDGL_VM:
    def __init__(self):
        self.D = np.array([1.,2.,3.,4.,5.,6.,7.,8.,1.])
        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.phi_phi = 2.6180339887
        self.phi = 1.6180339887
        self.weights = np.ones(8)/8
        self.ip = 0
        self.program = [0,1,2,3,1,0,2,3,1,0]
        self.history_D = []
        self.history_void = []
        self.history_omega = []

    def step(self, blend_factor=0.05):
        if self.ip >= len(self.program):
            print("End of program.")
            return False
        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.phi_phi * p_val + weighted_sum + self.omega)
        self.void += np.mean(self.D) * blend_factor
        self.omega += 0.01 * blend_factor
        self.ip += 1

        self.history_D.append(self.D.copy())
        self.history_void.append(self.void)
        self.history_omega.append(self.omega)
        return True

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

    def reset(self):
        self.__init__()

    def show_state(self):
        print(f"D registers: {self.D}")
        print(f"Void: {self.void:.5f}, Omega: {self.omega:.5f}")

    # optional live plot
    def plot_state(self):
        plt.figure(figsize=(10,4))
        for i in range(len(self.D)):
            plt.plot([h[i] for h in self.history_D], label=f"D{i+1}")
        plt.plot(self.history_void, label="Void")
        plt.plot(self.history_omega, label="Omega")
        plt.legend()
        plt.show()

# --------------------------
# Command prompt / REPL
# --------------------------
def vm_repl(vm):
    print("Welcome to HDGL Analog VM. Type 'help' for commands.")
    while True:
        cmd = input("HDGL> ").strip().lower()
        if cmd in ['quit','exit']:
            break
        elif cmd == 'help':
            print("Commands:")
            print(" step [n]     - execute n steps (default 1)")
            print(" run [n]      - run continuously or n steps")
            print(" reset        - reset VM")
            print(" state        - show current D, Void, Omega")
            print(" set Dn=X     - set register n to X (e.g. set D1=5)")
            print(" plot         - plot all registers, Void, Omega")
            print(" exit / quit  - exit prompt")
        elif cmd.startswith('step'):
            parts = cmd.split()
            n = int(parts[1]) if len(parts) > 1 else 1
            vm.run(steps=n)
            vm.show_state()
        elif cmd.startswith('run'):
            parts = cmd.split()
            n = int(parts[1]) if len(parts) > 1 else None
            vm.run(steps=n)
            vm.show_state()
        elif cmd == 'reset':
            vm.reset()
            print("VM reset.")
        elif cmd == 'state':
            vm.show_state()
        elif cmd.startswith('set'):
            try:
                _, assign = cmd.split()
                reg, val = assign.split('=')
                reg = reg.strip().upper()
                val = float(val.strip())
                if reg.startswith('D'):
                    idx = int(reg[1:]) - 1
                    vm.D[idx] = val
                    print(f"{reg} set to {val}")
                else:
                    print("Unknown register.")
            except:
                print("Invalid set command.")
        elif cmd == 'plot':
            vm.plot_state()
        else:
            print("Unknown command. Type 'help'.")

# --------------------------
# Main
# --------------------------
if __name__ == "__main__":
    vm = HDGL_VM()
    vm_repl(vm)
