# uf/UF.py
import time
import random
import os
import math
import string

# -----------------------------
# Versioning
# -----------------------------
VERSION = "1.4.1"

DEPRECATED_VERSIONS = [
    "1.0.0"
]

LATEST_VERSION = "1.4.1"

def version_status(current_version):
    if current_version in DEPRECATED_VERSIONS:
        return f"[UF Notice] You are using a deprecated version: {current_version}"
    elif current_version != LATEST_VERSION:
        return f"[UF Notice] Your version ({current_version}) is older than the latest release ({LATEST_VERSION})"
    else:
        return f"[UF] Version {current_version} is up to date"

# -----------------------------
# Simple Tokenizer for all modes
# -----------------------------
def tokenize(line):
    tokens = []
    current = ""
    in_string = False
    for char in line:
        if char == '"':
            in_string = not in_string
            current += char
        elif char.isspace() and not in_string:
            if current:
                tokens.append(current)
                current = ""
        else:
            current += char
    if current:
        tokens.append(current)
    return tokens

# -----------------------------
# Base Mode Class
# -----------------------------
class BaseMode:
    def __init__(self, variables):
        self.variables = variables

    def execute(self, tokens):
        raise NotImplementedError("Must implement execute method")

    def show_commands(self):
        raise NotImplementedError("Must implement show_commands method")

# -----------------------------
# UF Mode (User Friendly / 10S)
# -----------------------------
class UFMode(BaseMode):
    def show_commands(self):
        print("\n=== UF Commands ===")
        print("hi -> prints hello world")
        print("hip -> prints hi")
        print("hih {text} -> prints your text")
        print("hid {text} {number} -> repeat text")
        print("set {name} {value} -> assign variable")
        print("get {name} -> get variable value")
        print("add {a} {b} -> addition")
        print("sub {a} {b} -> subtraction")
        print("mul {a} {b} -> multiplication")
        print("div {a} {b} -> division")
        print("rand {min} {max} -> random number")
        print("time -> current time")
        print("sleep {seconds} -> pause execution")
        print("upper {text} -> uppercase")
        print("lower {text} -> lowercase")
        print("len {text} -> length of text")
        print("rev {text} -> reverse text")
        print("clear -> clear screen")
        print("rndchar -> random letter")
        print("==================\n")

    def execute(self, tokens):
        if not tokens:
            return
        cmd = tokens[0].lower()
        if cmd == "hi":
            print("hello world")
        elif cmd == "hip":
            print("hi")
        elif cmd == "hih":
            if len(tokens) < 2:
                print("Usage: hih {text}")
            else:
                print(" ".join(tokens[1:]))
        elif cmd == "hid":
            if len(tokens) < 3:
                print("Usage: hid {text} {number}")
            else:
                try:
                    times = int(tokens[-1])
                    text = " ".join(tokens[1:-1])
                    for _ in range(times):
                        print(text)
                except:
                    print("Last value must be a number.")
        elif cmd == "set":
            try:
                self.variables[tokens[1]] = " ".join(tokens[2:])
                print(f"{tokens[1]} = {self.variables[tokens[1]]}")
            except:
                print("Usage: set {name} {value}")
        elif cmd == "get":
            try:
                print(self.variables.get(tokens[1], "Variable not found"))
            except:
                print("Usage: get {name}")
        elif cmd == "add":
            try:
                print(float(tokens[1]) + float(tokens[2]))
            except:
                print("Usage: add {a} {b}")
        elif cmd == "sub":
            try:
                print(float(tokens[1]) - float(tokens[2]))
            except:
                print("Usage: sub {a} {b}")
        elif cmd == "mul":
            try:
                print(float(tokens[1]) * float(tokens[2]))
            except:
                print("Usage: mul {a} {b}")
        elif cmd == "div":
            try:
                print(float(tokens[1]) / float(tokens[2]))
            except:
                print("Usage: div {a} {b}")
        elif cmd == "rand":
            try:
                print(random.randint(int(tokens[1]), int(tokens[2])))
            except:
                print("Usage: rand {min} {max}")
        elif cmd == "time":
            print(time.ctime())
        elif cmd == "sleep":
            try:
                time.sleep(float(tokens[1]))
            except:
                print("Usage: sleep {seconds}")
        elif cmd == "upper":
            print(" ".join(tokens[1:]).upper())
        elif cmd == "lower":
            print(" ".join(tokens[1:]).lower())
        elif cmd == "len":
            print(len(" ".join(tokens[1:])))
        elif cmd == "rev":
            print(" ".join(tokens[1:])[::-1])
        elif cmd == "clear":
            os.system("cls" if os.name == "nt" else "clear")
        elif cmd == "rndchar":
            print(random.choice(string.ascii_letters))
        else:
            print("Unknown UF command.")

# -----------------------------
# iUF Mode (Intermediate / 7S)
# -----------------------------
class iUFMode(BaseMode):
    def show_commands(self):
        print("\n=== iUF Commands ===")
        print("print {text}")
        print("repeat {number} {text}")
        print("set {name} {value}")
        print("get {name}")
        print("add {a} {b}")
        print("sub {a} {b}")
        print("mul {a} {b}")
        print("div {a} {b}")
        print("len {text}")
        print("clear")
        print("upper {text}")
        print("lower {text}")
        print("rand {min} {max}")
        print("time")
        print("rev {text}")
        print("sleep {seconds}")
        print("rndchar")
        print("==================\n")

    def execute(self, tokens):
        if not tokens:
            return
        cmd = tokens[0].lower()
        if cmd == "print":
            print(" ".join(tokens[1:]))
        elif cmd == "repeat":
            try:
                times = int(tokens[1])
                text = " ".join(tokens[2:])
                for _ in range(times):
                    print(text)
            except:
                print("Usage: repeat {number} {text}")
        elif cmd == "set":
            try:
                self.variables[tokens[1]] = " ".join(tokens[2:])
                print(f"{tokens[1]} = {self.variables[tokens[1]]}")
            except:
                print("Usage: set {name} {value}")
        elif cmd == "get":
            try:
                print(self.variables.get(tokens[1], "Variable not found"))
            except:
                print("Usage: get {name}")
        elif cmd == "add":
            try:
                print(float(tokens[1]) + float(tokens[2]))
            except:
                print("Usage: add {a} {b}")
        elif cmd == "sub":
            try:
                print(float(tokens[1]) - float(tokens[2]))
            except:
                print("Usage: sub {a} {b}")
        elif cmd == "mul":
            try:
                print(float(tokens[1]) * float(tokens[2]))
            except:
                print("Usage: mul {a} {b}")
        elif cmd == "div":
            try:
                print(float(tokens[1]) / float(tokens[2]))
            except:
                print("Usage: div {a} {b}")
        elif cmd == "len":
            print(len(" ".join(tokens[1:])))
        elif cmd == "clear":
            os.system("cls" if os.name == "nt" else "clear")
        elif cmd == "upper":
            print(" ".join(tokens[1:]).upper())
        elif cmd == "lower":
            print(" ".join(tokens[1:]).lower())
        elif cmd == "rand":
            try:
                print(random.randint(int(tokens[1]), int(tokens[2])))
            except:
                print("Usage: rand {min} {max}")
        elif cmd == "time":
            print(time.ctime())
        elif cmd == "rev":
            print(" ".join(tokens[1:])[::-1])
        elif cmd == "sleep":
            try:
                time.sleep(float(tokens[1]))
            except:
                print("Usage: sleep {seconds}")
        elif cmd == "rndchar":
            print(random.choice(string.ascii_letters))
        else:
            print("Unknown iUF command.")

# -----------------------------
# nUF Mode (Advanced / 3.6S)
# -----------------------------
class nUFMode(BaseMode):
    def show_commands(self):
        print("\n=== nUF Commands ===")
        print("var x = value")
        print("show")
        print("calc {expression}")
        print("echo {text}")
        print("> {text}")
        print("sqrt {number}")
        print("pow {base} {exp}")
        print("round {number}")
        print("len {text}")
        print("repeatf {times} {text}")
        print("upper {text}")
        print("lower {text}")
        print("rev {text}")
        print("rand {min} {max}")
        print("time")
        print("sleep {seconds}")
        print("sys {command}")
        print("exec {python_code}")
        print("rndchar")
        print("==================\n")

    def evaluate_expression(self, expr):
        for var in self.variables:
            expr = expr.replace(var, str(self.variables[var]))
        try:
            allowed_names = {"math": math}
            return eval(expr, {"__builtins__": None}, allowed_names)
        except:
            return None

    def execute(self, tokens):
        if not tokens:
            return
        cmd = tokens[0].lower()
        if cmd == "var":
            try:
                name = tokens[1]
                if tokens[2] != "=":
                    print("Syntax: var x = value")
                    return
                value = " ".join(tokens[3:])
                self.variables[name] = value
                print(f"{name} = {value}")
            except:
                print("Syntax: var x = value")
        elif cmd == "show":
            print("Variables:", self.variables)
        elif cmd == "calc":
            expr = " ".join(tokens[1:])
            result = self.evaluate_expression(expr)
            if result is not None:
                print(result)
            else:
                print("Invalid expression.")
        elif cmd in ["echo", ">"]:
            print(" ".join(tokens[1:]))
        elif cmd == "sqrt":
            try:
                print(math.sqrt(float(tokens[1])))
            except:
                print("Usage: sqrt {number}")
        elif cmd == "pow":
            try:
                print(math.pow(float(tokens[1]), float(tokens[2])))
            except:
                print("Usage: pow {base} {exp}")
        elif cmd == "round":
            try:
                print(round(float(tokens[1])))
            except:
                print("Usage: round {number}")
        elif cmd == "len":
            print(len(" ".join(tokens[1:])))
        elif cmd == "repeatf":
            try:
                times = int(tokens[1])
                text = " ".join(tokens[2:])
                for _ in range(times):
                    print(text)
            except:
                print("Usage: repeatf {times} {text}")
        elif cmd == "upper":
            print(" ".join(tokens[1:]).upper())
        elif cmd == "lower":
            print(" ".join(tokens[1:]).lower())
        elif cmd == "rev":
            print(" ".join(tokens[1:])[::-1])
        elif cmd == "rand":
            try:
                print(random.randint(int(tokens[1]), int(tokens[2])))
            except:
                print("Usage: rand {min} {max}")
        elif cmd == "time":
            print(time.ctime())
        elif cmd == "sleep":
            try:
                time.sleep(float(tokens[1]))
            except:
                print("Usage: sleep {seconds}")
        elif cmd == "sys":
            os.system(" ".join(tokens[1:]))
        elif cmd == "exec":
            try:
                exec(" ".join(tokens[1:]))
            except Exception as e:
                print("Python error:", e)
        elif cmd == "rndchar":
            print(random.choice(string.ascii_letters))
        else:
            print("Unknown nUF command.")

# -----------------------------
# dnUF Mode (Expert / 1S)
# -----------------------------
class dnUFMode(BaseMode):
    def show_commands(self):
        print("\n=== dnUF Commands ===")
        print("let x = value")
        print("vars")
        print("echo {text} (supports $variables)")
        print("calc {expression}")
        print("if {condition} then {command}")
        print("sleep {seconds}")
        print("sys {command}")
        print("exec {python_code}")
        print("len {text}")
        print("read {filename}")
        print("upper {text}")
        print("lower {text}")
        print("rev {text}")
        print("rand {min} {max}")
        print("time")
        print("rndchar")
        print("==================\n")

    def evaluate_expression(self, expr):
        for var in self.variables:
            expr = expr.replace(var, str(self.variables[var]))
        try:
            return eval(expr, {"__builtins__": None}, {"math": math})
        except:
            return None

    def execute(self, tokens):
        if not tokens:
            return
        cmd = tokens[0].lower()
        if cmd == "let":
            try:
                name = tokens[1]
                if tokens[2] != "=":
                    print("Syntax: let x = value")
                    return
                value = " ".join(tokens[3:])
                self.variables[name] = value
                print(f"[OK] {name} = {value}")
            except:
                print("Syntax: let x = value")
        elif cmd == "vars":
            print("Variables:", self.variables)
        elif cmd == "echo":
            text = " ".join(tokens[1:])
            for var in self.variables:
                text = text.replace(f"${var}", str(self.variables[var]))
            print(text)
        elif cmd == "calc":
            expr = " ".join(tokens[1:])
            result = self.evaluate_expression(expr)
            if result is not None:
                print(result)
            else:
                print("Invalid calculation.")
        elif cmd == "if":
            try:
                then_index = tokens.index("then")
                condition = " ".join(tokens[1:then_index])
                for var in self.variables:
                    condition = condition.replace(var, str(self.variables[var]))
                if eval(condition, {"__builtins__": None}, {}):
                    action = " ".join(tokens[then_index+1:])
                    if action.startswith("echo"):
                        print(" ".join(action.split()[1:]))
            except:
                print("Syntax: if condition then ...")
        elif cmd == "sleep":
            try:
                seconds = float(tokens[1])
                time.sleep(seconds)
                print(f"Slept for {seconds} seconds.")
            except:
                print("Usage: sleep {seconds}")
        elif cmd == "sys":
            os.system(" ".join(tokens[1:]))
        elif cmd == "exec":
            try:
                exec(" ".join(tokens[1:]))
            except Exception as e:
                print("Python error:", e)
        elif cmd == "len":
            print(len(" ".join(tokens[1:])))
        elif cmd == "read":
            try:
                filename = tokens[1]
                with open(filename, "r") as f:
                    content = f.read()
                print(content)
            except:
                print("Usage: read {filename}")
        elif cmd == "upper":
            print(" ".join(tokens[1:]).upper())
        elif cmd == "lower":
            print(" ".join(tokens[1:]).lower())
        elif cmd == "rev":
            print(" ".join(tokens[1:])[::-1])
        elif cmd == "rand":
            try:
                print(random.randint(int(tokens[1]), int(tokens[2])))
            except:
                print("Usage: rand {min} {max}")
        elif cmd == "time":
            print(time.ctime())
        elif cmd == "rndchar":
            print(random.choice(string.ascii_letters))
        else:
            print("Unknown dnUF command.")

# -----------------------------
# Main UF Engine
# -----------------------------
def main():
    print(f"UF Language Engine v{VERSION}")
    print(version_status(VERSION))
    print("Modes available: UF, iUF, nUF, dnUF")

    mode = ""
    valid_modes = ["uf", "iuf", "nuf", "dnuf"]
    while mode.lower() not in valid_modes:
        mode = input("Select mode (UF / iUF / nUF / dnUF): ").strip().lower()

    variables = {}
    mode_classes = {
        "uf": UFMode,
        "iuf": iUFMode,
        "nuf": nUFMode,
        "dnuf": dnUFMode
    }
    current_mode = mode_classes[mode](variables)

    print(f"\nLoaded mode: {mode.upper()}")
    if mode == "uf":
        current_mode.show_commands()
    else:
        print("Tip: Discover commands yourself...")

    while True:
        try:
            code = input(f"{mode.upper()} > ").strip()
        except EOFError:
            print("\nExiting UF Engine...")
            break

        if code == "":
            continue
        if code.lower() == "exit":
            print("Exiting UF Engine...")
            break

        if mode == "iuf" and code.lower() == "help":
            current_mode.show_commands()
            continue
        if mode == "nuf" and code.lower() == "help me":
            current_mode.show_commands()
            continue
        if mode == "dnuf" and code.lower() == "help me please now":
            current_mode.show_commands()
            continue

        tokens = tokenize(code)
        current_mode.execute(tokens)

# -----------------------------
# Script Execution Support
# -----------------------------
def run_file(filename, mode_name="uf"):
    valid_modes = ["uf", "iuf", "nuf", "dnuf"]
    if mode_name not in valid_modes:
        print("Invalid mode for script execution.")
        return

    variables = {}
    mode_classes = {
        "uf": UFMode,
        "iuf": iUFMode,
        "nuf": nUFMode,
        "dnuf": dnUFMode
    }

    current_mode = mode_classes[mode_name](variables)

    try:
        with open(filename, "r") as f:
            for line in f:
                line = line.strip()
                if not line or line.startswith("#"):
                    continue
                tokens = tokenize(line)
                current_mode.execute(tokens)
    except FileNotFoundError:
        print(f"File not found: {filename}")
    except Exception as e:
        print(f"Error running file: {e}")

# -----------------------------
# Entry Point
# -----------------------------
if __name__ == "__main__":
    main()