# Simulated OS with External Program Execution + Enhanced Editor + BASIC
# Save as: os_simulator.py

import sys
import os
import subprocess
from pathlib import Path

class FileSystemNode:
    def __init__(self, name, is_directory=False, content=None):
        self.name = name
        self.is_directory = is_directory
        self.content = content if not is_directory else {}
        self.parent = None

class BasicInterpreter:
    def __init__(self):
        self.program = {}
        self.variables = {}

    def run_repl(self):
        print("BASIC Interpreter. Type EXIT to return.")
        while True:
            try:
                line = input("BASIC> ").strip()
            except (EOFError, KeyboardInterrupt):
                print("\nExiting BASIC.")
                break
            if not line:
                continue
            line = line.upper()
            if line == "EXIT":
                break
            elif line == "RUN":
                self.execute()
            elif line == "LIST":
                for ln in sorted(self.program):
                    print(f"{ln} {self.program[ln]}")
            elif line == "NEW":
                self.program.clear()
                print("Ready.")
            else:
                parts = line.split(" ", 1)
                if parts[0].isdigit():
                    num = int(parts[0])
                    code = parts[1] if len(parts) > 1 else ""
                    if code:
                        self.program[num] = code
                    elif num in self.program:
                        del self.program[num]
                else:
                    self.parse_and_execute(line)

    def execute(self):
        self.variables = {}
        lines = sorted(self.program.keys())
        i = 0
        while i < len(lines):
            stmt = self.program[lines[i]]
            self.parse_and_execute(stmt)
            i += 1

    def parse_and_execute(self, stmt):
        stmt = stmt.strip().upper()
        if stmt.startswith("PRINT"):
            expr = stmt[6:].strip()
            if expr.startswith('"') and expr.endswith('"'):
                print(expr[1:-1])
            else:
                try:
                    val = eval(expr, {"__builtins__": {}}, self.variables)
                    print(val)
                except:
                    print("?SYNTAX ERROR")
        elif stmt.startswith("LET "):
            stmt = stmt[4:]
            if "=" in stmt:
                var, expr = stmt.split("=", 1)
                var = var.strip()
                try:
                    val = eval(expr.strip(), {"__builtins__": {}}, self.variables)
                    self.variables[var] = val
                except:
                    print("?SYNTAX ERROR")
        elif stmt.startswith("GOTO "):
            try:
                target = int(stmt[5:].strip())
                if target in self.program:
                    # Not perfect GOTO (would need loop rewrite), but works for simple cases
                    pass
            except:
                print("?SYNTAX ERROR")
        elif stmt == "END":
            pass

class SimulatedOS:
    def __init__(self):
        self.root = FileSystemNode("/", is_directory=True)
        self.current_dir = self.root
        self.basic_interp = BasicInterpreter()
        self._create_initial_files()

        # Recommended folder for external programs
        self.programs_dir = Path("programs")
        self.programs_dir.mkdir(exist_ok=True)
        print(f"Tip: Put your .py programs in the '{self.programs_dir}' folder next to this script!")

    def _create_initial_files(self):
        home = FileSystemNode("home", is_directory=True)
        self.root.content["home"] = home
        home.parent = self.root

        user = FileSystemNode("user", is_directory=True)
        home.content["user"] = user
        user.parent = home

        welcome = FileSystemNode("welcome.txt", content="Welcome to PyOS v4!\nNow with neofetch!\nType 'help' for commands.")
        user.content["welcome.txt"] = welcome
        welcome.parent = user

        hello_py = FileSystemNode("hello.py", content='name = input("What is your name? ")\nprint(f"Hello {name}! Welcome to external programs!")')
        user.content["hello.py"] = hello_py
        hello_py.parent = user

    def _get_full_path(self, node):
        path = []
        while node and node != self.root:
            path.append(node.name)
            node = node.parent
        return "/" + "/".join(reversed(path)) if path else "/"

    def _resolve_path(self, path):
        if path == "/": return self.root
        parts = path.strip("/").split("/")
        current = self.root if path.startswith("/") else self.current_dir
        for part in parts:
            if not part or part == ".": continue
            if part == "..":
                if current.parent:
                    current = current.parent
                continue
            if not current.is_directory or part not in current.content:
                return None
            current = current.content[part]
        return current

    # === Commands ===

    def ls(self, args):
        target = self.current_dir if not args else self._resolve_path(args[0])
        if not target or not target.is_directory:
            print("ls: no such directory")
            return
        for name in sorted(target.content):
            print(name)

    def cd(self, args):
        if not args:
            self.current_dir = self._resolve_path("/home/user") or self.current_dir
            return
        target = self._resolve_path(args[0])
        if target and target.is_directory:
            self.current_dir = target
        else:
            print("cd: no such directory")

    def pwd(self, args):
        print(self._get_full_path(self.current_dir))

    def cat(self, args):
        if not args: return print("cat: missing file")
        node = self._resolve_path(args[0])
        if node and not node.is_directory:
            print(node.content)
        else:
            print("cat: not a file or not found")

    def edit(self, args):
        if not args: return print("edit: missing file")
        path = args[0]
        node = self._resolve_path(path)
        if node and node.is_directory:
            return print("edit: is a directory")

        if not node:
            # Create file
            parts = path.strip("/").split("/")
            current = self.root if path.startswith("/") else self.current_dir
            for part in parts[:-1]:
                if part:
                    if part not in current.content:
                        print("edit: parent directory does not exist")
                        return
                    current = current.content[part]
            node = FileSystemNode(parts[-1], content="")
            current.content[parts[-1]] = node
            node.parent = current

        print(f"--- Editing {path} --- (blank line to save & exit, Ctrl+C to cancel)")
        lines = []
        if node.content:
            print(node.content)
            print("---")
        while True:
            try:
                line = input()
                if line == "" and lines == [] and node.content:
                    break  # Just pressed enter without changes
                if line == "":
                    break
                lines.append(line)
            except (EOFError, KeyboardInterrupt):
                print("\nEdit cancelled.")
                return
        node.content = "\n".join(lines)
        print("Saved.")

    def run(self, args):
        if not args:
            return print("run: missing program.py")

        program_name = args[0]
        program_args = args[1:]

        # First, try to find in the real "programs/" folder
        real_path = Path("programs") / program_name
        if real_path.is_file() and real_path.suffix == ".py":
            print(f"Launching external program: {real_path}")
            try:
                result = subprocess.run(
                    [sys.executable, str(real_path)] + program_args,
                    cwd="programs"
                )
                print(f"\nProgram exited with code {result.returncode}")
            except Exception as e:
                print(f"Error running program: {e}")
            return

        # Second, check if it's a virtual .py file inside the simulated FS
        node = self._resolve_path(program_name)
        if node and not node.is_directory and program_name.endswith(".py"):
            # Save virtual program to a temporary real file
            import tempfile
            with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
                f.write(node.content)
                temp_path = f.name

            print(f"Launching virtual program: {program_name}")
            try:
                result = subprocess.run(
                    [sys.executable, temp_path] + program_args
                )
                print(f"\nProgram exited with code {result.returncode}")
            except Exception as e:
                print(f"Error: {e}")
            finally:
                Path(temp_path).unlink(missing_ok=True)
            return

        print(f"run: program not found: {program_name}")
        print("   Put .py files in the 'programs/' folder or create them with 'edit game.py'")

    def mkdir(self, args):
        if not args: return print("mkdir: missing name")
        path = args[0]
        parts = [p for p in path.strip("/").split("/") if p]
        current = self.root if path.startswith("/") else self.current_dir
        for p in parts[:-1]:
            if p not in current.content:
                current.content[p] = FileSystemNode(p, is_directory=True)
                current.content[p].parent = current
            current = current.content[p]
        if parts[-1] not in current.content:
            current.content[parts[-1]] = FileSystemNode(parts[-1], is_directory=True)
            current.content[parts[-1]].parent = current

    def rm(self, args):
        if not args: return print("rm: missing target")
        node = self._resolve_path(args[0])
        if not node or node == self.root:
            print("rm: not found or protected")
            return
        if node.is_directory and node.content:
            print("rm: directory not empty")
            return
        del node.parent.content[node.name]

    def neofetch(self, args):
        ascii_art = """
       .--.      
      |o_o |     
      |:_/ |     
     //   \\ \\   
    (|     | )   
   /'\\_   _/`\\  
   \\___)=(___/  
        """
        os_name = "PyOS v4 - Powered by xAI"
        specs = [
            "OS: PyOS 4.0 (Simulated Edition)",
            "Kernel: Python 3.x Quantum Flux",
            "Uptime: Eternal (since the Big Bang)",
            "Packages: Infinite (all the memes)",
            "Shell: GrokShell 1.0",
            "Resolution: 42xUniverse",
            "DE: ASCII Art Environment",
            "WM: Windowless Magic",
            "Theme: Dark Matter [Grok]",
            "Icons: Emoji Overlords",
            "Terminal: Your Console",
            "CPU: Neural Net Overclocked @ ∞ GHz",
            "GPU: Imagination Renderer",
            "Memory: All the RAM in the Multiverse",
            "Easter Egg: Why did the AI cross the road? To optimize the other side!"
        ]
        
        # Print ASCII art and specs side by side
        art_lines = ascii_art.strip().split("\n")
        max_art_len = max(len(line) for line in art_lines)
        for i in range(max(len(art_lines), len(specs))):
            art = art_lines[i] if i < len(art_lines) else ""
            spec = specs[i] if i < len(specs) else ""
            print(f"{art.ljust(max_art_len)}   {spec}")
        print("\n")

    def help(self, args):
        print("""Available commands:
  ls             List directory
  cd [dir]       Change directory (cd alone goes home)
  pwd            Show current path
  cat <file>     Show file content
  edit <file>    Create/edit text file
  run <prog.py>  Run external Python program (from 'programs/' folder or virtual FS)
  basic          Enter BASIC interpreter
  mkdir <dir>    Create directory
  rm <file>      Remove file or empty directory
  neofetch       Show system info with ASCII art
  clear          Clear screen
  help           This help
  exit           Quit PyOS""")

    def clear(self, args):
        print("\n" * 60)

    def shell(self):
        print("Welcome to PyOS v4 - Now with improved interrupt handling!")
        print("Type 'help' for commands.\n")
        while True:
            try:
                cmd_line = input(f"{self._get_full_path(self.current_dir)} > ").strip()
            except (EOFError, KeyboardInterrupt):
                print("\nGoodbye!")
                break

            if not cmd_line:
                continue

            parts = cmd_line.split()
            cmd = parts[0].lower()
            args = parts[1:]

            if cmd == "exit":
                print("Shutting down PyOS...")
                break
            elif cmd == "ls": self.ls(args)
            elif cmd == "cd": self.cd(args)
            elif cmd == "pwd": self.pwd(args)
            elif cmd == "cat": self.cat(args)
            elif cmd == "edit": self.edit(args)
            elif cmd == "run": self.run(args)
            elif cmd == "basic": self.basic_interp.run_repl()
            elif cmd == "mkdir": self.mkdir(args)
            elif cmd == "rm": self.rm(args)
            elif cmd == "neofetch": self.neofetch(args)
            elif cmd == "clear": self.clear(args)
            elif cmd == "help": self.help(args)
            else:
                print(f"Unknown command: {cmd}")

if __name__ == "__main__":
    os_system = SimulatedOS()
    os_system.shell()