import machine
import time
import rp2
import json
import array
import uctypes
import neopixel
import sys
import os
from machine import Pin, I2C, PWM, Timer
import sh1106

# --- CONFIGURATION DU SYSTÈME ---
TEST = False           
SYS_FREQ = 48000000    

machine.freq(SYS_FREQ)

def dprint(*args, **kwargs):
    if TEST:
        print(*args, **kwargs)

# --- CONSTANTE DE SÉLECTION DU MODE DE MODULATION ---
USE_EXTERNAL_PWM_MODULATION = False

# --- BROCHAGE MATÉRIEL RP2040-ZERO ---
LED_MATRIX_PIN = 0      # Broche de données pour la matrice WS2812 (PIO)
LED_INTERNAL_PIN = 16   # NeoPixel interne
MOD_OUT_PIN = 8         # Sortie de modulation (Broche 8)
I2C_SDA = 4             # SDA pour l'OLED SH1106
I2C_SCL = 5             # SCL pour l'OLED SH1106
ENC_CLK = 14            # Encodeur CLK
ENC_DT = 15             # Encodeur DT
ENC_SW = 2              # Poussoir sur le GPIO 2

matrix_dimension = 8
MAX_LEDS = 64

# --- PROGRAMME PIO POUR LA MATRICE (WS2812 - Format GRB sur 24 bits supérieurs) ---
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, pull_thresh=24)
def ws2812_pio():
    wrap_target()
    label("bitloop")
    out(x, 1)               .side(0) [2]
    jmp(not_x, "do_zero")   .side(1) [1]
    jmp("bitloop")          .side(1) [4]
    label("do_zero")
    nop()                   .side(0) [4]
    wrap()

sm = rp2.StateMachine(0, ws2812_pio, freq=8000000, sideset_base=Pin(LED_MATRIX_PIN))
sm.active(1)

# --- CONFIGURATION DMA DIRECTE 32-BIT (RP2040) ---
DMA_BASE = 0x50000000
CH0_READ_ADDR  = DMA_BASE + 0x000
CH0_WRITE_ADDR = DMA_BASE + 0x004
CH0_TRANS_COUNT= DMA_BASE + 0x008
CH0_CTRL_TRIG  = DMA_BASE + 0x00C
DMA_CH_ABORT   = DMA_BASE + 0x444
PIO0_FIFO_TX0  = 0x50200010

def send_via_dma(data_buffer):
    """Envoie la trame à la matrice via le canal DMA 0 en mode 32-bit"""
    active_buffer = data_buffer if (matrix_power or current_menu in ["CALIB", "RUN_PROG"]) else buf_off
        
    active_words = matrix_dimension * matrix_dimension
    
    machine.mem32[DMA_CH_ABORT] = 1 << 0
    while machine.mem32[CH0_CTRL_TRIG] & (1 << 31):
        pass
    
    machine.mem32[CH0_READ_ADDR] = uctypes.addressof(memoryview(active_buffer))
    machine.mem32[CH0_WRITE_ADDR] = PIO0_FIFO_TX0
    machine.mem32[CH0_TRANS_COUNT] = active_words
    
    ctrl_val = (0 << 15) | (0 << 5) | (1 << 4) | (2 << 2) | (1 << 0)
    machine.mem32[CH0_CTRL_TRIG] = ctrl_val

# --- INITIALISATION DE LA NEOPIXEL INTERNE (GPIO 16) ---
internal_np = neopixel.NeoPixel(Pin(LED_INTERNAL_PIN), 1)

# --- CONFIGURATION PWM ET TIMER POUR LA BROCHE 8 ---
mod_pwm = PWM(Pin(MOD_OUT_PIN))
mod_pwm.duty_u16(0)
mod_timer = Timer(-1)

def toggle_gpio8_pin(t):
    """Bascule la broche 8 pour les très basses fréquences (< 5 Hz)"""
    current_duty = mod_pwm.duty_u16()
    mod_pwm.duty_u16(0 if current_duty > 0 else 32768)

def configure_modulation(freq_hz):
    """Gère la modulation hybride : PWM matériel si >= 5Hz, Timer logiciel si < 5Hz"""
    global mod_timer
    mod_timer.deinit()
    
    if freq_hz <= 0.0 or not matrix_power:
        mod_pwm.duty_u16(0)
        return
        
    if freq_hz < 5.0:
        mod_pwm.duty_u16(32768)
        period_ms = max(1, int(round(500.0 / freq_hz)))
        mod_timer.init(period=period_ms, mode=Timer.PERIODIC, callback=toggle_gpio8_pin)
    else:
        sys_clk = machine.freq()
        div = 64.0 if freq_hz >= 10.0 else 128.0
        top = int((sys_clk / (freq_hz * div)) - 1)
        top = max(1, min(65535, top))
        try:
            actual_freq = int(sys_clk / (div * (top + 1)))
            mod_pwm.freq(max(1, actual_freq))
            mod_pwm.duty_u16(32768)
        except Exception:
            mod_pwm.duty_u16(0)

# --- INITIALISATION I2C ET OLED SH1106 ---
i2c = I2C(0, sda=Pin(I2C_SDA), scl=Pin(I2C_SCL), freq=400000)
display = None
try:
    display = sh1106.SH1106_I2C(128, 64, i2c, addr=0x3c, res=None, rotate=180)
except Exception:
    pass

# --- ENCODER PINS & BOUTON ---
clk = Pin(ENC_CLK, Pin.IN, Pin.PULL_UP)
dt = Pin(ENC_DT, Pin.IN, Pin.PULL_UP)
sw = Pin(ENC_SW, Pin.IN, Pin.PULL_UP)

# --- PALETTE DE COULEURS ---
COLOR_MAP = {
    "noir": (0, 0, 0),
    "rouge": (255, 0, 0),
    "ecarlate": (255, 36, 0),
    "orange": (255, 127, 0),
    "ambre": (255, 191, 0),
    "jaune": (255, 255, 0),
    "chartreuse": (127, 255, 0),
    "vert": (0, 255, 0),
    "emeraude": (0, 255, 127),
    "cyan": (0, 255, 255),
    "azure": (0, 127, 255),
    "bleu": (0, 0, 255),
    "indigo": (75, 0, 130),
    "violet": (148, 0, 211),
    "magenta": (255, 0, 255),
    "rose": (255, 105, 180),
    "blanc": (255, 255, 255)
}

COLORS = list(COLOR_MAP.items())

# --- ÉTATS DU SYSTÈME ---
current_menu = "MAIN"   # "MAIN", "CALIB", "SEL_PROG", "RUN_PROG"
cursor_pos = 0          
calib_cursor_pos = 0    
file_cursor_pos = 0
is_editing = False      

color_index = 1
modulation_hz = 0.0
matrix_modulation_enabled = True 
intensity = 100         
matrix_power = True
selected_program_file = "Aucun"

r_balance = 100
g_balance = 100
b_balance = 100
calib_color_index = 1   

last_clk_val = clk.value()
last_click_time = 0
press_start_time = 0
mod_state = True

buf_on = array.array("I", [0] * MAX_LEDS)
buf_off = array.array("I", [0] * MAX_LEDS)
dma_timer = Timer(-1)

# --- GESTION DE LA MÉMOIRE FLASH & CONFIG ---
CONFIG_FILE = "config.json"

def load_config():
    global color_index, modulation_hz, matrix_modulation_enabled, intensity, matrix_dimension, matrix_power, r_balance, g_balance, b_balance, calib_color_index, selected_program_file
    try:
        with open(CONFIG_FILE, "r") as f:
            data = json.load(f)
            color_index = data.get("color_index", 1)
            modulation_hz = data.get("modulation_hz", 0.0)
            matrix_modulation_enabled = data.get("matrix_modulation_enabled", True)
            intensity = data.get("intensity", 100)
            matrix_dimension = data.get("matrix_dimension", 8)
            matrix_power = data.get("matrix_power", True)
            r_balance = data.get("r_balance", 100)
            g_balance = data.get("g_balance", 100)
            b_balance = data.get("b_balance", 100)
            selected_program_file = data.get("selected_program_file", "Aucun")
            calib_color_index = color_index
    except Exception:
        pass

def save_config():
    data = {
        "color_index": color_index,
        "modulation_hz": modulation_hz,
        "matrix_modulation_enabled": matrix_modulation_enabled,
        "intensity": intensity,
        "matrix_dimension": matrix_dimension,
        "matrix_power": matrix_power,
        "r_balance": r_balance,
        "g_balance": g_balance,
        "b_balance": b_balance,
        "selected_program_file": selected_program_file
    }
    try:
        with open(CONFIG_FILE, "w") as f:
            json.dump(data, f)
        if display:
            display.fill(0)
            display.text("SAUVEGARDE OK !", 10, 28, 1)
            display.show()
        time.sleep(1)
    except Exception:
        pass

load_config()
matrix_dimension = 8
calib_color_index = color_index

def get_txt_files():
    """Récupère la liste de tous les fichiers .txt présents dans la flash"""
    try:
        files = [f for f in os.listdir() if f.endswith(".txt")]
        return files if files else ["Aucun .txt"]
    except Exception:
        return ["Aucun .txt"]

def update_pixel_buffers(custom_color=None, custom_intensity=None):
    global buf_on
    active_count = matrix_dimension * matrix_dimension
    
    if custom_color:
        r, g, b = custom_color
    else:
        active_col_idx = calib_color_index if current_menu == "CALIB" else color_index
        _, (r, g, b) = COLORS[active_col_idx]
    
    cur_intensity = custom_intensity if custom_intensity is not None else intensity
    
    r_int = min(255, int(r * cur_intensity * r_balance / 10000))
    g_int = min(255, int(g * cur_intensity * g_balance / 10000))
    b_int = min(255, int(b * cur_intensity * b_balance / 10000))
    
    color_val = (g_int << 24) | (r_int << 16) | (b_int << 8)
    
    for i in range(MAX_LEDS):
        if i < active_count:
            buf_on[i] = color_val
        else:
            buf_on[i] = 0
            
    if matrix_power or current_menu in ["CALIB", "RUN_PROG"]:
        internal_np[0] = (r_int, g_int, b_int)
    else:
        internal_np[0] = (0, 0, 0)
    internal_np.write()

def dma_callback(t):
    global mod_state
    if matrix_power and modulation_hz > 0.0 and matrix_modulation_enabled:
        mod_state = not mod_state
        target_buf = buf_on if mod_state else buf_off
        send_via_dma(target_buf)

# --- MISE À JOUR DE LA CONFIGURATION DU TIMER POUR MODULER LA MATRICE ---
def update_timer_config():
    global mod_state
    dma_timer.deinit()
    
    if modulation_hz <= 0.0 or not matrix_power:
        mod_pwm.duty_u16(0)
        mod_state = True
        send_via_dma(buf_on)
    else:
        # Si on est en mode RUN_PROG ou si explicitement demandé, on module la matrice via dma_timer
        if current_menu == "RUN_PROG" or not USE_EXTERNAL_PWM_MODULATION:
            mod_pwm.duty_u16(0)
            if matrix_modulation_enabled:
                period_ms = max(1, int(round(500.0 / modulation_hz)))
                dma_timer.init(period=period_ms, mode=Timer.PERIODIC, callback=dma_callback)
            else:
                mod_state = True
                send_via_dma(buf_on)
        else:
            configure_modulation(modulation_hz)
            mod_state = True
            send_via_dma(buf_on)

# --- EXECUTION DU PROGRAMME .TXT SÉCURISÉ ---
# --- EXECUTION DU PROGRAMME .TXT AVEC DIAGNOSTIC OLED ---
# --- EXECUTION DU PROGRAMME .TXT ULTRA-ROBUSTE ---
# --- EXECUTION DU PROGRAMME .TXT AVEC ANTI-REBOND AU DÉMARRAGE ---
# --- EXECUTION DU PROGRAMME AVEC DIAGNOSTIC OLED ---
# --- EXECUTION DU PROGRAMME AVEC SUPPORT DES VIRGULES ET ESPACES ---
# --- EXECUTION DU PROGRAMME AVEC DÉCOMPTE TEMPS RÉEL ---
# --- EXECUTION DU PROGRAMME AVEC MODULATION DYNAMIQUE PAR LIGNE ---
# --- EXECUTION DU PROGRAMME AVEC MODULATION ACTIVE SUR LA MATRICE ---
# --- EXECUTION DU PROGRAMME AVEC MODULATION DIRECTE ET DÉCOMPTE ---
# --- EXECUTION DU PROGRAMME AVEC RESTAURATION DE L'ÉTAT INITIAL ---
def run_program(filename):
    global current_menu, is_editing, modulation_hz, intensity, matrix_modulation_enabled, color_index
    is_editing = False
    
    if filename == "Aucun" or filename == "Aucun .txt":
        if display:
            display.fill(0)
            display.text("CHOISIR UN PROG", 10, 24, 1)
            display.text("D'ABORD !", 24, 38, 1)
            display.show()
        time.sleep(1.5)
        update_display()
        return
        
    # --- SAUVEGARDE DE L'ÉTAT INITIAL DU MENU PRINCIPAL ---
    saved_color_index = color_index
    saved_modulation_hz = modulation_hz
    saved_intensity = intensity
    saved_matrix_mod = matrix_modulation_enabled
    
    current_menu = "RUN_PROG"
    
    if display:
        display.fill(0)
        display.text("LECTURE...", 10, 15, 1)
        display.show()

    try:
        with open(filename, "r") as f:
            lines = f.readlines()
    except Exception as e:
        if display:
            display.fill(0)
            display.text("INTROUVABLE", 10, 28, 1)
            display.show()
        time.sleep(2.0)
        current_menu = "MAIN"
        update_display()
        return

    valid_steps = []
    for raw_l in lines:
        clean_l = raw_l.strip()
        if not clean_l or clean_l.startswith("#"):
            continue
        if "#" in clean_l:
            clean_l = clean_l.split("#")[0].strip()
            if not clean_l:
                continue
        valid_steps.append(clean_l)

    if not valid_steps:
        if display:
            display.fill(0)
            display.text("FICHIER VIDE", 15, 22, 1)
            display.show()
        time.sleep(2.0)
        current_menu = "MAIN"
        update_display()
        return

    try:
        while current_menu == "RUN_PROG":
            step_idx = 0
            while step_idx < len(valid_steps) and current_menu == "RUN_PROG":
                line_str = valid_steps[step_idx]
                step_idx += 1
                
                if "," in line_str:
                    parts = [p.strip() for p in line_str.split(",")]
                elif ";" in line_str:
                    parts = [p.strip() for p in line_str.split(";")]
                else:
                    parts = line_str.split()
                
                if len(parts) == 1 and parts[0].lower() == "loop":
                    step_idx = 0
                    continue
                    
                if len(parts) < 4:
                    continue
                    
                col_name = parts[0].lower()
                
                try:
                    freq = float(parts[1])
                    duration = float(parts[2])
                    step_intensity = float(parts[3])
                except ValueError:
                    continue
                    
                col_rgb = COLOR_MAP.get(col_name, (255, 255, 255))
                
                modulation_hz = max(0.0, min(50.0, freq))
                intensity = max(0, min(100, int(step_intensity)))
                matrix_modulation_enabled = (modulation_hz > 0.0)
                
                update_pixel_buffers(custom_color=col_rgb, custom_intensity=intensity)
                
                duration_ms = int(duration * 1000)
                elapsed = 0
                led_state = True
                
                half_period_ms = int(500.0 / modulation_hz) if modulation_hz > 0 else 0
                last_toggle_time = time.ticks_ms()
                
                while elapsed < duration_ms and current_menu == "RUN_PROG":
                    current_time = time.ticks_ms()
                    
                    if sw.value() == 0:
                        while sw.value() == 0:
                            time.sleep_ms(10)
                        current_menu = "MAIN"
                        break
                    
                    if modulation_hz > 0:
                        if time.ticks_diff(current_time, last_toggle_time) >= half_period_ms:
                            led_state = not led_state
                            last_toggle_time = current_time
                            send_via_dma(buf_on if led_state else buf_off)
                    else:
                        send_via_dma(buf_on)
                    
                    remaining_sec = max(0.0, (duration_ms - elapsed) / 1000.0)
                    mod_status = f"{modulation_hz:.1f}Hz" if matrix_modulation_enabled else "Off"
                    
                    if display:
                        display.fill(0)
                        display.text("--- RUN ---", 0, 0, 1)
                        display.text(f"Coul: {col_name}", 0, 16, 1)
                        display.text(f"Mod: {mod_status}", 0, 28, 1)
                        display.text(f"Rest: {remaining_sec:.1f}s", 0, 40, 1)
                        display.text(f"Int: {intensity}%", 0, 52, 1)
                        display.show()
                        
                    time.sleep_ms(50)
                    elapsed += 50
                    
                if current_menu != "RUN_PROG":
                    break
            
            if current_menu == "RUN_PROG":
                time.sleep(1)
                current_menu = "MAIN"
                
    except Exception as e:
        if display:
            display.fill(0)
            display.text("ERREUR RUN", 10, 22, 1)
            display.text(str(e)[:16], 0, 36, 1)
            display.show()
        time.sleep(2.5)
        current_menu = "MAIN"
        
    # --- RESTAURATION DE L'ÉTAT INITIAL À LA SORTIE ---
    color_index = saved_color_index
    modulation_hz = saved_modulation_hz
    intensity = saved_intensity
    matrix_modulation_enabled = saved_matrix_mod
    
    mod_pwm.duty_u16(0)
    dma_timer.deinit()
    update_pixel_buffers()
    update_timer_config()
    update_display()
    
def update_display():
    if not display:
        return
    display.fill(0)
    
    if current_menu == "MAIN":
        c_name, _ = COLORS[color_index]
        menu_items = [
            f"Coul:{c_name}",
            f"Mod:{modulation_hz:.1f}Hz" if modulation_hz > 0.0 else f"Mod:Off",
            f"Mat.Mod:{'OUI' if matrix_modulation_enabled else 'NON'}",
            f"Intens:{intensity}%",
            "Calibration RVB >",
            f"Prog:{selected_program_file[:8]}",
            "Lancer Programme",
            "[SAUVER]"
        ]
        
        total_items = len(menu_items)
        start_item = max(0, min(cursor_pos - 3, total_items - 7))
        
        for i in range(7):
            idx = start_item + i
            if idx < total_items:
                prefix = "> " if cursor_pos == idx else "  "
                display.text(f"{prefix}{menu_items[idx]}", 0, i * 9, 1)
                
    elif current_menu == "CALIB":
        cc_name, _ = COLORS[calib_color_index]
        calib_items = [
            f"Test Coul:{cc_name}",
            f"Bal. R : {r_balance}%",
            f"Bal. G : {g_balance}%",
            f"Bal. B : {b_balance}%",
            "[RETOUR]"
        ]
        
        display.text("--- CALIBRATION RVB ---", 0, 0, 1)
        for i in range(len(calib_items)):
            prefix = "> " if calib_cursor_pos == i else "  "
            display.text(f"{prefix}{calib_items[i]}", 0, (i + 1) * 10, 1)
            
    elif current_menu == "SEL_PROG":
        txt_files = get_txt_files()
        display.text("--- CHOIX PROGRAMME ---", 0, 0, 1)
        total_files = len(txt_files)
        start_item = max(0, min(file_cursor_pos - 2, total_files - 5))
        
        for i in range(5):
            idx = start_item + i
            if idx < total_files:
                prefix = "> " if file_cursor_pos == idx else "  "
                display.text(f"{prefix}{txt_files[idx][:14]}", 0, (i + 1) * 11, 1)
            
    if is_editing:
        display.fill_rect(122, 1, 5, 5, 1)
    else:
        display.pixel(124, 3, 1)
        
    display.show()

# --- INITIALISATION FINALE ---
update_pixel_buffers()
update_timer_config()
update_display()

# --- BOUCLE PRINCIPALE ---
try:
    while True:
        current_time = time.ticks_ms()
        current_clk = clk.value()
        
        if current_clk != last_clk_val:
            if current_clk == 0:
                clockwise = (dt.value() == 1)
                delta_t = time.ticks_diff(current_time, last_click_time)
                last_click_time = current_time
                
                step = 1.0 if delta_t < 120 else 0.1
                
                if current_menu == "MAIN":
                    if not is_editing:
                        if clockwise:
                            cursor_pos = (cursor_pos + 1) % 8
                        else:
                            cursor_pos = (cursor_pos - 1) % 8
                    else:
                        if cursor_pos == 0:
                            color_index = (color_index + (1 if clockwise else -1)) % len(COLORS)
                            update_pixel_buffers()
                            send_via_dma(buf_on if (mod_state or not matrix_modulation_enabled) else buf_off)
                        elif cursor_pos == 1:
                            modulation_hz = max(0.0, min(50.0, round(modulation_hz + (step if clockwise else -step), 1)))
                            update_timer_config()
                        elif cursor_pos == 2:
                            matrix_modulation_enabled = not matrix_modulation_enabled
                            update_timer_config()
                        elif cursor_pos == 3:
                            intensity = max(0, min(100, intensity + (5 if clockwise else -5)))
                            update_pixel_buffers()
                            send_via_dma(buf_on if (mod_state or not matrix_modulation_enabled) else buf_off)
                        elif cursor_pos == 5:
                            txt_list = get_txt_files()
                            if selected_program_file in txt_list:
                                file_cursor_pos = txt_list.index(selected_program_file)
                            else:
                                file_cursor_pos = 0
                            current_menu = "SEL_PROG"
                            is_editing = False
                            update_display()
                            
                elif current_menu == "CALIB":
                    if not is_editing:
                        if clockwise:
                            calib_cursor_pos = (calib_cursor_pos + 1) % 5
                        else:
                            calib_cursor_pos = (calib_cursor_pos - 1) % 5
                    else:
                        if calib_cursor_pos == 0:
                            calib_color_index = (calib_color_index + (1 if clockwise else -1)) % len(COLORS)
                            update_pixel_buffers()
                            send_via_dma(buf_on)
                        elif calib_cursor_pos == 1:
                            r_balance = max(0, min(120, r_balance + (5 if clockwise else -5)))
                            update_pixel_buffers()
                            send_via_dma(buf_on)
                        elif calib_cursor_pos == 2:
                            g_balance = max(0, min(120, g_balance + (5 if clockwise else -5)))
                            update_pixel_buffers()
                            send_via_dma(buf_on)
                        elif calib_cursor_pos == 3:
                            b_balance = max(0, min(120, b_balance + (5 if clockwise else -5)))
                            update_pixel_buffers()
                            send_via_dma(buf_on)
                            
                elif current_menu == "SEL_PROG":
                    txt_list = get_txt_files()
                    if clockwise:
                        file_cursor_pos = (file_cursor_pos + 1) % len(txt_list)
                    else:
                        file_cursor_pos = (file_cursor_pos - 1) % len(txt_list)
                            
                update_display()
            last_clk_val = current_clk

        if sw.value() == 0:
            if press_start_time == 0:
                press_start_time = current_time
        else:
            if press_start_time > 0:
                press_duration = time.ticks_diff(current_time, press_start_time)
                press_start_time = 0
                
                if press_duration > 1000:
                    if current_menu == "MAIN":
                        matrix_power = not matrix_power
                        update_pixel_buffers()
                        update_timer_config()
                    elif current_menu == "CALIB":
                        current_menu = "MAIN"
                        is_editing = False
                        update_pixel_buffers()
                        send_via_dma(buf_on if (mod_state or not matrix_modulation_enabled) else buf_off)
                    elif current_menu == "SEL_PROG":
                        current_menu = "MAIN"
                    update_display()
                    
                elif press_duration > 50:
                    if current_menu == "MAIN":
                        if cursor_pos == 4:
                            current_menu = "CALIB"
                            calib_cursor_pos = 0
                            is_editing = False
                            calib_color_index = color_index
                            update_pixel_buffers()
                            send_via_dma(buf_on)
                        elif cursor_pos == 5:
                            txt_list = get_txt_files()
                            if selected_program_file in txt_list:
                                file_cursor_pos = txt_list.index(selected_program_file)
                            else:
                                file_cursor_pos = 0
                            current_menu = "SEL_PROG"
                            is_editing = False
                            update_display()
                        elif cursor_pos == 6:
                            run_program(selected_program_file)
                        elif cursor_pos == 7:
                            save_config()
                            is_editing = False
                        else:
                            is_editing = not is_editing
                            
                    elif current_menu == "CALIB":
                        if calib_cursor_pos == 4:
                            current_menu = "MAIN"
                            is_editing = False
                            update_pixel_buffers()
                            send_via_dma(buf_on if (mod_state or not matrix_modulation_enabled) else buf_off)
                        else:
                            is_editing = not is_editing
                            
                    elif current_menu == "SEL_PROG":
                        txt_list = get_txt_files()
                        if 0 <= file_cursor_pos < len(txt_list):
                            selected_program_file = txt_list[file_cursor_pos]
                        current_menu = "MAIN"
                        is_editing = False
                            
                    update_display()
                    
        time.sleep_ms(2)

except Exception:
    pass