from tkinter import messagebox, simpledialog
import tkinter as tk
import numpy as np
try:
    import sounddevice as sd
except Exception:
    sd = None
import os
import platform
import re
import string
import threading
import time
import json
import math
import statistics
import random
import datetime
import urllib.request
import urllib.parse
import urllib.error
import subprocess
import shutil
import logging
from pathlib import Path
from collections import deque, defaultdict

print("hello from abdocode")

_used_shortcuts = {}
_enabled = True
_clipboard_history = []
_timer_registry = {}
def beep(freq=440, duration=0.5, volume=0.5):
    fs = 44100  # جودة الصوت
    t = np.linspace(0, duration, int(fs * duration))
    
    wave = volume * np.sin(2 * np.pi * freq * t)
    
    sd.play(wave, fs)
    sd.wait()

# =========================
# SOUND BACKEND ABSTRACTION
# =========================

class SoundPlayer:
    def __init__(self):
        self.backend = None
        self._sd = None
        self._sa = None
        self._winsound = None
        try:
            import sounddevice as _sd
            self._sd = _sd
            self.backend = 'sounddevice'
        except Exception:
            try:
                import simpleaudio as _sa
                self._sa = _sa
                self.backend = 'simpleaudio'
            except Exception:
                if platform.system() == 'Windows':
                    try:
                        import winsound as _winsound
                        self._winsound = _winsound
                        self.backend = 'winsound'
                    except Exception:
                        self.backend = 'stub'
                else:
                    self.backend = 'stub'

    def play_wave(self, wave, fs=44100):
        """Play a numpy waveform (float32 or float64, -1..1) with selected backend."""
        if isinstance(wave, np.ndarray) and wave.dtype.kind in ('f', 'i'):
            arr = wave
        else:
            arr = np.array(wave, dtype=np.float32)

        if self.backend == 'sounddevice' and self._sd is not None:
            self._sd.play(arr.astype(np.float32), fs)
            self._sd.wait()
            return

        if self.backend == 'simpleaudio' and self._sa is not None:
            # convert float -1..1 to PCM16
            pcm = np.clip(arr, -1.0, 1.0)
            pcm16 = (pcm * 32767).astype(np.int16)
            try:
                play_obj = self._sa.play_buffer(pcm16.tobytes(), 1, 2, fs)
                play_obj.wait_done()
            except Exception:
                pass
            return

        if self.backend == 'winsound' and self._winsound is not None:
            # winsound only supports simple Beep(frequency, duration_ms)
            # fall back to playing the dominant frequency of the wave
            try:
                # estimate frequency by zero crossings
                s = arr.astype(np.float32)
                if s.size == 0:
                    return
                # naive frequency estimate
                z = np.where(np.diff(np.signbit(s)))[0]
                if len(z) > 1:
                    periods = np.diff(z) / float(fs)
                    freq = int(1.0 / np.mean(periods)) if np.mean(periods) > 0 else 440
                else:
                    freq = 440
                duration_ms = int(len(arr) / fs * 1000)
                if freq < 37: freq = 37
                if freq > 32767: freq = 32767
                self._winsound.Beep(int(freq), max(1, duration_ms))
            except Exception:
                pass
            return

        # stub: no audio backend available — try fallback sleep for duration
        try:
            duration = len(arr) / float(fs)
            sd.sleep(int(duration * 1000)) if 'sd' in globals() else time.sleep(duration)
        except Exception:
            pass


# single global player used by playback helper functions
_sound_player = SoundPlayer()


def available_sound_backends():
    backends = []
    if sd is not None:
        backends.append('sounddevice')
    try:
        import simpleaudio  # type: ignore
        backends.append('simpleaudio')
    except Exception:
        pass
    if platform.system() == 'Windows':
        try:
            import winsound  # type: ignore
            backends.append('winsound')
        except Exception:
            pass
    if not backends:
        backends.append('stub')
    return backends


def set_sound_backend(name):
    global _sound_player
    name = str(name) if name is not None else None
    if name is None:
        return False
    # create a new player and try to force backend selection if possible
    player = SoundPlayer()
    if player.backend == name:
        _sound_player = player
        return True
    # if requested backend isn't the detected one, try to load specific modules
    try:
        if name == 'simpleaudio':
            import simpleaudio as _sa
            player._sa = _sa
            player.backend = 'simpleaudio'
        elif name == 'sounddevice':
            import sounddevice as _sd
            player._sd = _sd
            player.backend = 'sounddevice'
        elif name == 'winsound' and platform.system() == 'Windows':
            import winsound as _winsound
            player._winsound = _winsound
            player.backend = 'winsound'
        else:
            return False
        _sound_player = player
        return True
    except Exception:
        return False


def sound_backend_info():
    return {
        'detected': getattr(_sound_player, 'backend', None),
        'available': available_sound_backends(),
    }

# =========================
# CORE SAFETY FUNCTION
# =========================

def _safe_event(root, event):
    widget = root.focus_get()
    if widget:
        try:
            widget.event_generate(event)
        except:
            pass


# =========================
# BASIC SHORTCUT SYSTEM
# =========================

def add_shortcut(root, key, event):
    if key in _used_shortcuts:
        messagebox.showwarning(
            "Hotkey Warning",
            f"{key} already used for { _used_shortcuts[key] }"
        )
        return False

    def handler(e):
        if not _enabled:
            return
        _safe_event(root, event)

    root.bind_all(key, handler)
    _used_shortcuts[key] = event
    return True


def remove_shortcut(root, key):
    if key in _used_shortcuts:
        root.unbind_all(key)
        del _used_shortcuts[key]
        return True
    return False


def disable_all():
    global _enabled
    _enabled = False


def enable_all():
    global _enabled
    _enabled = True


def reset_all(root):
    for key in list(_used_shortcuts.keys()):
        root.unbind_all(key)
    _used_shortcuts.clear()


def list_shortcuts():
    return _used_shortcuts.copy()
def copy(root): add_shortcut(root, "<Control-c>", "<<Copy>>")
def paste(root): add_shortcut(root, "<Control-v>", "<<Paste>>")
def cut(root): add_shortcut(root, "<Control-x>", "<<Cut>>")
def select_all(root): add_shortcut(root, "<Control-a>", "<<SelectAll>>")
def undo(root): add_shortcut(root, "<Control-z>", "<<Undo>>")
def redo(root): add_shortcut(root, "<Control-y>", "<<Redo>>")
def find(root): add_shortcut(root, "<Control-f>", "<<Find>>")
def replace(root): add_shortcut(root, "<Control-h>", "<<Replace>>")
def goto(root): add_shortcut(root, "<Control-g>", "<<Goto>>")
def duplicate(root): add_shortcut(root, "<Control-d>", "<<Duplicate>>")
def select_line(root): add_shortcut(root, "<Control-l>", "<<SelectLine>>")
def delete_word(root): add_shortcut(root, "<Control-BackSpace>", "<<DeleteWord>>")
def delete_line(root): add_shortcut(root, "<Control-Delete>", "<<DeleteLine>>")
def cut_alt(root): add_shortcut(root, "<Shift-Delete>", "<<Cut>>")
def home(root): add_shortcut(root, "<Home>", "<<Home>>")
def end(root): add_shortcut(root, "<End>", "<<End>>")
def word_left(root): add_shortcut(root, "<Control-Left>", "<<WordLeft>>")
def word_right(root): add_shortcut(root, "<Control-Right>", "<<WordRight>>")
def page_up(root): add_shortcut(root, "<Control-Up>", "<<PageUp>>")
def page_down(root): add_shortcut(root, "<Control-Down>", "<<PageDown>>")
def select_left(root): add_shortcut(root, "<Shift-Left>", "<<SelectLeft>>")
def select_right(root): add_shortcut(root, "<Shift-Right>", "<<SelectRight>>")
def select_up(root): add_shortcut(root, "<Shift-Up>", "<<SelectUp>>")
def select_down(root): add_shortcut(root, "<Shift-Down>", "<<SelectDown>>")
def select_word_left(root): add_shortcut(root, "<Control-Shift-Left>", "<<SelectWordLeft>>")
def select_word_right(root): add_shortcut(root, "<Control-Shift-Right>", "<<SelectWordRight>>")
def save(root): add_shortcut(root, "<Control-s>", "<<Save>>")
def open_file(root): add_shortcut(root, "<Control-o>", "<<Open>>")
def new_file(root): add_shortcut(root, "<Control-n>", "<<New>>")
def print_file(root): add_shortcut(root, "<Control-p>", "<<Print>>")
def refresh(root): add_shortcut(root, "<F5>", "<<Refresh>>")
def use_all(root):
    copy(root)
    paste(root)
    cut(root)
    select_all(root)
    undo(root)
    redo(root)
    find(root)
    replace(root)
    goto(root)
    duplicate(root)
    select_line(root)
    delete_word(root)
    delete_line(root)
    cut_alt(root)
    home(root)
    end(root)
    word_left(root)
    word_right(root)
    page_up(root)
    page_down(root)
    select_left(root)
    select_right(root)
    select_up(root)
    select_down(root)
    select_word_left(root)
    select_word_right(root)
    save(root)
    open_file(root)
    new_file(root)
    print_file(root)
    refresh(root)
def show_all_shortcuts():
    shortcuts = list_shortcuts()
    if not shortcuts:
        messagebox.showinfo("Hotkeys", "No shortcuts defined.")
        return

    msg = "Current Shortcuts:\n\n"
    for key, event in shortcuts.items():
        msg += f"{key} -> {event}\n"

    messagebox.showinfo("Hotkeys", msg)
def shortcuts_popup_list(root):
    # root may be an event from a binding
    if getattr(root, 'widget', None) is not None and hasattr(root, 'x_root'):
        event = root
        root = event.widget

    shortcuts = list_shortcuts()
    if not shortcuts:
        messagebox.showinfo("Hotkeys", "No shortcuts defined.")
        return
    # Create a searchable popup window listing shortcuts with actions
    popup = tk.Toplevel(root)
    popup.title('Shortcuts')
    popup.transient(root)
    popup.grab_set()

    frame = tk.Frame(popup, padx=8, pady=8)
    frame.pack(fill='both', expand=True)

    filter_var = tk.StringVar()
    tk.Label(frame, text='Filter:').pack(anchor='w')
    filter_entry = tk.Entry(frame, textvariable=filter_var)
    filter_entry.pack(fill='x', pady=(0, 6))

    list_frame = tk.Frame(frame)
    list_frame.pack(fill='both', expand=True)
    shortcuts_list = tk.Listbox(list_frame, width=60, height=14)
    shortcuts_list.pack(side='left', fill='both', expand=True)
    scrollbar = tk.Scrollbar(list_frame, command=shortcuts_list.yview)
    scrollbar.pack(side='right', fill='y')
    shortcuts_list.config(yscrollcommand=scrollbar.set)

    # internal mapping of displayed items -> (key, event)
    displayed = []

    def update_list(*args):
        displayed.clear()
        shortcuts_list.delete(0, tk.END)
        q = filter_var.get().strip().lower()
        for k, ev in list_shortcuts().items():
            text = f"{k} -> {ev}"
            if not q or q in text.lower():
                displayed.append((k, ev))
                shortcuts_list.insert(tk.END, text)

    def copy_selected():
        sel = shortcuts_list.curselection()
        if not sel:
            return
        idx = sel[0]
        k, ev = displayed[idx]
        txt = f"{k} -> {ev}"
        try:
            root.clipboard_clear()
            root.clipboard_append(txt)
            root.update()
        except Exception:
            pass

    def remove_selected():
        sel = shortcuts_list.curselection()
        if not sel:
            return
        idx = sel[0]
        k, ev = displayed[idx]
        try:
            root.unbind_all(k)
        except Exception:
            pass
        _used_shortcuts.pop(k, None)
        update_list()

    def trigger_selected():
        sel = shortcuts_list.curselection()
        if not sel:
            return
        idx = sel[0]
        k, ev = displayed[idx]
        try:
            _safe_event(root, ev)
        except Exception:
            try:
                root.event_generate(ev)
            except Exception:
                pass

    def play_beep_selected():
        sel = shortcuts_list.curselection()
        if not sel:
            return
        idx = sel[0]
        k, ev = displayed[idx]
        # use the event name (sanitized) to create an audible signature
        name = re.sub(r"[^A-Za-z0-9]", '_', f"{k}_{ev}")
        try:
            play_name_beep(name)
        except Exception:
            pass

    btn_frame = tk.Frame(frame)
    btn_frame.pack(fill='x', pady=(8, 0))
    tk.Button(btn_frame, text='Copy', command=copy_selected).pack(side='left', padx=4)
    tk.Button(btn_frame, text='Remove', command=remove_selected).pack(side='left', padx=4)
    tk.Button(btn_frame, text='Trigger', command=trigger_selected).pack(side='left', padx=4)
    tk.Button(btn_frame, text='Play Beep', command=play_beep_selected).pack(side='left', padx=4)
    tk.Button(btn_frame, text='Close', command=popup.destroy).pack(side='right')

    filter_var.trace_add('write', update_list)
    update_list()
    filter_entry.focus_set()


def sounds_popup_list(root):
    """Show a searchable popup listing registered sounds from `sound_map`.

    Clicking a sound button will play the sound (non-blocking).
    """
    # root may be an event from a binding
    if getattr(root, 'widget', None) is not None and hasattr(root, 'x_root'):
        event = root
        root = event.widget

    if not sound_map:
        messagebox.showinfo('Sounds', 'No sounds registered.')
        return

    popup = tk.Toplevel(root)
    popup.title('Sounds')
    popup.transient(root)
    popup.grab_set()

    frame = tk.Frame(popup, padx=8, pady=8)
    frame.pack(fill='both', expand=True)

    filter_var = tk.StringVar()
    tk.Label(frame, text='Filter:').pack(anchor='w')
    filter_entry = tk.Entry(frame, textvariable=filter_var)
    filter_entry.pack(fill='x', pady=(0, 6))

    list_frame = tk.Frame(frame)
    list_frame.pack(fill='both', expand=True)
    canvas = tk.Canvas(list_frame)
    scrollbar = tk.Scrollbar(list_frame, orient='vertical', command=canvas.yview)
    scrollable = tk.Frame(canvas)

    scrollable.bind(
        "<Configure>",
        lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
    )
    canvas.create_window((0, 0), window=scrollable, anchor='nw')
    canvas.configure(yscrollcommand=scrollbar.set)
    canvas.pack(side='left', fill='both', expand=True)
    scrollbar.pack(side='right', fill='y')

    displayed = []

    def update_list(*args):
        for w in scrollable.winfo_children():
            w.destroy()
        displayed.clear()
        q = filter_var.get().strip().lower()
        keys = sorted(sound_map.keys())
        for k in keys:
            if q and q not in k.lower():
                continue
            displayed.append(k)
            btn = tk.Button(scrollable, text=k, anchor='w', width=40,
                            command=lambda _k=k: threading.Thread(target=_play_sound_thread, args=(_k,), daemon=True).start())
            btn.pack(fill='x', pady=2)

    def _play_sound_thread(key):
        try:
            fn = sound_map.get(key)
            if callable(fn):
                # call directly (may block) — use thread to avoid UI freeze
                fn()
            else:
                play(key)
        except Exception:
            try:
                play(key)
            except Exception:
                pass

    btn_frame = tk.Frame(frame)
    btn_frame.pack(fill='x', pady=(8, 0))
    tk.Button(btn_frame, text='Close', command=popup.destroy).pack(side='right')

    filter_var.trace_add('write', update_list)
    update_list()
    filter_entry.focus_set()


# Sequence detection for Ctrl+S then '.' to open sounds popup
_ctrl_s_time = None

def _on_ctrl_s_event(e):
    global _ctrl_s_time
    _ctrl_s_time = time.time()

def _on_keypress_for_sequence(e):
    global _ctrl_s_time
    try:
        ch = getattr(e, 'char', '')
    except Exception:
        ch = ''
    if ch == '.' and _ctrl_s_time and (time.time() - _ctrl_s_time) < 1.0:
        # find a root window to attach popup to
        root = e.widget if hasattr(e, 'widget') and isinstance(e.widget, tk.Misc) else None
        if root is None:
            for w in tk._default_root.children.values() if tk._default_root else []:
                root = tk._default_root
                break
        try:
            sounds_popup_list(root or tk._default_root)
        except Exception:
            pass
        _ctrl_s_time = None


def enable_shortcuts_ui(root):
    """Enable right-click context and Ctrl+S + '.' sequence on given `root`.

    This function is library-friendly: it only binds handlers and provides popups.
    """
    # bind right-click to show shortcuts popup
    try:
        root.bind_all('<Button-3>', shortcuts_popup_list)
    except Exception:
        pass
    # bind control-s detection and keypress for '.'
    try:
        root.bind_all('<Control-s>', _on_ctrl_s_event)
        root.bind_all('<Key>', _on_keypress_for_sequence)
    except Exception:
        pass


# =========================
# CLIPBOARD UTILITIES
# =========================

def _add_clipboard_history(text):
    text = str(text)
    if text and (_clipboard_history == [] or _clipboard_history[-1] != text):
        _clipboard_history.append(text)


def write_clipboard(root, text):
    try:
        root.clipboard_clear()
        root.clipboard_append(str(text))
        root.update()
        _add_clipboard_history(text)
        return True
    except Exception:
        return False


def read_clipboard(root):
    try:
        return root.clipboard_get()
    except Exception:
        return ""


def clear_clipboard(root):
    try:
        root.clipboard_clear()
        root.update()
        return True
    except Exception:
        return False


def get_clipboard_history():
    return list(_clipboard_history)


def paste_clipboard_history_item(root, index):
    try:
        text = _clipboard_history[index]
        return write_clipboard(root, text)
    except Exception:
        return False


# =========================
# FILE UTILITIES
# =========================

def read_text_file(path, encoding='utf-8'):
    path_obj = Path(path)
    with path_obj.open('r', encoding=encoding) as file:
        return file.read()


def write_text_file(path, text, encoding='utf-8'):
    path_obj = Path(path)
    path_obj.parent.mkdir(parents=True, exist_ok=True)
    with path_obj.open('w', encoding=encoding) as file:
        file.write(str(text))


def append_text_file(path, text, encoding='utf-8'):
    path_obj = Path(path)
    path_obj.parent.mkdir(parents=True, exist_ok=True)
    with path_obj.open('a', encoding=encoding) as file:
        file.write(str(text))


def list_files(folder, extension=None, recursive=False):
    base = Path(folder)
    if recursive:
        items = base.rglob('*')
    else:
        items = base.glob('*')
    files = [str(p) for p in items if p.is_file()]
    if extension:
        return [f for f in files if f.lower().endswith(extension.lower())]
    return files


def find_files(folder, pattern, recursive=True, extension=None):
    base = Path(folder)
    matcher = re.compile(pattern)
    paths = base.rglob('*') if recursive else base.glob('*')
    matches = []
    for p in paths:
        if p.is_file():
            if matcher.search(str(p)):
                if extension is None or str(p).lower().endswith(extension.lower()):
                    matches.append(str(p))
    return matches


# =========================
# TEXT UTILITIES
# =========================

def normalize_whitespace(text):
    return re.sub(r'\s+', ' ', str(text)).strip()


def count_words(text):
    return len(normalize_whitespace(text).split(' ')) if normalize_whitespace(text) else 0


def to_title_case(text):
    return str(text).title()


def snake_to_camel(text):
    parts = str(text).split('_')
    return parts[0].lower() + ''.join(word.capitalize() for word in parts[1:])


def camel_to_snake(text):
    s = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', str(text))
    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s).lower()


def remove_punctuation(text):
    return str(text).translate(str.maketrans('', '', string.punctuation))


# =========================
# TIMER & SCHEDULER UTILITIES
# =========================

def set_timeout(callback, delay, *args, **kwargs):
    timer = threading.Timer(delay, callback, args=args, kwargs=kwargs)
    timer.daemon = True
    timer.start()
    timer_id = id(timer)
    _timer_registry[timer_id] = timer
    return timer_id


def _interval_runner(timer_id, callback, interval, args, kwargs):
    if timer_id not in _timer_registry:
        return
    callback(*args, **kwargs)
    timer = threading.Timer(interval, _interval_runner, args=(timer_id, callback, interval, args, kwargs))
    timer.daemon = True
    _timer_registry[timer_id] = timer
    timer.start()


def set_interval(callback, interval, *args, **kwargs):
    timer_id = int(time.time() * 1000000)
    timer = threading.Timer(interval, _interval_runner, args=(timer_id, callback, interval, args, kwargs))
    timer.daemon = True
    _timer_registry[timer_id] = timer
    timer.start()
    return timer_id


def cancel_timer(timer_id):
    timer = _timer_registry.pop(timer_id, None)
    if timer:
        timer.cancel()
        return True
    return False


def active_timers():
    return list(_timer_registry.keys())


# =========================
# SYSTEM UTILITIES
# =========================

def get_system_info():
    try:
        user = os.getlogin()
    except Exception:
        user = None
    return {
        'platform': platform.system(),
        'release': platform.release(),
        'version': platform.version(),
        'machine': platform.machine(),
        'processor': platform.processor(),
        'python_version': platform.python_version(),
        'cwd': os.getcwd(),
        'user': user,
    }


def get_environment_variables():
    return dict(os.environ)


def open_folder(path):
    path_obj = Path(path)
    if not path_obj.exists():
        return False
    try:
        if platform.system() == 'Windows':
            os.startfile(str(path_obj))
        elif platform.system() == 'Darwin':
            os.system(f'open "{path_obj}"')
        else:
            os.system(f'xdg-open "{path_obj}"')
        return True
    except Exception:
        return False


def get_file_size(path):
    path_obj = Path(path)
    return path_obj.stat().st_size if path_obj.exists() else 0


# =========================
# UI CONVENIENCE SHORTCUTS
# =========================

def show_info(title, message):
    messagebox.showinfo(title, str(message))


def show_warning(title, message):
    messagebox.showwarning(title, str(message))


def show_error(title, message):
    messagebox.showerror(title, str(message))


def ask_yes_no(title, message):
    return messagebox.askyesno(title, str(message))


def ask_input(title, prompt, default=''):
    return simpledialog.askstring(title, prompt, initialvalue=default)


def simple_popup(root, title, message):
    popup = tk.Toplevel(root)
    popup.title(title)
    label = tk.Label(popup, text=str(message), padx=10, pady=10)
    label.pack()
    btn = tk.Button(popup, text='OK', command=popup.destroy, padx=8, pady=4)
    btn.pack(pady=10)
    popup.transient(root)
    popup.grab_set()
    root.wait_window(popup)


def note_to_frequency(note):
    note = note.strip()
    if note.upper() == "REST":
        return 0.0

    octave = int(note[-1])
    name = note[:-1]
    if len(name) > 1 and name[-1] in "#b":
        base_name = name
    else:
        base_name = name

    semitone_map = {
        'C': 0,  'C#': 1, 'Db': 1,
        'D': 2,  'D#': 3, 'Eb': 3,
        'E': 4,  'F': 5,  'F#': 6,
        'Gb': 6, 'G': 7,  'G#': 8,
        'Ab': 8, 'A': 9,  'A#': 10,
        'Bb': 10, 'B': 11,
    }

    semitone = semitone_map.get(base_name, 0) + (octave + 1) * 12
    return 440.0 * 2 ** ((semitone - 69) / 12.0)


def play_tone(note, duration=0.2, volume=0.5, waveform='sine'):
    freq = note_to_frequency(note) if isinstance(note, str) else note
    if freq == 0:
        try:
            time.sleep(duration)
        except Exception:
            pass
        return

    fs = 44100
    t = np.linspace(0, duration, int(fs * duration), endpoint=False)

    if waveform == 'square':
        wave = np.sign(np.sin(2 * np.pi * freq * t))
    elif waveform == 'triangle':
        wave = 2 * np.abs(2 * ((freq * t) % 1) - 1) - 1
    else:
        wave = np.sin(2 * np.pi * freq * t)

    envelope = np.ones_like(wave)
    attack = min(0.02, duration * 0.2)
    release = min(0.03, duration * 0.2)
    attack_len = int(fs * attack)
    release_len = int(fs * release)
    if attack_len > 0:
        envelope[:attack_len] = np.linspace(0.0, 1.0, attack_len)
    if release_len > 0:
        envelope[-release_len:] = np.linspace(1.0, 0.0, release_len)

    wave = volume * envelope * wave
    try:
        _sound_player.play_wave(wave.astype(np.float32), fs=fs)
    except Exception:
        # final fallback to sounddevice if available
        try:
            if sd is not None:
                sd.play(wave.astype(np.float32), fs)
                sd.wait()
            else:
                time.sleep(duration)
        except Exception:
            # if nothing works, sleep for duration
            try:
                time.sleep(duration)
            except Exception:
                pass


def play_notes(sequence, tempo=120, volume=0.4, waveform='sine'):
    beat = 60.0 / tempo
    for note, length in sequence:
        play_tone(note, duration=length * beat, volume=volume, waveform=waveform)


sound_map = {}

# better wave generation helpers for richer sounds

def _generate_wave(note, duration=0.24, waveform='sine', fs=44100):
    freq = note_to_frequency(note) if isinstance(note, str) else note
    if freq == 0:
        return np.zeros(int(fs * duration), dtype=np.float32)
    t = np.linspace(0, duration, int(fs * duration), endpoint=False)
    if waveform == 'square':
        wave = np.sign(np.sin(2 * np.pi * freq * t))
    elif waveform == 'triangle':
        wave = 2 * np.abs(2 * ((freq * t) % 1) - 1) - 1
    elif waveform == 'sawtooth':
        wave = 2 * ((freq * t) % 1) - 1
    elif waveform == 'pulse':
        wave = np.where(np.sin(2 * np.pi * freq * t) >= 0, 1.0, -0.4)
    else:
        wave = np.sin(2 * np.pi * freq * t)
    attack = min(0.02, duration * 0.15)
    release = min(0.03, duration * 0.15)
    envelope = np.ones_like(wave)
    attack_len = int(fs * attack)
    release_len = int(fs * release)
    if attack_len > 0:
        envelope[:attack_len] = np.linspace(0.0, 1.0, attack_len)
    if release_len > 0:
        envelope[-release_len:] = np.linspace(1.0, 0.0, release_len)
    return wave * envelope


def _mix_waves(waves):
    if not waves:
        return np.array([], dtype=np.float32)
    max_len = max(len(w) for w in waves)
    mix = np.zeros(max_len, dtype=np.float32)
    for w in waves:
        mix[:len(w)] += w
    peak = np.max(np.abs(mix))
    return mix / peak if peak > 0 else mix


def play_chord(notes, duration=0.28, volume=0.4, waveform='sine'):
    waves = [_generate_wave(note, duration, waveform) for note in notes]
    mixed = _mix_waves(waves)
    _sound_player.play_wave((mixed * volume).astype(np.float32), fs=44100)


def play_bell(note, duration=0.36, volume=0.38):
    base_wave = _generate_wave(note, duration, 'sine')
    octave_wave = _generate_wave(note_to_frequency(note) * 2, duration, 'triangle')
    harmonic_wave = _generate_wave(note_to_frequency(note) * 3, duration, 'sine')
    bell = _mix_waves([0.7 * base_wave, 0.18 * octave_wave, 0.12 * harmonic_wave])
    _sound_player.play_wave((bell * volume).astype(np.float32), fs=44100)


def play_swipe(notes, duration=0.18, volume=0.36, waveform='sawtooth'):
    for note in notes:
        play_tone(note, duration=duration, volume=volume, waveform=waveform)


def play_ping(notes, duration=0.14, volume=0.42, waveform='sine'):
    for note in notes:
        play_tone(note, duration=duration, volume=volume, waveform=waveform)


def play_sequence(sequence, tempo=120, volume=0.37, waveform='sine'):
    beat = 60.0 / tempo
    for note, length in sequence:
        play_tone(note, duration=length * beat, volume=volume, waveform=waveform)


sound_map["Victory"] = lambda: play_chord(['C5', 'E5', 'G5', 'C6'], duration=0.32, volume=0.45, waveform='triangle')
sound_map["Defeat"] = lambda: play_bell('A3', duration=0.5, volume=0.35)
sound_map["Win"] = lambda: play_sequence([('G4', 0.18), ('B4', 0.18), ('D5', 0.24), ('G5', 0.3)], tempo=160, volume=0.44, waveform='triangle')
sound_map["Lose"] = lambda: play_sequence([('D4', 0.3), ('C4', 0.25), ('A3', 0.4)], tempo=92, volume=0.34, waveform='triangle')
sound_map["Click"] = lambda: play_ping(['C6'], duration=0.08, volume=0.5, waveform='sine')
sound_map["Hover"] = lambda: play_ping(['E5', 'D5'], duration=0.07, volume=0.42, waveform='sine')
sound_map["Select"] = lambda: play_ping(['G4', 'B4'], duration=0.1, volume=0.44, waveform='square')
sound_map["Cancel"] = lambda: play_tone('D4', duration=0.22, volume=0.43, waveform='triangle')
sound_map["Start"] = lambda: play_sequence([('E4', 0.16), ('G4', 0.16), ('C5', 0.3)], tempo=150, volume=0.42, waveform='sine')
sound_map["Finish"] = lambda: play_chord(['C5', 'E5', 'G5'], duration=0.36, volume=0.44, waveform='sine')
sound_map["LevelUp"] = lambda: play_sequence([('C4', 0.16), ('E4', 0.16), ('G4', 0.18), ('C5', 0.28)], tempo=170, volume=0.45, waveform='triangle')
sound_map["LevelDown"] = lambda: play_sequence([('G4', 0.22), ('E4', 0.22), ('C4', 0.3)], tempo=98, volume=0.35, waveform='triangle')
sound_map["Coin"] = lambda: play_ping(['C6', 'E6'], duration=0.09, volume=0.5, waveform='sine')
sound_map["Score"] = lambda: play_sequence([('G5', 0.16), ('A5', 0.16), ('C6', 0.22)], tempo=170, volume=0.42, waveform='sine')
sound_map["Bonus"] = lambda: play_sequence([('A4', 0.12), ('C5', 0.12), ('E5', 0.22)], tempo=180, volume=0.44, waveform='triangle')
sound_map["Combo"] = lambda: play_sequence([('B4', 0.11), ('D5', 0.11), ('F#5', 0.22)], tempo=190, volume=0.43, waveform='square')
sound_map["UltraWin"] = lambda: play_chord(['C5', 'E5', 'G5', 'B5'], duration=0.35, volume=0.46, waveform='triangle')
sound_map["GameOver"] = lambda: play_bell('A3', duration=0.42, volume=0.38)
sound_map["Restart"] = lambda: play_sequence([('D4', 0.2), ('F#4', 0.2), ('A4', 0.25)], tempo=130, volume=0.4, waveform='sawtooth')
sound_map["Pause"] = lambda: play_sequence([('E4', 0.32), ('D4', 0.32)], tempo=82, volume=0.35, waveform='triangle')
sound_map["Resume"] = lambda: play_sequence([('G4', 0.22), ('A4', 0.22)], tempo=150, volume=0.42, waveform='sine')
sound_map["Unlock"] = lambda: play_chord(['C5', 'D5', 'E5'], duration=0.28, volume=0.44, waveform='triangle')
sound_map["Lock"] = lambda: play_chord(['C4', 'A3'], duration=0.36, volume=0.36, waveform='sawtooth')
sound_map["Achievement"] = lambda: play_chord(['G4', 'B4', 'D5'], duration=0.28, volume=0.44, waveform='triangle')
sound_map["Boss"] = lambda: play_chord(['C2', 'G2', 'C3'], duration=0.55, volume=0.43, waveform='triangle')
sound_map["Alert"] = lambda: play_ping(['G5', 'E5'], duration=0.14, volume=0.43, waveform='square')
sound_map["Notification"] = lambda: play_ping(['E5', 'G5'], duration=0.18, volume=0.42, waveform='sine')
sound_map["Message"] = lambda: play_ping(['F5', 'E5'], duration=0.16, volume=0.41, waveform='triangle')
sound_map["IncomingCall"] = lambda: play_sequence([('A5', 0.22), ('C6', 0.22), ('E6', 0.22)], tempo=118, volume=0.45, waveform='sine')
sound_map["OutgoingCall"] = lambda: play_sequence([('G5', 0.22), ('B5', 0.22), ('D6', 0.22)], tempo=118, volume=0.45, waveform='sine')
sound_map["Email"] = lambda: play_sequence([('F5', 0.2), ('A5', 0.2), ('C6', 0.2)], tempo=140, volume=0.44, waveform='triangle')
sound_map["Success"] = lambda: play_chord(['C5', 'E5', 'G5'], duration=0.32, volume=0.45, waveform='sine')
sound_map["Fail"] = lambda: play_tone('D4', duration=0.28, volume=0.35, waveform='triangle')
sound_map["Error"] = lambda: play_tone('F4', duration=0.28, volume=0.35, waveform='square')
sound_map["Warning"] = lambda: play_sequence([('B4', 0.18), ('G4', 0.18)], tempo=110, volume=0.4, waveform='square')
sound_map["Connect"] = lambda: play_chord(['E5', 'G5'], duration=0.22, volume=0.43, waveform='triangle')
sound_map["Disconnect"] = lambda: play_chord(['B4', 'A4'], duration=0.24, volume=0.4, waveform='sawtooth')
sound_map["Loading"] = lambda: play_swipe(['C5', 'D5', 'E5'], duration=0.1, volume=0.36, waveform='sawtooth')
sound_map["Done"] = lambda: play_chord(['G5', 'C6'], duration=0.26, volume=0.44, waveform='sine')
sound_map["Save"] = lambda: play_sequence([('E5', 0.2), ('C5', 0.2), ('G5', 0.2)], tempo=140, volume=0.44, waveform='triangle')
sound_map["Delete"] = lambda: play_sequence([('C4', 0.18), ('E4', 0.18), ('G3', 0.2)], tempo=110, volume=0.42, waveform='sawtooth')
sound_map["Copy"] = lambda: play_ping(['G4', 'B4'], duration=0.16, volume=0.44, waveform='square')
sound_map["Paste"] = lambda: play_ping(['A4', 'C5'], duration=0.16, volume=0.44, waveform='triangle')
sound_map["Cut"] = lambda: play_ping(['F4', 'D4'], duration=0.16, volume=0.42, waveform='square')
sound_map["Refresh"] = lambda: play_swipe(['C5', 'D5', 'C5'], duration=0.1, volume=0.4, waveform='triangle')
sound_map["Sync"] = lambda: play_chord(['E4', 'G4', 'B4'], duration=0.2, volume=0.42, waveform='sine')
sound_map["Update"] = lambda: play_sequence([('A4', 0.2), ('B4', 0.2), ('C5', 0.22)], tempo=145, volume=0.42, waveform='triangle')
sound_map["Install"] = lambda: play_sequence([('C4', 0.14), ('E4', 0.14), ('G4', 0.22)], tempo=175, volume=0.44, waveform='sawtooth')
sound_map["Boot"] = lambda: play_sequence([('C5', 0.24), ('E5', 0.24), ('G5', 0.24)], tempo=120, volume=0.45, waveform='triangle')
sound_map["Shutdown"] = lambda: play_bell('A3', duration=0.6, volume=0.39)
sound_map["Tap"] = lambda: play_tone('D5', duration=0.08, volume=0.42, waveform='square')
sound_map["Pop"] = lambda: play_tone('F5', duration=0.1, volume=0.42, waveform='triangle')
sound_map["Beep"] = lambda: play_tone('G4', duration=0.1, volume=0.42, waveform='sine')
sound_map["Ding"] = lambda: play_bell('E5', duration=0.18, volume=0.41)
sound_map["ClickSoft"] = lambda: play_tone('F4', duration=0.08, volume=0.38, waveform='sine')
sound_map["ClickHard"] = lambda: play_tone('C4', duration=0.1, volume=0.42, waveform='square')
sound_map["Swipe"] = lambda: play_swipe(['G5', 'A5'], duration=0.12, volume=0.41, waveform='sawtooth')
sound_map["Slide"] = lambda: play_swipe(['A4', 'G4'], duration=0.12, volume=0.41, waveform='triangle')
sound_map["Open"] = lambda: play_chord(['E5', 'G5'], duration=0.24, volume=0.43, waveform='triangle')
sound_map["Close"] = lambda: play_chord(['D4', 'C4'], duration=0.26, volume=0.4, waveform='sawtooth')
sound_map["MenuOpen"] = lambda: play_ping(['G4', 'A4'], duration=0.11, volume=0.42, waveform='triangle')
sound_map["MenuClose"] = lambda: play_ping(['E4', 'D4'], duration=0.11, volume=0.42, waveform='triangle')
sound_map["TabSwitch"] = lambda: play_ping(['F4', 'A4'], duration=0.12, volume=0.42, waveform='square')
sound_map["Drag"] = lambda: play_ping(['C4', 'D4'], duration=0.14, volume=0.4, waveform='sine')
sound_map["Drop"] = lambda: play_ping(['E4', 'C4'], duration=0.16, volume=0.4, waveform='triangle')
sound_map["Focus"] = lambda: play_ping(['A5', 'G5'], duration=0.1, volume=0.42, waveform='sine')
sound_map["Unfocus"] = lambda: play_ping(['D4', 'C4'], duration=0.1, volume=0.4, waveform='triangle')
sound_map["SelectItem"] = lambda: play_ping(['B4', 'D5'], duration=0.15, volume=0.42, waveform='triangle')
sound_map["Deselect"] = lambda: play_ping(['C4', 'Bb3'], duration=0.15, volume=0.4, waveform='square')
sound_map["Confirm"] = lambda: play_chord(['C5', 'G5'], duration=0.28, volume=0.44, waveform='triangle')
sound_map["Back"] = lambda: play_ping(['D4', 'C4'], duration=0.2, volume=0.4, waveform='sawtooth')
sound_map["Forward"] = lambda: play_ping(['G4', 'A4'], duration=0.22, volume=0.42, waveform='sine')
sound_map["Scroll"] = lambda: play_swipe(['E5', 'F5', 'G5'], duration=0.08, volume=0.39, waveform='sine')
sound_map["ZoomIn"] = lambda: play_chord(['C5', 'E5'], duration=0.22, volume=0.42, waveform='triangle')
sound_map["ZoomOut"] = lambda: play_chord(['G4', 'E4'], duration=0.22, volume=0.4, waveform='triangle')
sound_map["Laser"] = lambda: play_sequence([('A5', 0.1), ('C6', 0.1), ('E6', 0.15)], tempo=220, volume=0.44, waveform='square')
sound_map["Plasma"] = lambda: play_sequence([('F5', 0.15), ('G5', 0.15), ('A5', 0.22)], tempo=200, volume=0.44, waveform='triangle')
sound_map["Warp"] = lambda: play_sequence([('C4', 0.1), ('C5', 0.32)], tempo=130, volume=0.42, waveform='triangle')
sound_map["Robot"] = lambda: play_chord(['G3', 'D4', 'G4'], duration=0.24, volume=0.44, waveform='square')
sound_map["AI"] = lambda: play_sequence([('F4', 0.15), ('A4', 0.15), ('C5', 0.22)], tempo=140, volume=0.44, waveform='sine')
sound_map["Drone"] = lambda: play_chord(['D3', 'F3'], duration=0.48, volume=0.42, waveform='triangle')
sound_map["Scan"] = lambda: play_swipe(['E4', 'F4', 'G4'], duration=0.1, volume=0.42, waveform='sine')
sound_map["Hack"] = lambda: play_sequence([('D4', 0.1), ('F4', 0.1), ('A4', 0.22)], tempo=185, volume=0.42, waveform='square')
sound_map["Glitch"] = lambda: play_sequence([('G5', 0.05), ('C5', 0.05), ('E5', 0.05)], tempo=260, volume=0.45, waveform='square')
sound_map["Signal"] = lambda: play_chord(['A4', 'C5'], duration=0.22, volume=0.42, waveform='sine')

# Add 100 extra sound effects for real usage scenarios.
sfx_patterns = [
    [('C6', 0.06), ('E6', 0.06), ('G6', 0.08)],
    [('D6', 0.07), ('F#6', 0.07), ('A6', 0.09)],
    [('E5', 0.08), ('G5', 0.08), ('B5', 0.1)],
    [('F5', 0.05), ('A5', 0.05), ('C6', 0.07)],
    [('G5', 0.08), ('B5', 0.08), ('D6', 0.1)],
    [('A5', 0.06), ('C6', 0.06), ('E6', 0.08)],
    [('B4', 0.09), ('D5', 0.09), ('F#5', 0.11)],
    [('C5', 0.05), ('E5', 0.05), ('G5', 0.06)],
]
for i in range(1, 101):
    pattern = sfx_patterns[(i - 1) % len(sfx_patterns)]
    tempo = 140 + ((i - 1) % 6) * 6
    waveform = 'square' if i % 7 == 0 else 'triangle' if i % 5 == 0 else 'sine'
    volume = 0.38 + ((i - 1) % 4) * 0.01
    if i % 3 == 0:
        sound_map[f"SFX{i:03d}"] = lambda p=pattern, t=tempo, w=waveform, v=volume: play_sequence(p, tempo=t, volume=v, waveform=w)
    elif i % 3 == 1:
        sound_map[f"SFX{i:03d}"] = lambda p=pattern, w=waveform, v=volume: play_chord([note for note, _ in p], duration=0.18, volume=v, waveform=w)
    else:
        sound_map[f"SFX{i:03d}"] = lambda p=pattern, t=tempo, w=waveform, v=volume: play_swipe([note for note, _ in p], duration=0.08, volume=v, waveform=w)

# Add 100 musical-style sound names with melodic variations.
base_sequences = [
    [('C4', 0.2), ('E4', 0.2), ('G4', 0.2), ('C5', 0.3)],
    [('D4', 0.18), ('F#4', 0.18), ('A4', 0.18), ('D5', 0.3)],
    [('E4', 0.15), ('G#4', 0.15), ('B4', 0.15), ('E5', 0.3)],
    [('F4', 0.2), ('A4', 0.2), ('C5', 0.2), ('F5', 0.3)],
    [('G4', 0.15), ('B4', 0.15), ('D5', 0.15), ('G5', 0.3)],
    [('A4', 0.18), ('C#5', 0.18), ('E5', 0.18), ('A5', 0.3)],
    [('B4', 0.15), ('D5', 0.15), ('F#5', 0.15), ('B5', 0.3)],
]
for i in range(1, 101):
    pattern = base_sequences[(i - 1) % len(base_sequences)]
    tempo = 100 + ((i - 1) % 5) * 10
    waveform = 'triangle' if i % 7 == 0 else 'square' if i % 11 == 0 else 'sine'
    sound_map[f"Melody{i:03d}"] = lambda p=pattern, t=tempo, w=waveform: play_sequence(p, tempo=t, waveform=w, volume=0.4)

# Add 300 extra musical-style sound names with richer, more varied melodic textures.
extra_sequences = [
    [('C4', 0.15), ('D4', 0.15), ('E4', 0.15), ('G4', 0.25)],
    [('E4', 0.18), ('G4', 0.18), ('B4', 0.18), ('E5', 0.3)],
    [('G3', 0.2), ('B3', 0.2), ('D4', 0.2), ('G4', 0.3)],
    [('A3', 0.16), ('C4', 0.16), ('E4', 0.16), ('A4', 0.28)],
    [('F4', 0.14), ('A4', 0.14), ('C5', 0.14), ('F5', 0.28)],
    [('D4', 0.2), ('F#4', 0.15), ('A4', 0.15), ('D5', 0.3)],
    [('B3', 0.2), ('D4', 0.2), ('F#4', 0.2), ('B4', 0.3)],
    [('C5', 0.12), ('E5', 0.12), ('G5', 0.12), ('C6', 0.24)],
    [('A4', 0.15), ('B4', 0.15), ('C#5', 0.15), ('E5', 0.3)],
    [('G4', 0.18), ('A4', 0.18), ('B4', 0.18), ('D5', 0.3)],
    [('F#4', 0.14), ('A4', 0.14), ('C#5', 0.14), ('F#5', 0.3)],
    [('E4', 0.16), ('F#4', 0.16), ('G#4', 0.16), ('B4', 0.3)],
    [('D5', 0.15), ('C5', 0.15), ('A4', 0.15), ('F4', 0.25)],
    [('C4', 0.2), ('B3', 0.2), ('A3', 0.2), ('G3', 0.3)],
    [('E5', 0.1), ('D5', 0.1), ('C5', 0.1), ('B4', 0.1), ('A4', 0.2)],
]
for i in range(101, 401):
    pattern = extra_sequences[(i - 101) % len(extra_sequences)]
    tempo = 90 + ((i - 101) % 10) * 7
    waveform = 'square' if i % 13 == 0 else 'triangle' if i % 7 == 0 else 'sine'
    volume = 0.35 + ((i - 101) % 4) * 0.05
    sound_map[f"Melody{i:03d}"] = lambda p=pattern, t=tempo, w=waveform, v=volume: play_sequence(p, tempo=t, volume=v, waveform=w)

# General utilities added to make the library competitive.

# =========================
# USEFUL HELPER FUNCTIONS
# =========================

utility_functions = {}

def _register_utility(name, fn):
    globals()[name] = fn
    utility_functions[name] = fn
    return fn


def safe_int(value, default=0):
    try:
        return int(value)
    except Exception:
        return default


def safe_float(value, default=0.0):
    try:
        return float(value)
    except Exception:
        return default


def safe_bool(value, default=False):
    if isinstance(value, bool):
        return value
    if isinstance(value, str):
        return value.strip().lower() in ('1', 'true', 'yes', 'on')
    try:
        return bool(value)
    except Exception:
        return default


def safe_str(value, default=''):
    try:
        return str(value)
    except Exception:
        return default


def split_words(text):
    return normalize_whitespace(str(text)).split(' ')


def join_words(words, separator=' '):
    return separator.join(str(w) for w in words)


def repeat_text(text, count=2):
    return str(text) * max(0, int(count))


def abbreviate_text(text, max_length=30):
    text = str(text)
    if len(text) <= max_length:
        return text
    return text[:max(0, max_length - 3)].rstrip() + '...'


def is_palindrome(text):
    clean = re.sub(r'\W+', '', str(text).lower())
    return clean == clean[::-1]


def reverse_text(text):
    return str(text)[::-1]


def contains_substring(text, substring):
    return str(substring) in str(text)


def starts_with(text, prefix):
    return str(text).startswith(str(prefix))


def ends_with(text, suffix):
    return str(text).endswith(str(suffix))


def slugify(text):
    clean = re.sub(r'[^\w\s-]', '', str(text).lower())
    return re.sub(r'[\s_-]+', '-', clean).strip('-')


def normalize_spaces(text):
    return re.sub(r'\s+', ' ', str(text)).strip()


def truncate_text(text, max_length=30):
    return abbreviate_text(text, max_length=max_length)


def extract_digits(text):
    return ''.join(ch for ch in str(text) if ch.isdigit())


def random_choice_list(values):
    try:
        return random.choice(list(values))
    except Exception:
        return None


def random_range(minimum, maximum):
    try:
        return random.randint(int(minimum), int(maximum))
    except Exception:
        return None


def weighted_choice(choices):
    total = sum(weight for item, weight in choices if weight > 0)
    if total <= 0:
        return None
    r = random.uniform(0, total)
    current = 0
    for item, weight in choices:
        current += weight
        if current >= r:
            return item
    return None


def clamp_value(value, minimum, maximum):
    try:
        return max(minimum, min(maximum, value))
    except Exception:
        return value


def lerp_value(start, end, fraction):
    return start + (end - start) * fraction


def remap_value(value, old_min, old_max, new_min, new_max):
    if old_max == old_min:
        raise ValueError('old_min and old_max cannot be equal')
    ratio = (value - old_min) / (old_max - old_min)
    return new_min + ratio * (new_max - new_min)


def is_even(value):
    return isinstance(value, int) and value % 2 == 0


def is_odd(value):
    return isinstance(value, int) and value % 2 != 0


def is_prime(value):
    try:
        n = int(value)
    except Exception:
        return False
    if n < 2:
        return False
    if n in (2, 3):
        return True
    if n % 2 == 0:
        return False
    limit = int(math.sqrt(n)) + 1
    for i in range(3, limit, 2):
        if n % i == 0:
            return False
    return True


def factorial_safe(value, default=1):
    try:
        n = int(value)
        if n < 0:
            return default
        result = 1
        for i in range(2, n + 1):
            result *= i
        return result
    except Exception:
        return default


def gcd_safe(a, b):
    try:
        return math.gcd(int(a), int(b))
    except Exception:
        return 0


def lcm_safe(a, b):
    try:
        a, b = int(a), int(b)
        if a == 0 or b == 0:
            return 0
        return abs(a * b) // math.gcd(a, b)
    except Exception:
        return 0


def average_list(values):
    try:
        values = list(values)
        return sum(values) / len(values) if values else 0
    except Exception:
        return 0


def median_list(values):
    try:
        return statistics.median(list(values))
    except Exception:
        return None


def mode_list(values):
    try:
        return statistics.mode(list(values))
    except Exception:
        return None


def variance_list(values):
    try:
        return statistics.pvariance(list(values))
    except Exception:
        return None


def stddev_list(values):
    try:
        return statistics.pstdev(list(values))
    except Exception:
        return None


def percentile(values, percent):
    try:
        values = sorted(list(values))
        k = (len(values) - 1) * percent / 100.0
        f = math.floor(k)
        c = math.ceil(k)
        if f == c:
            return values[int(k)]
        d0 = values[int(f)] * (c - k)
        d1 = values[int(c)] * (k - f)
        return d0 + d1
    except Exception:
        return None


def sum_list(values):
    try:
        return sum(values)
    except Exception:
        return 0


def product_list(values):
    result = 1
    try:
        for value in values:
            result *= value
        return result
    except Exception:
        return None


def unique_list(sequence):
    return list(dict.fromkeys(sequence))


def flatten_nested(sequence):
    result = []
    for item in sequence:
        if isinstance(item, list):
            result.extend(flatten_nested(item))
        else:
            result.append(item)
    return result


def chunk_list_safe(sequence, chunk_size):
    return [sequence[i:i + chunk_size] for i in range(0, len(sequence), chunk_size)]


def pairwise(sequence):
    return list(zip(sequence, sequence[1:]))


def map_list(sequence, func):
    return [func(item) for item in sequence]


def filter_list(sequence, func):
    return [item for item in sequence if func(item)]


def find_index(sequence, predicate, default=-1):
    for index, item in enumerate(sequence):
        if predicate(item):
            return index
    return default


def first_or_default(sequence, default=None):
    return sequence[0] if sequence else default


def last_or_default(sequence, default=None):
    return sequence[-1] if sequence else default


def group_by_key(sequence, key_function):
    groups = defaultdict(list)
    for item in sequence:
        groups[key_function(item)].append(item)
    return dict(groups)


def merge_unique_lists(*lists):
    result = []
    for lst in lists:
        for item in lst:
            if item not in result:
                result.append(item)
    return result


def dict_invert(d):
    return {v: k for k, v in d.items()}


def safe_get_dict(d, key, default=None):
    if isinstance(d, dict):
        return d.get(key, default)
    return default


def dict_contains_key(d, key):
    return isinstance(d, dict) and key in d


def list_difference(a, b):
    return [item for item in a if item not in b]


def list_intersection(a, b):
    return [item for item in a if item in b]


def list_union(a, b):
    return unique_list(list(a) + list(b))


def path_exists(path):
    return Path(path).exists()


def is_file_path(path):
    return Path(path).is_file()


def is_dir_path(path):
    return Path(path).is_dir()


def read_lines(path, encoding='utf-8'):
    try:
        return Path(path).read_text(encoding=encoding).splitlines()
    except Exception:
        return []


def write_lines(path, lines, encoding='utf-8'):
    try:
        Path(path).parent.mkdir(parents=True, exist_ok=True)
        Path(path).write_text('\n'.join(str(line) for line in lines), encoding=encoding)
        return True
    except Exception:
        return False


def append_lines(path, lines, encoding='utf-8'):
    try:
        Path(path).parent.mkdir(parents=True, exist_ok=True)
        with Path(path).open('a', encoding=encoding) as file:
            file.write('\n'.join(str(line) for line in lines) + '\n')
        return True
    except Exception:
        return False


def make_dirs(path):
    try:
        Path(path).mkdir(parents=True, exist_ok=True)
        return True
    except Exception:
        return False


def delete_path(path):
    try:
        target = Path(path)
        if target.is_dir():
            shutil.rmtree(target)
        else:
            target.unlink()
        return True
    except Exception:
        return False


def copy_path(src, dst):
    try:
        Path(dst).parent.mkdir(parents=True, exist_ok=True)
        shutil.copy2(src, dst)
        return True
    except Exception:
        return False


def move_path(src, dst):
    try:
        Path(dst).parent.mkdir(parents=True, exist_ok=True)
        shutil.move(src, dst)
        return True
    except Exception:
        return False


def file_size_safe(path):
    try:
        return Path(path).stat().st_size
    except Exception:
        return 0


def format_date_short(value=None):
    return format_datetime(value, '%Y-%m-%d')


def format_date_long(value=None):
    return format_datetime(value, '%Y-%m-%d %H:%M:%S')


def parse_date_safe(text, fmt='%Y-%m-%d %H:%M:%S'):
    try:
        return parse_datetime(text, fmt)
    except Exception:
        return None


def today_iso():
    return datetime.datetime.now().date().isoformat()


def tomorrow_iso():
    return (datetime.datetime.now() + datetime.timedelta(days=1)).date().isoformat()


def yesterday_iso():
    return (datetime.datetime.now() - datetime.timedelta(days=1)).date().isoformat()


def add_days_safe(value, days):
    try:
        if isinstance(value, (int, float)):
            value = datetime.datetime.fromtimestamp(value)
        return value + datetime.timedelta(days=days)
    except Exception:
        return None


def subtract_days_safe(value, days):
    try:
        if isinstance(value, (int, float)):
            value = datetime.datetime.fromtimestamp(value)
        return value - datetime.timedelta(days=days)
    except Exception:
        return None


def is_weekend_safe(value=None):
    if value is None:
        value = datetime.datetime.now()
    if isinstance(value, (int, float)):
        value = datetime.datetime.fromtimestamp(value)
    return value.weekday() >= 5


def timestamp_now():
    return datetime.datetime.now().timestamp()


def seconds_since(value):
    try:
        return (datetime.datetime.now() - value).total_seconds()
    except Exception:
        return None


def minutes_since(value):
    try:
        return seconds_since(value) / 60.0
    except Exception:
        return None


def hours_since(value):
    try:
        return seconds_since(value) / 3600.0
    except Exception:
        return None


def days_since(value):
    try:
        return seconds_since(value) / 86400.0
    except Exception:
        return None


def random_hex(length=8):
    return ''.join(random.choice('0123456789abcdef') for _ in range(max(1, int(length))))


def random_string(length=8, chars=string.ascii_letters + string.digits):
    return ''.join(random.choice(chars) for _ in range(max(1, int(length))))


def uuid_simple():
    return ''.join(random.choice('0123456789abcdef') for _ in range(32))


def url_encode(value):
    return urllib.parse.quote_plus(str(value))


def url_decode(value):
    return urllib.parse.unquote_plus(str(value))


def parse_query(query):
    return dict(urllib.parse.parse_qsl(str(query)))


def build_query(params):
    return urllib.parse.urlencode(params)


def safe_json_load(path, encoding='utf-8'):
    try:
        return load_json(path, encoding=encoding)
    except Exception:
        return None


def safe_json_dump(path, data, encoding='utf-8', indent=4):
    try:
        save_json(path, data, encoding=encoding, indent=indent)
        return True
    except Exception:
        return False


def get_env_var(key, default=None):
    return os.environ.get(str(key), default)


def set_env_var(key, value):
    os.environ[str(key)] = str(value)
    return True


def delete_env_var(key):
    os.environ.pop(str(key), None)
    return True


def http_get_text(url, timeout=10):
    try:
        body, _, _ = http_get(url, timeout=timeout)
        return body.decode('utf-8')
    except Exception:
        return None


def http_post_json_safe(url, json_data=None, timeout=10):
    try:
        body, _, _ = http_post(url, json_data=json_data, timeout=timeout)
        return body.decode('utf-8')
    except Exception:
        return None


def shell_quote(text):
    return str(text).replace('"', '\\"').replace("'", "\\'")


def wait_seconds(seconds):
    try:
        time.sleep(float(seconds))
        return True
    except Exception:
        return False


def retry_function(fn, attempts=3, delay=0.2, *args, **kwargs):
    last_error = None
    for _ in range(max(1, int(attempts))):
        try:
            return fn(*args, **kwargs)
        except Exception as exc:
            last_error = exc
            time.sleep(delay)
    raise last_error


def memoize_function(fn):
    cache = {}
    def wrapper(*args):
        if args in cache:
            return cache[args]
        result = fn(*args)
        cache[args] = result
        return result
    return wrapper


def ensure_list(value):
    if value is None:
        return []
    if isinstance(value, list):
        return value
    return [value]


def ensure_dict(value):
    return value if isinstance(value, dict) else {}


def is_callable(value):
    return callable(value)


def register_useful_functions():
    entries = [
        ('safe_int', safe_int),
        ('safe_float', safe_float),
        ('safe_bool', safe_bool),
        ('safe_str', safe_str),
        ('split_words', split_words),
        ('join_words', join_words),
        ('repeat_text', repeat_text),
        ('abbreviate_text', abbreviate_text),
        ('is_palindrome', is_palindrome),
        ('reverse_text', reverse_text),
        ('contains_substring', contains_substring),
        ('starts_with', starts_with),
        ('ends_with', ends_with),
        ('slugify', slugify),
        ('normalize_spaces', normalize_spaces),
        ('truncate_text', truncate_text),
        ('extract_digits', extract_digits),
        ('random_choice_list', random_choice_list),
        ('random_range', random_range),
        ('weighted_choice', weighted_choice),
        ('clamp_value', clamp_value),
        ('lerp_value', lerp_value),
        ('remap_value', remap_value),
        ('is_even', is_even),
        ('is_odd', is_odd),
        ('is_prime', is_prime),
        ('factorial_safe', factorial_safe),
        ('gcd_safe', gcd_safe),
        ('lcm_safe', lcm_safe),
        ('average_list', average_list),
        ('median_list', median_list),
        ('mode_list', mode_list),
        ('variance_list', variance_list),
        ('stddev_list', stddev_list),
        ('percentile', percentile),
        ('sum_list', sum_list),
        ('product_list', product_list),
        ('unique_list', unique_list),
        ('flatten_nested', flatten_nested),
        ('chunk_list_safe', chunk_list_safe),
        ('pairwise', pairwise),
        ('map_list', map_list),
        ('filter_list', filter_list),
        ('find_index', find_index),
        ('first_or_default', first_or_default),
        ('last_or_default', last_or_default),
        ('group_by_key', group_by_key),
        ('merge_unique_lists', merge_unique_lists),
        ('dict_invert', dict_invert),
        ('safe_get_dict', safe_get_dict),
        ('dict_contains_key', dict_contains_key),
        ('list_difference', list_difference),
        ('list_intersection', list_intersection),
        ('list_union', list_union),
        ('path_exists', path_exists),
        ('is_file_path', is_file_path),
        ('is_dir_path', is_dir_path),
        ('read_lines', read_lines),
        ('write_lines', write_lines),
        ('append_lines', append_lines),
        ('make_dirs', make_dirs),
        ('delete_path', delete_path),
        ('copy_path', copy_path),
        ('move_path', move_path),
        ('file_size_safe', file_size_safe),
        ('format_date_short', format_date_short),
        ('format_date_long', format_date_long),
        ('parse_date_safe', parse_date_safe),
        ('today_iso', today_iso),
        ('tomorrow_iso', tomorrow_iso),
        ('yesterday_iso', yesterday_iso),
        ('add_days_safe', add_days_safe),
        ('subtract_days_safe', subtract_days_safe),
        ('is_weekend_safe', is_weekend_safe),
        ('timestamp_now', timestamp_now),
        ('seconds_since', seconds_since),
        ('minutes_since', minutes_since),
        ('hours_since', hours_since),
        ('days_since', days_since),
        ('random_hex', random_hex),
        ('random_string', random_string),
        ('uuid_simple', uuid_simple),
        ('url_encode', url_encode),
        ('url_decode', url_decode),
        ('parse_query', parse_query),
        ('build_query', build_query),
        ('safe_json_load', safe_json_load),
        ('safe_json_dump', safe_json_dump),
        ('get_env_var', get_env_var),
        ('set_env_var', set_env_var),
        ('delete_env_var', delete_env_var),
        ('http_get_text', http_get_text),
        ('http_post_json_safe', http_post_json_safe),
        ('shell_quote', shell_quote),
        ('wait_seconds', wait_seconds),
        ('retry_function', retry_function),
        ('memoize_function', memoize_function),
        ('ensure_list', ensure_list),
        ('ensure_dict', ensure_dict),
        ('is_callable', is_callable),
    ]
    for name, fn in entries:
        _register_utility(name, fn)


register_useful_functions()

# =========================
# DYNAMIC EXTRA FUNCTION GENERATOR
# =========================

extra_functions = {}

def _make_extra_function(i):
    name = f"extra_func_{i:05d}"
    def fn(x=None, _i=i, _name=name):
        # perform a tiny unique computation depending on index
        val = (_i * 2654435761) & 0xFFFFFFFF
        if x is None:
            return {'name': _name, 'index': _i, 'value': val}
        # small, deterministic, unique transformation using index
        try:
            return {'name': _name, 'index': _i, 'value': (hash(str(x)) ^ val) & 0xFFFFFFFF}
        except Exception:
            return {'name': _name, 'index': _i, 'value': val}

    fn.__name__ = name
    fn.__doc__ = f"Dynamically generated function #{i}"
    return fn

def _get_readme_path():
    # README is assumed to live in the workspace root (two levels up from this module)
    try:
        return Path(__file__).resolve().parents[1] / 'README.md'
    except Exception:
        return Path('README.md')


def _ensure_readme_section():
    path = _get_readme_path()
    marker = '## Auto-Generated Functions'
    try:
        text = path.read_text(encoding='utf-8') if path.exists() else ''
    except Exception:
        text = ''
    if marker not in text:
        try:
            with path.open('a', encoding='utf-8') as f:
                f.write('\n\n' + marker + '\n\n')
        except Exception:
            pass


def register_doc_in_readme(name, doc):
    """Append a brief documentation line for `name` into README under the auto-generated section.

    This function avoids duplicate entries.
    """
    path = _get_readme_path()
    try:
        text = path.read_text(encoding='utf-8') if path.exists() else ''
    except Exception:
        text = ''
    _ensure_readme_section()
    try:
        if f'- `{name}`' in text:
            return False
    except Exception:
        pass
    summary = (doc or '').split('\n', 1)[0].strip()
    line = f"- `{name}`: {summary}\n"
    try:
        with path.open('a', encoding='utf-8') as f:
            f.write(line)
        return True
    except Exception:
        return False


def register_docs_in_readme(items):
    for name, doc in items:
        register_doc_in_readme(name, doc)


def generate_extra_functions(count=50000, prefix='extra_func_', annotate=False):
    """Generate `count` uniquely named functions and register them in module globals.

    Each function is unique by index and performs a small deterministic computation.
    If `annotate` is True, a short entry is appended to README.md for each created function.
    """
    docs = []
    for i in range(1, count + 1):
        name = f"{prefix}{i:05d}"
        if name in globals():
            continue
        fn = _make_extra_function(i)
        globals()[name] = fn
        extra_functions[name] = fn
        if annotate:
            docs.append((name, getattr(fn, '__doc__', '') or f'Dynamically generated function #{i}'))
    if annotate and docs:
        try:
            register_docs_in_readme(docs)
        except Exception:
            pass

# generate on import (generate 1,100 functions total — existing plus extra)
try:
    # generate a large set of small deterministic helper functions (useful for testing/tools)
    generate_extra_functions(1100, annotate=True)
except Exception:
    # if generation fails for memory/time reasons, fall back to a smaller set
    try:
        generate_extra_functions(200, annotate=True)
    except Exception:
        pass


# =========================
# NAME-BEEP SYSTEM
# =========================

def play_name_beep(name, volume=0.45, speed=0.08, waveform='sine'):
    """Play a short sequence of tones representing the given `name`.

    Each character is mapped to a deterministic frequency; the sequence
    is played sequentially to form a short audible signature (a "beep").
    """
    try:
        s = str(name)
    except Exception:
        s = ''
    parts = []
    fs = 44100
    for ch in s.lower():
        if 'a' <= ch <= 'z':
            idx = ord(ch) - ord('a')
            freq = 220 + (idx % 12) * 28 + (idx // 12) * 6
        elif '0' <= ch <= '9':
            idx = ord(ch) - ord('0')
            freq = 400 + idx * 18
        elif ch in (' ', '-', '_'):
            freq = 180
        else:
            freq = 440
        parts.append(_generate_wave(freq, duration=speed, waveform=waveform, fs=fs) if 'fs' in _generate_wave.__code__.co_varnames else _generate_wave(freq, duration=speed, waveform=waveform))

    if not parts:
        try:
            time.sleep(speed)
        except Exception:
            pass
        return

    seq = np.concatenate(parts).astype(np.float32)
    # small overall envelope to avoid clicks
    if seq.size:
        peak = np.max(np.abs(seq))
        if peak > 0:
            seq = seq / peak
        try:
            _sound_player.play_wave((seq * float(volume)).astype(np.float32), fs=fs)
        except Exception:
            try:
                if sd is not None:
                    sd.play((seq * float(volume)).astype(np.float32), fs)
                    sd.wait()
                else:
                    time.sleep(len(seq) / float(fs))
            except Exception:
                time.sleep(len(seq) / float(fs))


def register_name_beep(name):
    """Register a named beep into `sound_map` under key `Beep_<name>`.

    Returns the registered key.
    """
    key = f"Beep_{name}"
    def _play(n=name):
        play_name_beep(n)
    sound_map[key] = lambda _n=name: play_name_beep(_n)
    # document the registered beep in README for future discoverability
    try:
        register_doc_in_readme(key, f'Name beep for {name}')
    except Exception:
        pass
    return key


def register_name_beep_group(names):
    """Register a list/iterable of names as individual beeps in `sound_map`."""
    for n in names:
        register_name_beep(n)


def list_registered_name_beeps():
    """Return list of registered name beep keys in `sound_map`."""
    return [k for k in sound_map.keys() if k.startswith('Beep_')]


def remove_name_beep(name):
    key = f"Beep_{name}"
    return sound_map.pop(key, None)

# =========================
# VALIDATION UTILITIES
# =========================

def is_integer(value):
    return isinstance(value, int) and not isinstance(value, bool)


def is_float(value):
    return isinstance(value, float)


def is_string(value):
    return isinstance(value, str)


def is_list(value):
    return isinstance(value, list)


def is_dict(value):
    return isinstance(value, dict)


def is_boolean(value):
    return isinstance(value, bool)


def validate_schema(data, schema, path=''):
    errors = []
    if not isinstance(schema, dict):
        return False, ['schema must be a dict']

    if 'type' in schema:
        expected = schema['type']
        if expected == 'string' and not isinstance(data, str):
            errors.append(f'{path or "root"}: expected string')
        elif expected == 'integer' and not is_integer(data):
            errors.append(f'{path or "root"}: expected integer')
        elif expected == 'number' and not isinstance(data, (int, float)):
            errors.append(f'{path or "root"}: expected number')
        elif expected == 'boolean' and not isinstance(data, bool):
            errors.append(f'{path or "root"}: expected boolean')
        elif expected == 'object' and not isinstance(data, dict):
            errors.append(f'{path or "root"}: expected object')
        elif expected == 'array' and not isinstance(data, list):
            errors.append(f'{path or "root"}: expected array')

    if isinstance(data, dict) and 'properties' in schema:
        for key, subschema in schema['properties'].items():
            if key in data:
                valid, sub_errors = validate_schema(data[key], subschema, f'{path}.{key}' if path else key)
                errors.extend(sub_errors)
            elif subschema.get('required', False):
                errors.append(f'{path}.{key}' if path else key + ': required field missing')

    if isinstance(data, list) and 'items' in schema:
        for index, item in enumerate(data):
            valid, sub_errors = validate_schema(item, schema['items'], f'{path}[{index}]')
            errors.extend(sub_errors)

    return len(errors) == 0, errors


def ensure_schema(data, schema):
    valid, errors = validate_schema(data, schema)
    if not valid:
        raise ValueError('Validation failed: ' + '; '.join(errors))
    return True


def has_keys(obj, keys):
    return all(k in obj for k in keys)


def assert_type(value, expected_type):
    return isinstance(value, expected_type)


def is_truthy(value):
    return bool(value)


def is_blank(text):
    return not str(text).strip()


# =========================
# JSON & CONFIG UTILITIES
# =========================

def load_json(path, encoding='utf-8'):
    with open(path, 'r', encoding=encoding) as file:
        return json.load(file)


def save_json(path, data, encoding='utf-8', indent=4):
    Path(path).parent.mkdir(parents=True, exist_ok=True)
    with open(path, 'w', encoding=encoding) as file:
        json.dump(data, file, ensure_ascii=False, indent=indent)


def merge_dicts(*dicts, deep=True):
    result = {}
    for d in dicts:
        if not isinstance(d, dict):
            continue
        for key, value in d.items():
            if deep and key in result and isinstance(result[key], dict) and isinstance(value, dict):
                result[key] = merge_dicts(result[key], value, deep=True)
            else:
                result[key] = value
    return result


def deep_get(data, path, default=None, separator='.'):
    if data is None:
        return default
    if separator in path:
        parts = path.split(separator)
    else:
        parts = [path]
    current = data
    for part in parts:
        if isinstance(current, dict) and part in current:
            current = current[part]
        else:
            return default
    return current


def deep_set(data, path, value, separator='.'):
    if not isinstance(data, dict):
        raise ValueError('deep_set target must be a dict')
    keys = path.split(separator)
    current = data
    for key in keys[:-1]:
        if key not in current or not isinstance(current[key], dict):
            current[key] = {}
        current = current[key]
    current[keys[-1]] = value
    return data


def load_json_url(url, headers=None, timeout=10):
    request = urllib.request.Request(url, headers=headers or {})
    with urllib.request.urlopen(request, timeout=timeout) as response:
        return json.loads(response.read().decode('utf-8'))


# =========================
# HTTP UTILITIES
# =========================

def http_get(url, headers=None, timeout=10):
    request = urllib.request.Request(url, headers=headers or {}, method='GET')
    with urllib.request.urlopen(request, timeout=timeout) as response:
        return response.read(), response.getcode(), dict(response.headers)


def http_post(url, data=None, json_data=None, headers=None, timeout=10):
    request_headers = headers.copy() if headers else {}
    body = None
    if json_data is not None:
        body = json.dumps(json_data).encode('utf-8')
        request_headers['Content-Type'] = 'application/json'
    elif data is not None:
        if isinstance(data, dict):
            body = urllib.parse.urlencode(data).encode('utf-8')
        else:
            body = str(data).encode('utf-8')
    request = urllib.request.Request(url, data=body, headers=request_headers, method='POST')
    with urllib.request.urlopen(request, timeout=timeout) as response:
        return response.read(), response.getcode(), dict(response.headers)


def fetch_json(url, headers=None, timeout=10):
    body, code, headers = http_get(url, headers=headers, timeout=timeout)
    return json.loads(body.decode('utf-8'))


def check_url(url, timeout=5):
    try:
        _, code, _ = http_get(url, timeout=timeout)
        return 200 <= code < 400
    except Exception:
        return False


def download_file(url, dest, chunk_size=8192, timeout=10):
    Path(dest).parent.mkdir(parents=True, exist_ok=True)
    request = urllib.request.Request(url)
    with urllib.request.urlopen(request, timeout=timeout) as response:
        with open(dest, 'wb') as out_file:
            while True:
                chunk = response.read(chunk_size)
                if not chunk:
                    break
                out_file.write(chunk)
    return dest


# =========================
# DATE / TIME UTILITIES
# =========================

def current_timestamp():
    return datetime.datetime.now().timestamp()


def format_datetime(value=None, fmt='%Y-%m-%d %H:%M:%S'):
    if value is None:
        value = datetime.datetime.now()
    if isinstance(value, (int, float)):
        value = datetime.datetime.fromtimestamp(value)
    return value.strftime(fmt)


def parse_datetime(text, fmt='%Y-%m-%d %H:%M:%S'):
    return datetime.datetime.strptime(str(text), fmt)


def relative_time(value, now=None):
    if now is None:
        now = datetime.datetime.now()
    if isinstance(value, (int, float)):
        value = datetime.datetime.fromtimestamp(value)
    delta = now - value
    seconds = int(delta.total_seconds())
    if seconds < 0:
        return 'in the future'
    if seconds < 60:
        return f'{seconds}s ago'
    if seconds < 3600:
        return f'{seconds // 60}m ago'
    if seconds < 86400:
        return f'{seconds // 3600}h ago'
    return f'{seconds // 86400}d ago'


def countdown(seconds, callback=None, interval=1):
    start = time.time()
    remaining = seconds
    while remaining > 0:
        time.sleep(min(interval, remaining))
        remaining = seconds - (time.time() - start)
        if callback:
            callback(max(0, int(remaining)))
    return 0


def is_weekend(value=None):
    if value is None:
        value = datetime.datetime.now()
    if isinstance(value, (int, float)):
        value = datetime.datetime.fromtimestamp(value)
    return value.weekday() >= 5


def add_business_days(value, days):
    if isinstance(value, (int, float)):
        value = datetime.datetime.fromtimestamp(value)
    result = value
    while days > 0:
        result += datetime.timedelta(days=1)
        if result.weekday() < 5:
            days -= 1
    return result


# =========================
# MATH / STATISTICS UTILITIES
# =========================

def clamp(value, minimum, maximum):
    return max(minimum, min(maximum, value))


def lerp(start, end, fraction):
    return start + (end - start) * fraction


def remap(value, old_min, old_max, new_min, new_max):
    if old_max == old_min:
        raise ValueError('old_min and old_max cannot be equal')
    ratio = (value - old_min) / (old_max - old_min)
    return new_min + ratio * (new_max - new_min)


def mean_values(values):
    return statistics.mean(values)


def median_values(values):
    return statistics.median(values)


def mode_values(values):
    try:
        return statistics.mode(values)
    except statistics.StatisticsError:
        return None


def variance(values, sample=False):
    return statistics.pvariance(values) if not sample else statistics.variance(values)


def stddev(values, sample=False):
    return math.sqrt(variance(values, sample=sample))


def percent_change(original, new):
    if original == 0:
        return float('inf') if new else 0.0
    return ((new - original) / abs(original)) * 100.0


def safe_divide(a, b, default=0.0):
    try:
        return a / b
    except Exception:
        return default


def random_choice_weighted(choices):
    total = sum(weight for item, weight in choices)
    if total <= 0:
        return None
    r = random.uniform(0, total)
    upto = 0
    for item, weight in choices:
        if upto + weight >= r:
            return item
        upto += weight
    return None


# =========================
# PROCESS & COMMAND UTILITIES
# =========================

def run_command(command, shell=False, cwd=None, env=None, timeout=None):
    if isinstance(command, str) and not shell:
        command = command.split()
    result = subprocess.run(command, shell=shell, cwd=cwd, env=env, capture_output=True, text=True, timeout=timeout)
    return {
        'returncode': result.returncode,
        'stdout': result.stdout,
        'stderr': result.stderr,
    }


def run_command_async(command, callback=None, shell=False, cwd=None, env=None, timeout=None):
    def worker():
        result = run_command(command, shell=shell, cwd=cwd, env=env, timeout=timeout)
        if callback:
            callback(result)
    thread = threading.Thread(target=worker, daemon=True)
    thread.start()
    return thread


def which(program):
    return shutil.which(program)


def is_process_running(name):
    if platform.system() == 'Windows':
        tasklist = subprocess.run(['tasklist'], capture_output=True, text=True)
        return name.lower() in tasklist.stdout.lower()
    else:
        ps = subprocess.run(['ps', 'ax'], capture_output=True, text=True)
        return name.lower() in ps.stdout.lower()


def kill_process(pid):
    try:
        os.kill(pid, 9)
        return True
    except Exception:
        return False


def copy_file(src, dst):
    Path(dst).parent.mkdir(parents=True, exist_ok=True)
    shutil.copy2(src, dst)
    return dst


def move_file(src, dst):
    Path(dst).parent.mkdir(parents=True, exist_ok=True)
    shutil.move(src, dst)
    return dst


def delete_file(path):
    try:
        Path(path).unlink()
        return True
    except Exception:
        return False


# =========================
# DATA STRUCTURE UTILITIES
# =========================

def chunk_list(sequence, chunk_size):
    if chunk_size <= 0:
        raise ValueError('chunk_size must be positive')
    return [sequence[i:i + chunk_size] for i in range(0, len(sequence), chunk_size)]


def flatten_list(nested_list):
    result = []
    for item in nested_list:
        if isinstance(item, list):
            result.extend(flatten_list(item))
        else:
            result.append(item)
    return result


def unique_preserve(sequence):
    seen = set()
    result = []
    for item in sequence:
        if item not in seen:
            seen.add(item)
            result.append(item)
    return result


def group_by(sequence, key_function):
    groups = defaultdict(list)
    for item in sequence:
        groups[key_function(item)].append(item)
    return dict(groups)


def coalesce(*values):
    for value in values:
        if value is not None:
            return value
    return None


def dict_merge(*dicts):
    merged = {}
    for d in dicts:
        if isinstance(d, dict):
            merged.update(d)
    return merged


# =========================
# SETTINGS & STORAGE UTILITIES
# =========================

def load_settings(path, default=None, encoding='utf-8'):
    if not Path(path).exists():
        return default if default is not None else {}
    return load_json(path, encoding=encoding)


def save_settings(path, settings, encoding='utf-8', indent=4):
    return save_json(path, settings, encoding=encoding, indent=indent)


def get_setting(settings, key, default=None):
    if isinstance(settings, dict):
        return deep_get(settings, key, default=default)
    return default


def set_setting(settings, key, value):
    if not isinstance(settings, dict):
        raise ValueError('settings must be a dict')
    return deep_set(settings, key, value)


def update_settings(settings, updates):
    if not isinstance(settings, dict) or not isinstance(updates, dict):
        raise ValueError('update_settings requires dicts')
    return merge_dicts(settings, updates)


def flatten_settings(settings, separator='.'):
    result = {}
    def _flatten(source, parent_key=''):
        for key, value in source.items():
            full_key = f'{parent_key}{separator}{key}' if parent_key else key
            if isinstance(value, dict):
                _flatten(value, full_key)
            else:
                result[full_key] = value
    _flatten(settings)
    return result


# =========================
# EVENT BUS / PUBLISH-SUBSCRIBE
# =========================

class EventBus:
    def __init__(self):
        self._listeners = defaultdict(list)

    def subscribe(self, event_name, listener):
        if callable(listener):
            self._listeners[event_name].append(listener)
            return True
        return False

    def unsubscribe(self, event_name, listener=None):
        if event_name not in self._listeners:
            return False
        if listener is None:
            self._listeners.pop(event_name, None)
            return True
        try:
            self._listeners[event_name].remove(listener)
            return True
        except ValueError:
            return False

    def emit(self, event_name, *args, **kwargs):
        for listener in list(self._listeners.get(event_name, [])):
            try:
                listener(*args, **kwargs)
            except Exception:
                pass

    def clear(self):
        self._listeners.clear()


# =========================
# LOGGING UTILITIES
# =========================

_log_file = None


def set_log_file(path, level=logging.INFO, mode='a'):
    global _log_file
    logger = logging.getLogger(__name__)
    logger.setLevel(level)
    formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s')
    file_handler = logging.FileHandler(path, mode=mode, encoding='utf-8')
    file_handler.setFormatter(formatter)
    logger.handlers = [file_handler]
    _log_file = path
    return path


def log_info(message):
    logging.getLogger(__name__).info(str(message))


def log_warning(message):
    logging.getLogger(__name__).warning(str(message))


def log_error(message):
    logging.getLogger(__name__).error(str(message))


def log_debug(message):
    logging.getLogger(__name__).debug(str(message))


def get_log_file():
    return _log_file


def configure_logging(level=logging.INFO, fmt='[%(levelname)s] %(message)s'):
    logging.basicConfig(level=level, format=fmt)
    return logging.getLogger(__name__)


# =========================
# CLI / OUTPUT UTILITIES
# =========================

def print_table(rows, headers=None, padding=2):
    if headers:
        rows = [headers] + rows
    widths = [max(len(str(value)) for value in column) for column in zip(*rows)]
    lines = []
    for row in rows:
        line = '  '.join(str(value).ljust(widths[index] + padding) for index, value in enumerate(row))
        lines.append(line)
    print('\n'.join(lines))


def progress_bar(iterable, prefix='', size=40, fill='█'):
    total = len(iterable)
    for index, item in enumerate(iterable, start=1):
        filled = int(size * index / total)
        bar = fill * filled + '-' * (size - filled)
        print(f'\r{prefix} |{bar}| {index}/{total}', end='')
        yield item
    print()


def prompt_choices(root, title, options, default_index=0):
    if not options:
        return None
    choice = simpledialog.askstring(title, 'Choose: ' + ', '.join(str(i+1) + ':' + str(opt) for i, opt in enumerate(options)), initialvalue=str(default_index+1), parent=root)
    try:
        idx = int(choice) - 1
        return options[idx] if 0 <= idx < len(options) else None
    except Exception:
        return None


def clear_console():
    if platform.system() == 'Windows':
        os.system('cls')
    else:
        os.system('clear')


def confirm_action(message='Proceed?', default=True):
    return messagebox.askyesno('Confirm', message, icon='question')


def show_progress(title, current, total):
    percent = int((current / total) * 100) if total else 0
    return f'{title}: {percent}% ({current}/{total})'


def play(name):
    def _runner():
        if name in sound_map:
            try:
                sound_map[name]()
            except Exception:
                pass
        else:
            print(f"Sound '{name}' not found in sound_map.")

    thread = threading.Thread(target=_runner, daemon=True)
    thread.start()
    return thread


def add_sound(name, func):
    sound_map[name] = lambda: func()

# =========================
# MAJOR ARCHITECTURE EXPANSION
# =========================

class PluginManager:
    """Manages plugins and extension lifecycle."""

    def __init__(self):
        self.plugins = {}

    def register(self, name, plugin):
        self.plugins[name] = plugin
        return True

    def unregister(self, name):
        if name in self.plugins:
            del self.plugins[name]
            return True
        return False

    def get_plugin(self, name, default=None):
        return self.plugins.get(name, default)

    def list_plugins(self):
        return list(self.plugins.keys())


class WorkflowEngine:
    """Orchestrates workflow stages with dependencies."""

    def __init__(self):
        self.stages = {}
        self.dependencies = {}

    def add_stage(self, name, callback, depends_on=None):
        self.stages[name] = callback
        self.dependencies[name] = depends_on or []
        return self

    def run_stage(self, name, context=None):
        if name not in self.stages:
            raise KeyError(f'Stage {name} is not registered')
        context = context or {}
        for dependency in self.dependencies.get(name, []):
            self.run_stage(dependency, context)
        return self.stages[name](context)

    def run_all(self, context=None):
        context = context or {}
        for name in list(self.stages.keys()):
            self.run_stage(name, context)
        return context


class ResourceRegistry:
    """Tracks named resources across application layers."""

    def __init__(self):
        self.resources = {}

    def register_resource(self, name, resource):
        self.resources[name] = resource
        return self

    def get_resource(self, name, default=None):
        return self.resources.get(name, default)

    def list_resources(self):
        return list(self.resources.keys())


class DistributedJobScheduler:
    """Schedules jobs using a simple distributed work queue."""

    def __init__(self):
        self.jobs = []
        self.results = {}

    def schedule(self, job_id, callback, *args, **kwargs):
        self.jobs.append((job_id, callback, args, kwargs))
        return self

    def run_next(self):
        if not self.jobs:
            return None
        job_id, callback, args, kwargs = self.jobs.pop(0)
        result = callback(*args, **kwargs)
        self.results[job_id] = result
        return result

    def run_all(self):
        while self.jobs:
            self.run_next()
        return self.results


class SecurityPolicyManager:
    """Manages security policies and validation rules."""

    def __init__(self):
        self.policies = {}

    def add_policy(self, name, callback):
        self.policies[name] = callback
        return self

    def validate(self, name, context=None):
        if name not in self.policies:
            raise KeyError(f'Policy {name} not found')
        return bool(self.policies[name](context or {}))

    def list_policies(self):
        return list(self.policies.keys())


class AuditTrail:
    """Records immutable audit events."""

    def __init__(self):
        self.events = []

    def record_event(self, event_type, details=None):
        self.events.append({
            'type': event_type,
            'details': details,
            'timestamp': current_timestamp(),
        })
        return self

    def query(self, event_type=None):
        if event_type is None:
            return list(self.events)
        return [item for item in self.events if item['type'] == event_type]


class ModelRegistry:
    """Stores models and their metadata."""

    def __init__(self):
        self.models = {}

    def register_model(self, name, model, metadata=None):
        self.models[name] = {
            'model': model,
            'metadata': metadata or {},
        }
        return self

    def get_model(self, name):
        return self.models.get(name)

    def list_models(self):
        return list(self.models.keys())


class AnalyticsEngine:
    """Processes metrics and analytics streams."""

    def __init__(self):
        self.events = []

    def track_event(self, name, properties=None):
        self.events.append({'name': name, 'properties': properties or {}, 'time': current_timestamp()})
        return self

    def summarize(self):
        return {
            'total_events': len(self.events),
            'event_names': list({event['name'] for event in self.events}),
        }


plugin_manager = PluginManager()
workflow_engine = WorkflowEngine()
resource_registry = ResourceRegistry()
job_scheduler = DistributedJobScheduler()
security_policy_manager = SecurityPolicyManager()
audit_trail = AuditTrail()
model_registry = ModelRegistry()
analytics_engine = AnalyticsEngine()

"""
show_abdocode() — واجهة توثيق تفاعلية لمكتبة abdocode
ضعها في نهاية ملف abdocode.py أو استوردها منه
"""

import tkinter as tk
from tkinter import ttk, font as tkfont

# =====================================================================
# بيانات التوثيق الكاملة مصنّفة
# =====================================================================

DOCS = {
    "🔊 الصوت — Sound": {
        "description": "تشغيل نغمات وأصوات برمجياً بدون ملفات خارجية. تدعم موجات متعددة وخلط نغمات.",
        "items": [
            {
                "name": "beep(freq, duration, volume)",
                "signature": "beep(freq=440, duration=0.5, volume=0.5)",
                "desc": "يشغّل نغمة بسيطة بتردد وطول محدد.",
                "example": "beep(440, 0.5, 0.5)  # نغمة 440Hz لمدة نصف ثانية",
            },
            {
                "name": "play_tone(note, duration, volume, waveform)",
                "signature": "play_tone(note, duration=0.2, volume=0.5, waveform='sine')",
                "desc": "يشغّل نغمة موسيقية بالاسم (مثل 'C5') أو بالتردد. يدعم: sine, square, triangle.",
                "example": "play_tone('C5', 0.3, 0.5, 'square')",
            },
            {
                "name": "play_notes(sequence, tempo, volume, waveform)",
                "signature": "play_notes(sequence, tempo=120, volume=0.4, waveform='sine')",
                "desc": "يشغّل تسلسل نغمات. كل عنصر: (اسم_النغمة, الطول).",
                "example": "play_notes([('C4',1),('E4',1),('G4',2)], tempo=120)",
            },
            {
                "name": "play_chord(notes, duration, volume, waveform)",
                "signature": "play_chord(notes, duration=0.28, volume=0.4, waveform='sine')",
                "desc": "يشغّل عدة نغمات في آنٍ واحد (وتر موسيقي).",
                "example": "play_chord(['C5','E5','G5'], duration=0.3)",
            },
            {
                "name": "play_bell(note, duration, volume)",
                "signature": "play_bell(note, duration=0.36, volume=0.38)",
                "desc": "يشغّل صوت جرس بنغمة محددة مع توافقيات.",
                "example": "play_bell('E5')",
            },
            {
                "name": "play_sequence(sequence, tempo, volume, waveform)",
                "signature": "play_sequence(sequence, tempo=120, volume=0.37, waveform='sine')",
                "desc": "مثل play_notes — يشغّل تسلسل نغمات.",
                "example": "play_sequence([('G4',0.2),('C5',0.3)], tempo=150)",
            },
            {
                "name": "play_swipe(notes, duration, volume, waveform)",
                "signature": "play_swipe(notes, duration=0.18, volume=0.36, waveform='sawtooth')",
                "desc": "يشغّل نغمات بشكل سريع متتالي (تأثير المسح).",
                "example": "play_swipe(['G5','A5','B5'])",
            },
            {
                "name": "play_ping(notes, duration, volume, waveform)",
                "signature": "play_ping(notes, duration=0.14, volume=0.42, waveform='sine')",
                "desc": "يشغّل نغمات قصيرة حادة (تأثير ping).",
                "example": "play_ping(['C6','E6'])",
            },
            {
                "name": "play(name)",
                "signature": "play(name)",
                "desc": "يشغّل صوتاً مسجّلاً من sound_map بالاسم. غير متزامن (thread).",
                "example": 'play("Victory")  # يشغّل صوت النصر',
            },
            {
                "name": "add_sound(name, func)",
                "signature": "add_sound(name, func)",
                "desc": "يضيف صوتاً مخصصاً إلى sound_map.",
                "example": 'add_sound("MySound", lambda: play_tone("A4", 0.2))',
            },
            {
                "name": "note_to_frequency(note)",
                "signature": "note_to_frequency(note) → float",
                "desc": "يحوّل اسم نغمة ('C4', 'A#5', 'REST') إلى تردد Hz.",
                "example": 'note_to_frequency("A4")  # → 440.0',
            },
            {
                "name": "play_name_beep(name, volume, speed, waveform)",
                "signature": "play_name_beep(name, volume=0.45, speed=0.08, waveform='sine')",
                "desc": "يشغّل تسلسل نغمات فريد مشتق من أحرف الاسم.",
                "example": 'play_name_beep("AbdoCode")',
            },
            {
                "name": "sound_map",
                "signature": "sound_map  (dict)",
                "desc": "قاموس يحتوي +160 صوتاً جاهزاً. مفاتيح مثل: Victory, Click, Error, LevelUp, Coin, Boss, Laser...",
                "example": 'sound_map["Click"]()  # تشغيل مباشر',
            },
        ],
    },
    "🔧 خلفية الصوت — Sound Backend": {
        "description": "إدارة مكتبة الصوت المستخدمة (sounddevice / simpleaudio / winsound).",
        "items": [
            {
                "name": "available_sound_backends()",
                "signature": "available_sound_backends() → list",
                "desc": "يُرجع قائمة بمكتبات الصوت المتاحة على الجهاز.",
                "example": "available_sound_backends()  # ['sounddevice']",
            },
            {
                "name": "set_sound_backend(name)",
                "signature": "set_sound_backend(name) → bool",
                "desc": "يضبط مكتبة الصوت المستخدمة.",
                "example": 'set_sound_backend("simpleaudio")',
            },
            {
                "name": "sound_backend_info()",
                "signature": "sound_backend_info() → dict",
                "desc": "يُرجع معلومات الخلفية المكتشفة والمتاحة.",
                "example": "sound_backend_info()  # {'detected': 'sounddevice', 'available': [...]}",
            },
        ],
    },
    "⌨️ الاختصارات — Shortcuts": {
        "description": "ربط اختصارات لوحة المفاتيح بأحداث Tkinter وإدارتها.",
        "items": [
            {
                "name": "add_shortcut(root, key, event)",
                "signature": "add_shortcut(root, key, event) → bool",
                "desc": "يضيف اختصاراً جديداً. يُنبّه إذا كان الاختصار مستخدماً.",
                "example": 'add_shortcut(root, "<Control-s>", "<<Save>>")',
            },
            {
                "name": "remove_shortcut(root, key)",
                "signature": "remove_shortcut(root, key) → bool",
                "desc": "يحذف اختصاراً محدداً.",
                "example": 'remove_shortcut(root, "<Control-s>")',
            },
            {
                "name": "use_all(root)",
                "signature": "use_all(root)",
                "desc": "يُسجّل جميع الاختصارات الشائعة دفعةً واحدة (Copy, Paste, Save, Undo...).",
                "example": "use_all(root)",
            },
            {
                "name": "disable_all() / enable_all()",
                "signature": "disable_all()  |  enable_all()",
                "desc": "يوقف أو يعيد تفعيل كل الاختصارات مؤقتاً.",
                "example": "disable_all()  # لا يتجاوب أي اختصار",
            },
            {
                "name": "reset_all(root)",
                "signature": "reset_all(root)",
                "desc": "يحذف جميع الاختصارات المسجّلة.",
                "example": "reset_all(root)",
            },
            {
                "name": "list_shortcuts()",
                "signature": "list_shortcuts() → dict",
                "desc": "يُرجع نسخة من قاموس الاختصارات المسجّلة {key: event}.",
                "example": "list_shortcuts()  # {'<Control-c>': '<<Copy>>', ...}",
            },
            {
                "name": "show_all_shortcuts()",
                "signature": "show_all_shortcuts()",
                "desc": "يعرض نافذة messageBox بكل الاختصارات الحالية.",
                "example": "show_all_shortcuts()",
            },
            {
                "name": "shortcuts_popup_list(root)",
                "signature": "shortcuts_popup_list(root)",
                "desc": "نافذة بحث تفاعلية للاختصارات: بحث، نسخ، حذف، تشغيل.",
                "example": "shortcuts_popup_list(root)",
            },
            {
                "name": "enable_shortcuts_ui(root)",
                "signature": "enable_shortcuts_ui(root)",
                "desc": "يُفعّل النقر الأيمن لعرض القائمة + تسلسل Ctrl+S ثم '.' لعرض الأصوات.",
                "example": "enable_shortcuts_ui(root)",
            },
            {
                "name": "الاختصارات الجاهزة",
                "signature": "copy, paste, cut, select_all, undo, redo, find, replace, goto, duplicate, save, open_file, new_file, print_file, refresh, home, end, ...",
                "desc": "دوال جاهزة لكل اختصار شائع — كل منها يستدعي add_shortcut بالمفتاح الصحيح.",
                "example": "copy(root)  # يربط Ctrl+C\nsave(root)  # يربط Ctrl+S",
            },
        ],
    },
    "📋 الحافظة — Clipboard": {
        "description": "قراءة وكتابة الحافظة مع تاريخ كامل للنصوص المنسوخة.",
        "items": [
            {
                "name": "write_clipboard(root, text)",
                "signature": "write_clipboard(root, text) → bool",
                "desc": "يكتب نصاً في الحافظة ويحفظه في السجل.",
                "example": 'write_clipboard(root, "مرحباً")',
            },
            {
                "name": "read_clipboard(root)",
                "signature": "read_clipboard(root) → str",
                "desc": "يقرأ النص الحالي من الحافظة.",
                "example": "text = read_clipboard(root)",
            },
            {
                "name": "clear_clipboard(root)",
                "signature": "clear_clipboard(root) → bool",
                "desc": "يمسح الحافظة.",
                "example": "clear_clipboard(root)",
            },
            {
                "name": "get_clipboard_history()",
                "signature": "get_clipboard_history() → list",
                "desc": "يُرجع قائمة بكل النصوص التي كُتبت في الحافظة.",
                "example": "history = get_clipboard_history()",
            },
            {
                "name": "paste_clipboard_history_item(root, index)",
                "signature": "paste_clipboard_history_item(root, index) → bool",
                "desc": "يُعيد نسخ عنصر من السجل إلى الحافظة باستخدام رقمه.",
                "example": "paste_clipboard_history_item(root, 0)  # أول عنصر",
            },
        ],
    },
    "📁 الملفات — Files": {
        "description": "قراءة وكتابة وحذف ونقل الملفات والمجلدات بسهولة.",
        "items": [
            {
                "name": "read_text_file(path, encoding)",
                "signature": "read_text_file(path, encoding='utf-8') → str",
                "desc": "يقرأ محتوى ملف نصي كاملاً.",
                "example": 'text = read_text_file("data.txt")',
            },
            {
                "name": "write_text_file(path, text, encoding)",
                "signature": "write_text_file(path, text, encoding='utf-8')",
                "desc": "يكتب نصاً في ملف (يُنشئ المجلدات تلقائياً).",
                "example": 'write_text_file("output.txt", "مرحبا")',
            },
            {
                "name": "append_text_file(path, text, encoding)",
                "signature": "append_text_file(path, text, encoding='utf-8')",
                "desc": "يضيف نصاً إلى نهاية ملف موجود.",
                "example": 'append_text_file("log.txt", "سطر جديد")',
            },
            {
                "name": "list_files(folder, extension, recursive)",
                "signature": "list_files(folder, extension=None, recursive=False) → list",
                "desc": "يُرجع قائمة بالملفات في مجلد، مع فلتر الامتداد.",
                "example": 'list_files("src", extension=".py", recursive=True)',
            },
            {
                "name": "find_files(folder, pattern, recursive, extension)",
                "signature": "find_files(folder, pattern, recursive=True, extension=None) → list",
                "desc": "يبحث عن ملفات بنمط regex.",
                "example": 'find_files(".", r"config.*\\.json")',
            },
            {
                "name": "read_lines / write_lines / append_lines",
                "signature": "read_lines(path) → list\nwrite_lines(path, lines)\nappend_lines(path, lines)",
                "desc": "قراءة/كتابة/إضافة الملف سطراً سطراً.",
                "example": 'lines = read_lines("data.txt")',
            },
            {
                "name": "copy_file / move_file / delete_file",
                "signature": "copy_file(src, dst)\nmove_file(src, dst)\ndelete_file(path) → bool",
                "desc": "نسخ أو نقل أو حذف ملف.",
                "example": 'copy_file("a.txt", "backup/a.txt")',
            },
            {
                "name": "make_dirs / delete_path / copy_path / move_path",
                "signature": "make_dirs(path) → bool\ndelete_path(path) → bool\ncopy_path(src, dst)\nmove_path(src, dst)",
                "desc": "إنشاء أو حذف أو نسخ أو نقل مجلدات كاملة.",
                "example": 'make_dirs("output/data/2025")',
            },
            {
                "name": "path_exists / is_file_path / is_dir_path",
                "signature": "path_exists(path) → bool\nis_file_path(path) → bool\nis_dir_path(path) → bool",
                "desc": "فحص وجود المسار ونوعه.",
                "example": 'if path_exists("config.json"): ...',
            },
            {
                "name": "get_file_size / file_size_safe",
                "signature": "get_file_size(path) → int\nfile_size_safe(path) → int",
                "desc": "يُرجع حجم الملف بالبايت (safe لا يُطلق استثناء).",
                "example": 'size = get_file_size("video.mp4")',
            },
            {
                "name": "open_folder(path)",
                "signature": "open_folder(path) → bool",
                "desc": "يفتح المجلد في مستكشف الملفات (Windows/Mac/Linux).",
                "example": 'open_folder("C:/Users/Abdo/Downloads")',
            },
        ],
    },
    "📝 النصوص — Text": {
        "description": "معالجة النصوص: تنسيق، بحث، تحويل، اختصار.",
        "items": [
            {
                "name": "normalize_whitespace(text)",
                "signature": "normalize_whitespace(text) → str",
                "desc": "يحذف المسافات الزائدة ويُعيد نصاً نظيفاً.",
                "example": 'normalize_whitespace("  hello   world  ")  # "hello world"',
            },
            {
                "name": "count_words(text)",
                "signature": "count_words(text) → int",
                "desc": "يحسب عدد الكلمات في النص.",
                "example": 'count_words("Hello World Abd")  # 3',
            },
            {
                "name": "to_title_case(text)",
                "signature": "to_title_case(text) → str",
                "desc": "يحوّل النص إلى Title Case.",
                "example": 'to_title_case("hello world")  # "Hello World"',
            },
            {
                "name": "snake_to_camel(text) / camel_to_snake(text)",
                "signature": "snake_to_camel(text) → str\ncamel_to_snake(text) → str",
                "desc": "تحويل بين snake_case و camelCase.",
                "example": 'snake_to_camel("hello_world")  # "helloWorld"',
            },
            {
                "name": "remove_punctuation(text)",
                "signature": "remove_punctuation(text) → str",
                "desc": "يحذف علامات الترقيم من النص.",
                "example": 'remove_punctuation("Hello, World!")  # "Hello World"',
            },
            {
                "name": "slugify(text)",
                "signature": "slugify(text) → str",
                "desc": "يحوّل النص إلى slug (للروابط).",
                "example": 'slugify("Hello World!")  # "hello-world"',
            },
            {
                "name": "abbreviate_text / truncate_text",
                "signature": "abbreviate_text(text, max_length=30) → str\ntruncate_text(text, max_length=30) → str",
                "desc": "يقطع النص ويُضيف '...' إذا تجاوز الطول المحدد.",
                "example": 'abbreviate_text("Long text here", 10)  # "Long te..."',
            },
            {
                "name": "is_palindrome / reverse_text",
                "signature": "is_palindrome(text) → bool\nreverse_text(text) → str",
                "desc": "فحص إذا كان النص palindrome أو عكسه.",
                "example": 'is_palindrome("racecar")  # True',
            },
            {
                "name": "contains_substring / starts_with / ends_with",
                "signature": "contains_substring(text, sub) → bool\nstarts_with(text, prefix) → bool\nends_with(text, suffix) → bool",
                "desc": "فحص وجود نص فرعي أو بداية أو نهاية النص.",
                "example": 'starts_with("Hello", "He")  # True',
            },
            {
                "name": "extract_digits(text)",
                "signature": "extract_digits(text) → str",
                "desc": "يستخرج الأرقام فقط من النص.",
                "example": 'extract_digits("abc123def456")  # "123456"',
            },
            {
                "name": "split_words / join_words / repeat_text",
                "signature": "split_words(text) → list\njoin_words(words, sep) → str\nrepeat_text(text, count) → str",
                "desc": "تقسيم، دمج، أو تكرار النص.",
                "example": 'join_words(["a","b","c"], "-")  # "a-b-c"',
            },
        ],
    },
    "⏱️ المؤقتات — Timers": {
        "description": "تنفيذ دوال بتأخير أو تكرار دوري في خيوط مستقلة.",
        "items": [
            {
                "name": "set_timeout(callback, delay, *args)",
                "signature": "set_timeout(callback, delay, *args, **kwargs) → timer_id",
                "desc": "ينفّذ callback مرة واحدة بعد delay ثانية.",
                "example": "set_timeout(lambda: print('done'), 3.0)",
            },
            {
                "name": "set_interval(callback, interval, *args)",
                "signature": "set_interval(callback, interval, *args, **kwargs) → timer_id",
                "desc": "ينفّذ callback بشكل دوري كل interval ثانية.",
                "example": "tid = set_interval(update_ui, 0.5)",
            },
            {
                "name": "cancel_timer(timer_id)",
                "signature": "cancel_timer(timer_id) → bool",
                "desc": "يُلغي مؤقتاً بمعرّفه.",
                "example": "cancel_timer(tid)",
            },
            {
                "name": "active_timers()",
                "signature": "active_timers() → list",
                "desc": "يُرجع قائمة بمعرّفات المؤقتات النشطة.",
                "example": "print(active_timers())",
            },
            {
                "name": "countdown(seconds, callback, interval)",
                "signature": "countdown(seconds, callback=None, interval=1)",
                "desc": "عدّ تنازلي يستدعي callback في كل خطوة.",
                "example": "countdown(10, lambda r: print(r))",
            },
            {
                "name": "wait_seconds(seconds)",
                "signature": "wait_seconds(seconds) → bool",
                "desc": "ينتظر عدداً من الثواني (يوقف الخيط الحالي).",
                "example": "wait_seconds(2.5)",
            },
        ],
    },
    "💻 النظام — System": {
        "description": "معلومات النظام، متغيرات البيئة، تشغيل الأوامر.",
        "items": [
            {
                "name": "get_system_info()",
                "signature": "get_system_info() → dict",
                "desc": "يُرجع معلومات النظام: OS, Python version, CWD, المستخدم...",
                "example": "info = get_system_info()\nprint(info['platform'])",
            },
            {
                "name": "get_environment_variables()",
                "signature": "get_environment_variables() → dict",
                "desc": "يُرجع كل متغيرات البيئة.",
                "example": "env = get_environment_variables()",
            },
            {
                "name": "get_env_var / set_env_var / delete_env_var",
                "signature": "get_env_var(key, default=None)\nset_env_var(key, value)\ndelete_env_var(key)",
                "desc": "قراءة أو ضبط أو حذف متغير بيئة محدد.",
                "example": 'set_env_var("DEBUG", "1")',
            },
            {
                "name": "run_command(command, shell, cwd, timeout)",
                "signature": "run_command(command, shell=False, cwd=None, timeout=None) → dict",
                "desc": "ينفّذ أمر shell ويُرجع {returncode, stdout, stderr}.",
                "example": 'result = run_command("python --version")',
            },
            {
                "name": "run_command_async(command, callback)",
                "signature": "run_command_async(command, callback=None, ...) → Thread",
                "desc": "ينفّذ أمراً في خيط مستقل ويستدعي callback عند الانتهاء.",
                "example": 'run_command_async("pip install X", lambda r: print(r))',
            },
            {
                "name": "which(program)",
                "signature": "which(program) → str | None",
                "desc": "يبحث عن مسار برنامج في PATH.",
                "example": 'which("python")  # "/usr/bin/python"',
            },
            {
                "name": "is_process_running(name)",
                "signature": "is_process_running(name) → bool",
                "desc": "يفحص إذا كانت عملية تعمل حالياً.",
                "example": 'is_process_running("chrome")',
            },
            {
                "name": "kill_process(pid)",
                "signature": "kill_process(pid) → bool",
                "desc": "يوقف عملية بمعرّفها pid.",
                "example": "kill_process(1234)",
            },
        ],
    },
    "🪟 واجهة المستخدم — UI": {
        "description": "نوافذ حوار وإشعارات ومكونات Tkinter جاهزة.",
        "items": [
            {
                "name": "show_info / show_warning / show_error",
                "signature": "show_info(title, message)\nshow_warning(title, message)\nshow_error(title, message)",
                "desc": "نوافذ رسائل معيارية (Info / Warning / Error).",
                "example": 'show_info("نجاح", "تم الحفظ بنجاح")',
            },
            {
                "name": "ask_yes_no(title, message)",
                "signature": "ask_yes_no(title, message) → bool",
                "desc": "يعرض سؤالاً بزرَّي نعم/لا.",
                "example": 'if ask_yes_no("تأكيد", "هل تريد الحذف؟"): ...',
            },
            {
                "name": "ask_input(title, prompt, default)",
                "signature": "ask_input(title, prompt, default='') → str | None",
                "desc": "يعرض مربع إدخال نص.",
                "example": 'name = ask_input("الاسم", "أدخل اسمك:")',
            },
            {
                "name": "simple_popup(root, title, message)",
                "signature": "simple_popup(root, title, message)",
                "desc": "نافذة بسيطة بزر OK.",
                "example": 'simple_popup(root, "مرحباً", "أهلاً بك!")',
            },
            {
                "name": "sounds_popup_list(root)",
                "signature": "sounds_popup_list(root)",
                "desc": "نافذة تفاعلية لعرض وتشغيل كل الأصوات المسجّلة.",
                "example": "sounds_popup_list(root)",
            },
            {
                "name": "prompt_choices(root, title, options)",
                "signature": "prompt_choices(root, title, options, default_index=0) → str | None",
                "desc": "يعرض قائمة خيارات مرقّمة ويُرجع الخيار المحدد.",
                "example": 'choice = prompt_choices(root, "لون", ["أحمر","أزرق","أخضر"])',
            },
            {
                "name": "confirm_action(message, default)",
                "signature": "confirm_action(message='Proceed?', default=True) → bool",
                "desc": "يعرض مربع تأكيد.",
                "example": 'if confirm_action("هل تريد المتابعة؟"): ...',
            },
            {
                "name": "show_progress(title, current, total)",
                "signature": "show_progress(title, current, total) → str",
                "desc": "يُنشئ نص تقدم مئوي.",
                "example": 'show_progress("رفع", 50, 100)  # "رفع: 50% (50/100)"',
            },
        ],
    },
    "🔢 الرياضيات — Math": {
        "description": "عمليات رياضية وإحصائية مع معالجة آمنة للأخطاء.",
        "items": [
            {
                "name": "clamp(value, min, max) / clamp_value",
                "signature": "clamp(value, minimum, maximum) → number",
                "desc": "يُقيّد القيمة بين حد أدنى وأقصى.",
                "example": "clamp(150, 0, 100)  # 100",
            },
            {
                "name": "lerp(start, end, fraction) / lerp_value",
                "signature": "lerp(start, end, fraction) → float",
                "desc": "Linear interpolation بين قيمتين.",
                "example": "lerp(0, 100, 0.25)  # 25.0",
            },
            {
                "name": "remap(value, old_min, old_max, new_min, new_max)",
                "signature": "remap(value, old_min, old_max, new_min, new_max) → float",
                "desc": "يُعيد تعيين قيمة من نطاق إلى آخر.",
                "example": "remap(5, 0, 10, 0, 100)  # 50.0",
            },
            {
                "name": "safe_divide(a, b, default)",
                "signature": "safe_divide(a, b, default=0.0) → float",
                "desc": "قسمة آمنة لا تُطلق استثناء عند القسمة على صفر.",
                "example": "safe_divide(10, 0)  # 0.0",
            },
            {
                "name": "percent_change(original, new)",
                "signature": "percent_change(original, new) → float",
                "desc": "يحسب نسبة التغيير المئوية.",
                "example": "percent_change(100, 120)  # 20.0",
            },
            {
                "name": "is_prime / is_even / is_odd",
                "signature": "is_prime(n) → bool\nis_even(n) → bool\nis_odd(n) → bool",
                "desc": "فحص خصائص الأعداد الصحيحة.",
                "example": "is_prime(17)  # True",
            },
            {
                "name": "factorial_safe / gcd_safe / lcm_safe",
                "signature": "factorial_safe(n) → int\ngcd_safe(a, b) → int\nlcm_safe(a, b) → int",
                "desc": "مضروب، القاسم المشترك الأكبر، المضاعف المشترك الأصغر.",
                "example": "factorial_safe(5)  # 120",
            },
            {
                "name": "الإحصاء — Statistics",
                "signature": "average_list / median_list / mode_list\nvariance_list / stddev_list / percentile\nsum_list / product_list",
                "desc": "دوال إحصائية شاملة على قوائم الأرقام.",
                "example": "average_list([1,2,3,4,5])  # 3.0\npercentile([1..100], 90)  # 90.0",
            },
        ],
    },
    "📦 القوائم والقواميس — Collections": {
        "description": "معالجة القوائم والقواميس: تصفية، تجميع، دمج، تحويل.",
        "items": [
            {
                "name": "chunk_list(sequence, chunk_size)",
                "signature": "chunk_list(sequence, chunk_size) → list",
                "desc": "يقسّم القائمة إلى أجزاء متساوية.",
                "example": "chunk_list([1,2,3,4,5,6], 2)  # [[1,2],[3,4],[5,6]]",
            },
            {
                "name": "flatten_list(nested)",
                "signature": "flatten_list(nested_list) → list",
                "desc": "يُسوّي القائمة المتداخلة.",
                "example": "flatten_list([[1,[2]],3])  # [1,2,3]",
            },
            {
                "name": "unique_preserve / unique_list",
                "signature": "unique_preserve(sequence) → list",
                "desc": "يحذف المكررات مع الحفاظ على الترتيب.",
                "example": "unique_preserve([1,2,1,3])  # [1,2,3]",
            },
            {
                "name": "group_by(sequence, key_function)",
                "signature": "group_by(sequence, key_fn) → dict",
                "desc": "يجمّع عناصر القائمة حسب دالة مفتاح.",
                "example": 'group_by([1,2,3,4], lambda x: x%2)  # {0:[2,4], 1:[1,3]}',
            },
            {
                "name": "map_list / filter_list",
                "signature": "map_list(seq, func) → list\nfilter_list(seq, func) → list",
                "desc": "تطبيق دالة أو فلترة على كل عناصر القائمة.",
                "example": "map_list([1,2,3], lambda x: x*2)  # [2,4,6]",
            },
            {
                "name": "find_index / first_or_default / last_or_default",
                "signature": "find_index(seq, predicate, default=-1)\nfirst_or_default(seq, default=None)\nlast_or_default(seq, default=None)",
                "desc": "البحث عن موضع أو أول/آخر عنصر بأمان.",
                "example": "first_or_default([])  # None",
            },
            {
                "name": "list_difference / list_intersection / list_union",
                "signature": "list_difference(a, b)\nlist_intersection(a, b)\nlist_union(a, b)",
                "desc": "عمليات المجموعات على القوائم.",
                "example": "list_intersection([1,2,3],[2,3,4])  # [2,3]",
            },
            {
                "name": "dict_invert / safe_get_dict / dict_contains_key",
                "signature": "dict_invert(d) → dict\nsafe_get_dict(d, key, default)\ndict_contains_key(d, key) → bool",
                "desc": "عكس القاموس، قراءة آمنة، فحص وجود مفتاح.",
                "example": 'dict_invert({"a":1,"b":2})  # {1:"a", 2:"b"}',
            },
            {
                "name": "merge_dicts / deep_get / deep_set",
                "signature": "merge_dicts(*dicts, deep=True)\ndeep_get(data, 'a.b.c', default)\ndeep_set(data, 'a.b.c', value)",
                "desc": "دمج قواميس متداخلة وقراءة/كتابة مسارات عميقة.",
                "example": 'deep_get(cfg, "db.host", "localhost")',
            },
            {
                "name": "coalesce(*values)",
                "signature": "coalesce(*values) → any",
                "desc": "يُرجع أول قيمة غير None.",
                "example": "coalesce(None, None, 42)  # 42",
            },
        ],
    },
    "✅ التحقق — Validation": {
        "description": "فحص الأنواع والقيم والمخططات.",
        "items": [
            {
                "name": "is_integer / is_float / is_string / is_list / is_dict / is_boolean",
                "signature": "is_integer(v) → bool  |  is_float(v) → bool  |  ...",
                "desc": "فحص نوع القيمة بدقة.",
                "example": "is_integer(42)  # True\nis_integer(42.0)  # False",
            },
            {
                "name": "safe_int / safe_float / safe_bool / safe_str",
                "signature": "safe_int(v, default=0)\nsafe_float(v, default=0.0)\nsafe_bool(v, default=False)\nsafe_str(v, default='')",
                "desc": "تحويل آمن للأنواع دون رمي استثناءات.",
                "example": 'safe_int("abc")  # 0',
            },
            {
                "name": "validate_schema(data, schema, path)",
                "signature": "validate_schema(data, schema) → (bool, list_errors)",
                "desc": "يتحقق من بنية البيانات حسب مخطط JSON-like.",
                "example": 'valid, errors = validate_schema(data, {"type":"object", "properties": {...}})',
            },
            {
                "name": "ensure_schema(data, schema)",
                "signature": "ensure_schema(data, schema) → True | raises ValueError",
                "desc": "مثل validate_schema لكن يُطلق استثناء عند الفشل.",
                "example": "ensure_schema(data, schema)",
            },
            {
                "name": "has_keys / assert_type / is_truthy / is_blank",
                "signature": "has_keys(obj, keys) → bool\nassert_type(v, t) → bool\nis_truthy(v) → bool\nis_blank(text) → bool",
                "desc": "فحوصات سريعة للقيم والمفاتيح.",
                "example": 'has_keys(cfg, ["host","port"])  # True',
            },
            {
                "name": "ensure_list / ensure_dict",
                "signature": "ensure_list(value) → list\nensure_dict(value) → dict",
                "desc": "يضمن إرجاع قائمة أو قاموس حتى لو كانت القيمة غير ذلك.",
                "example": "ensure_list(None)  # []\nensure_list(5)  # [5]",
            },
        ],
    },
    "📅 التاريخ والوقت — DateTime": {
        "description": "معالجة التواريخ والأوقات وحسابها.",
        "items": [
            {
                "name": "current_timestamp / timestamp_now",
                "signature": "current_timestamp() → float\ntimestamp_now() → float",
                "desc": "يُرجع الوقت الحالي كـ Unix timestamp.",
                "example": "ts = current_timestamp()",
            },
            {
                "name": "format_datetime(value, fmt)",
                "signature": "format_datetime(value=None, fmt='%Y-%m-%d %H:%M:%S') → str",
                "desc": "يُنسّق datetime كنص. None = الآن.",
                "example": 'format_datetime(fmt="%d/%m/%Y")',
            },
            {
                "name": "parse_datetime(text, fmt)",
                "signature": "parse_datetime(text, fmt='%Y-%m-%d %H:%M:%S') → datetime",
                "desc": "يُحوّل نصاً إلى datetime.",
                "example": 'parse_datetime("2025-01-15 10:30:00")',
            },
            {
                "name": "relative_time(value)",
                "signature": "relative_time(value, now=None) → str",
                "desc": "يُعبّر عن فارق الوقت بصياغة قابلة للقراءة.",
                "example": 'relative_time(old_dt)  # "2h ago"',
            },
            {
                "name": "today_iso / tomorrow_iso / yesterday_iso",
                "signature": "today_iso() → str  |  tomorrow_iso() → str  |  yesterday_iso() → str",
                "desc": "يُرجع تواريخ اليوم/الغد/الأمس بتنسيق ISO.",
                "example": 'today_iso()  # "2025-07-20"',
            },
            {
                "name": "seconds_since / minutes_since / hours_since / days_since",
                "signature": "seconds_since(dt) → float  |  ...",
                "desc": "يحسب الفارق الزمني من datetime حتى الآن.",
                "example": "hours_since(start_time)  # 2.5",
            },
            {
                "name": "add_days_safe / subtract_days_safe / add_business_days",
                "signature": "add_days_safe(dt, days)\nsubtract_days_safe(dt, days)\nadd_business_days(dt, days)",
                "desc": "إضافة أو طرح أيام من تاريخ (مع دعم أيام العمل).",
                "example": "add_business_days(today, 5)",
            },
            {
                "name": "is_weekend / is_weekend_safe",
                "signature": "is_weekend(value=None) → bool",
                "desc": "يفحص إذا كان التاريخ يوم عطلة.",
                "example": "is_weekend()  # True إذا كان السبت أو الأحد",
            },
        ],
    },
    "🌐 HTTP والشبكة — Network": {
        "description": "طلبات HTTP وتنزيل الملفات ومعالجة URLs.",
        "items": [
            {
                "name": "http_get(url, headers, timeout)",
                "signature": "http_get(url, headers=None, timeout=10) → (body, code, headers)",
                "desc": "يرسل طلب GET ويُرجع المحتوى وكود الحالة والترويسات.",
                "example": 'body, code, _ = http_get("https://api.example.com/data")',
            },
            {
                "name": "http_post(url, data, json_data, headers)",
                "signature": "http_post(url, data=None, json_data=None, ...) → (body, code, headers)",
                "desc": "يرسل طلب POST مع دعم JSON أو form data.",
                "example": 'http_post("https://api.com", json_data={"key":"val"})',
            },
            {
                "name": "fetch_json(url)",
                "signature": "fetch_json(url, headers=None, timeout=10) → dict",
                "desc": "يجلب JSON من URL مباشرة.",
                "example": 'data = fetch_json("https://api.example.com/user/1")',
            },
            {
                "name": "http_get_text / http_post_json_safe",
                "signature": "http_get_text(url) → str | None\nhttp_post_json_safe(url, json_data) → str | None",
                "desc": "نسخ آمنة تُرجع None عند الفشل بدل رمي استثناء.",
                "example": 'html = http_get_text("https://example.com")',
            },
            {
                "name": "check_url(url, timeout)",
                "signature": "check_url(url, timeout=5) → bool",
                "desc": "يفحص إذا كان URL يعمل (يُرجع 2xx/3xx).",
                "example": 'check_url("https://google.com")  # True',
            },
            {
                "name": "download_file(url, dest)",
                "signature": "download_file(url, dest, chunk_size=8192, timeout=10) → str",
                "desc": "ينزّل ملفاً من URL ويحفظه محلياً.",
                "example": 'download_file("https://example.com/file.zip", "local.zip")',
            },
            {
                "name": "url_encode / url_decode / parse_query / build_query",
                "signature": "url_encode(value) → str\nurl_decode(value) → str\nparse_query(query) → dict\nbuild_query(params) → str",
                "desc": "تشفير وفك تشفير URLs ومعالجة query strings.",
                "example": 'url_encode("hello world")  # "hello+world"',
            },
            {
                "name": "load_json_url(url)",
                "signature": "load_json_url(url, headers=None, timeout=10) → dict",
                "desc": "يجلب ويحلّل JSON من URL.",
                "example": 'cfg = load_json_url("https://api.com/config.json")',
            },
        ],
    },
    "💾 JSON والإعدادات — JSON/Config": {
        "description": "قراءة وكتابة ملفات JSON وإدارة إعدادات التطبيق.",
        "items": [
            {
                "name": "load_json / save_json",
                "signature": "load_json(path, encoding='utf-8') → dict\nsave_json(path, data, indent=4)",
                "desc": "قراءة وكتابة ملفات JSON مباشرة.",
                "example": 'config = load_json("config.json")\nsave_json("config.json", config)',
            },
            {
                "name": "safe_json_load / safe_json_dump",
                "signature": "safe_json_load(path) → dict | None\nsafe_json_dump(path, data) → bool",
                "desc": "نسخ آمنة لا تُطلق استثناءات.",
                "example": 'data = safe_json_load("might_not_exist.json") or {}',
            },
            {
                "name": "load_settings / save_settings",
                "signature": "load_settings(path, default=None) → dict\nsave_settings(path, settings)",
                "desc": "تحميل وحفظ إعدادات التطبيق (JSON).",
                "example": 'settings = load_settings("app.json", default={})',
            },
            {
                "name": "get_setting / set_setting / update_settings",
                "signature": "get_setting(settings, key, default)\nset_setting(settings, key, value)\nupdate_settings(settings, updates)",
                "desc": "قراءة وتعديل ودمج الإعدادات.",
                "example": 'theme = get_setting(settings, "ui.theme", "dark")',
            },
            {
                "name": "deep_get / deep_set",
                "signature": "deep_get(data, 'a.b.c', default=None)\ndeep_set(data, 'a.b.c', value)",
                "desc": "قراءة وكتابة مسارات متداخلة في القواميس.",
                "example": 'deep_set(cfg, "db.pool.size", 10)',
            },
            {
                "name": "flatten_settings(settings, separator)",
                "signature": "flatten_settings(settings, separator='.') → dict",
                "desc": "يُسوّي الإعدادات المتداخلة إلى مفاتيح نقطية.",
                "example": '# {"a":{"b":1}} → {"a.b": 1}',
            },
            {
                "name": "merge_dicts(*dicts, deep)",
                "signature": "merge_dicts(*dicts, deep=True) → dict",
                "desc": "يدمج عدة قواميس مع دعم الدمج العميق.",
                "example": 'result = merge_dicts(defaults, user_config)',
            },
        ],
    },
    "📢 السجل — Logging": {
        "description": "تسجيل الأحداث والأخطاء في ملفات أو الكونسول.",
        "items": [
            {
                "name": "set_log_file(path, level, mode)",
                "signature": "set_log_file(path, level=logging.INFO, mode='a') → str",
                "desc": "يُحدد ملف لحفظ السجلات.",
                "example": 'set_log_file("app.log")',
            },
            {
                "name": "log_info / log_warning / log_error / log_debug",
                "signature": "log_info(message)\nlog_warning(message)\nlog_error(message)\nlog_debug(message)",
                "desc": "تسجيل رسائل بمستويات مختلفة.",
                "example": 'log_info("تم بدء التطبيق")\nlog_error("فشل الاتصال")',
            },
            {
                "name": "configure_logging(level, fmt)",
                "signature": "configure_logging(level=logging.INFO, fmt='...') → Logger",
                "desc": "يضبط إعدادات السجل العامة.",
                "example": "configure_logging()",
            },
            {
                "name": "get_log_file()",
                "signature": "get_log_file() → str | None",
                "desc": "يُرجع مسار ملف السجل الحالي.",
                "example": "print(get_log_file())",
            },
        ],
    },
    "🔄 الأدوات المتقدمة — Advanced": {
        "description": "أنماط برمجية متقدمة: EventBus، إعادة المحاولة، التذاكر.",
        "items": [
            {
                "name": "EventBus",
                "signature": "bus = EventBus()\nbus.subscribe(event, listener)\nbus.emit(event, *args)\nbus.unsubscribe(event, listener)\nbus.clear()",
                "desc": "نظام Publish/Subscribe لربط مكونات التطبيق.",
                "example": "bus = EventBus()\nbus.subscribe('login', lambda u: print(u))\nbus.emit('login', 'Abdo')",
            },
            {
                "name": "retry_function(fn, attempts, delay)",
                "signature": "retry_function(fn, attempts=3, delay=0.2, *args) → result",
                "desc": "ينفّذ دالة ويُعيد المحاولة عند الفشل.",
                "example": "result = retry_function(connect_db, attempts=5)",
            },
            {
                "name": "memoize_function(fn)",
                "signature": "memoize_function(fn) → wrapped_fn",
                "desc": "يُضيف تخزين مؤقت (cache) لدالة.",
                "example": "fib = memoize_function(fibonacci)",
            },
            {
                "name": "random_choice_weighted / weighted_choice",
                "signature": "weighted_choice(choices) → item\nchoices = [(item, weight), ...]",
                "desc": "اختيار عشوائي بأوزان مختلفة.",
                "example": "weighted_choice([('A',70),('B',30)])  # 70% A, 30% B",
            },
            {
                "name": "random_string / random_hex / uuid_simple",
                "signature": "random_string(length=8, chars=...) → str\nrandom_hex(length=8) → str\nuuid_simple() → str",
                "desc": "توليد نصوص عشوائية ومعرّفات فريدة.",
                "example": 'token = random_string(32)',
            },
            {
                "name": "progress_bar(iterable, prefix, size)",
                "signature": "progress_bar(iterable, prefix='', size=40) → generator",
                "desc": "يُضيف شريط تقدم في الكونسول لأي iterable.",
                "example": "for item in progress_bar(data, 'معالجة'): process(item)",
            },
            {
                "name": "print_table(rows, headers, padding)",
                "signature": "print_table(rows, headers=None, padding=2)",
                "desc": "يطبع جدولاً منسّقاً في الكونسول.",
                "example": 'print_table([["A",1],["B",2]], headers=["Name","Val"])',
            },
            {
                "name": "shell_quote(text)",
                "signature": "shell_quote(text) → str",
                "desc": "يؤمّن نصاً للاستخدام في أوامر shell.",
                "example": "shell_quote(\"it's fine\")  # it's fine",
            },
        ],
    },
    "🏗️ الفئات المعمارية — Architecture": {
        "description": "فئات جاهزة للتطبيقات الكبيرة: إضافات، سير عمل، جدولة.",
        "items": [
            {
                "name": "PluginManager",
                "signature": "pm = PluginManager()\npm.register(name, plugin)\npm.get_plugin(name)\npm.unregister(name)\npm.list_plugins()",
                "desc": "نظام إدارة إضافات قابل للتوسعة.",
                "example": "pm = PluginManager()\npm.register('auth', AuthPlugin())",
            },
            {
                "name": "WorkflowEngine",
                "signature": "wf = WorkflowEngine()\nwf.add_stage(name, callback, depends_on)\nwf.run_stage(name, context)\nwf.run_all(context)",
                "desc": "محرك تدفق عمل مع دعم التبعيات بين المراحل.",
                "example": "wf = WorkflowEngine()\nwf.add_stage('validate', validate_fn)\nwf.add_stage('save', save_fn, depends_on=['validate'])",
            },
            {
                "name": "ResourceRegistry",
                "signature": "rr = ResourceRegistry()\nrr.register_resource(name, resource)\nrr.get_resource(name)\nrr.list_resources()",
                "desc": "سجل موارد مشتركة عبر طبقات التطبيق.",
                "example": "rr.register_resource('db', db_connection)",
            },
            {
                "name": "DistributedJobScheduler",
                "signature": "js = DistributedJobScheduler()\njs.schedule(job_id, callback, *args)\njs.run_next()\njs.run_all()",
                "desc": "جدولة وتنفيذ مهام بشكل متسلسل.",
                "example": "js.schedule('export', export_fn, data)\njs.run_all()",
            },
            {
                "name": "SecurityPolicyManager",
                "signature": "sp = SecurityPolicyManager()\nsp.add_policy(name, callback)\nsp.validate(name, context)\nsp.list_policies()",
                "desc": "إدارة سياسات الأمان والتحقق منها.",
                "example": "sp.add_policy('admin', lambda ctx: ctx.get('role')=='admin')",
            },
            {
                "name": "AuditTrail",
                "signature": "at = AuditTrail()\nat.record_event(type, details)\nat.query(event_type=None)",
                "desc": "تسجيل أحداث غير قابلة للتعديل مع timestamp.",
                "example": "at.record_event('login', {'user': 'Abdo'})",
            },
            {
                "name": "ModelRegistry",
                "signature": "mr = ModelRegistry()\nmr.register_model(name, model, metadata)\nmr.get_model(name)\nmr.list_models()",
                "desc": "تخزين النماذج وبياناتها الوصفية.",
                "example": "mr.register_model('classifier', my_model, {'version':'1.0'})",
            },
            {
                "name": "AnalyticsEngine",
                "signature": "ae = AnalyticsEngine()\nae.track_event(name, properties)\nae.summarize()",
                "desc": "تتبع أحداث التطبيق وتلخيصها.",
                "example": "ae.track_event('button_click', {'id': 'btn1'})\nae.summarize()",
            },
        ],
    },
    "🔔 الاسم-نغمة — Name Beep": {
        "description": "تسجيل وإدارة نغمات مشتقة من أسماء برمجياً.",
        "items": [
            {
                "name": "register_name_beep(name)",
                "signature": "register_name_beep(name) → key",
                "desc": "يُسجّل نغمة مشتقة من الاسم في sound_map.",
                "example": 'register_name_beep("Abdo")  # يُضيف "Beep_Abdo"',
            },
            {
                "name": "register_name_beep_group(names)",
                "signature": "register_name_beep_group(names)",
                "desc": "يُسجّل نغمات لعدة أسماء دفعةً واحدة.",
                "example": 'register_name_beep_group(["Ali","Sara","Mona"])',
            },
            {
                "name": "list_registered_name_beeps()",
                "signature": "list_registered_name_beeps() → list",
                "desc": "يُرجع أسماء كل النغمات المسجّلة بالبادئة Beep_.",
                "example": "list_registered_name_beeps()  # ['Beep_Abdo', ...]",
            },
            {
                "name": "remove_name_beep(name)",
                "signature": "remove_name_beep(name)",
                "desc": "يحذف نغمة اسم من sound_map.",
                "example": 'remove_name_beep("Abdo")',
            },
        ],
    },
}


# =====================================================================
# دالة show_abdocode الرئيسية
# =====================================================================

def row_show_abdocode():
    """
    يفتح نافذة توثيق تفاعلية لمكتبة abdocode.
    الصفحة الرئيسية تعرض أزراراً لكل صنف.
    النقر على زر يعرض صفحة تفصيلية بجميع الدوال وأمثلة الكود.
    """

    root = tk.Tk()
    root.title("AbdoCode — التوثيق التفاعلي")
    root.geometry("920x640")
    root.resizable(True, True)

    # ---- ألوان ----
    BG       = "#0f1117"
    SURFACE  = "#1a1d2e"
    CARD     = "#242740"
    ACCENT   = "#7c6af7"
    ACCENT2  = "#56cfb2"
    TEXT     = "#e8e8f0"
    MUTED    = "#8b8fa8"
    CODE_BG  = "#12151f"
    CODE_FG  = "#a8d8a0"
    TITLE_FG = "#ffffff"

    root.configure(bg=BG)

    # ---- خطوط ----
    try:
        title_font  = tkfont.Font(family="Segoe UI", size=22, weight="bold")
        head_font   = tkfont.Font(family="Segoe UI", size=13, weight="bold")
        body_font   = tkfont.Font(family="Segoe UI", size=11)
        code_font   = tkfont.Font(family="Consolas",  size=10)
        btn_font    = tkfont.Font(family="Segoe UI", size=11, weight="bold")
        small_font  = tkfont.Font(family="Segoe UI", size=9)
    except Exception:
        title_font = head_font = body_font = btn_font = small_font = None
        code_font  = None

    # =====================================================================
    # إطار التنقل العلوي (breadcrumb)
    # =====================================================================
    nav_frame = tk.Frame(root, bg=SURFACE, height=44)
    nav_frame.pack(fill="x")
    nav_frame.pack_propagate(False)

    nav_label = tk.Label(nav_frame, text="AbdoCode  /", bg=SURFACE,
                         fg=MUTED, font=body_font, padx=14)
    nav_label.pack(side="left", pady=8)

    nav_page = tk.Label(nav_frame, text="الرئيسية", bg=SURFACE,
                        fg=ACCENT2, font=body_font)
    nav_page.pack(side="left")

    back_btn = tk.Button(nav_frame, text="← رجوع", bg=SURFACE, fg=ACCENT,
                         relief="flat", bd=0, font=btn_font, cursor="hand2",
                         activebackground=SURFACE, activeforeground=ACCENT2)
    back_btn.pack(side="right", padx=14)
    back_btn.config(state="disabled")

    # =====================================================================
    # الإطار الرئيسي القابل للتمرير
    # =====================================================================
    main_frame = tk.Frame(root, bg=BG)
    main_frame.pack(fill="both", expand=True)

    canvas = tk.Canvas(main_frame, bg=BG, bd=0, highlightthickness=0)
    vbar   = tk.Scrollbar(main_frame, orient="vertical", command=canvas.yview)
    canvas.configure(yscrollcommand=vbar.set)
    vbar.pack(side="right", fill="y")
    canvas.pack(side="left", fill="both", expand=True)

    inner = tk.Frame(canvas, bg=BG)
    inner_id = canvas.create_window((0, 0), window=inner, anchor="nw")

    def _on_configure(event):
        canvas.configure(scrollregion=canvas.bbox("all"))

    def _on_canvas_resize(event):
        canvas.itemconfig(inner_id, width=event.width)

    inner.bind("<Configure>", _on_configure)
    canvas.bind("<Configure>", _on_canvas_resize)

    def _on_mousewheel(event):
        canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")

    canvas.bind_all("<MouseWheel>", _on_mousewheel)

    # =====================================================================
    # بناء الصفحة الرئيسية
    # =====================================================================
    def build_home():
        for widget in inner.winfo_children():
            widget.destroy()

        nav_page.config(text="الرئيسية")
        back_btn.config(state="disabled")

        # العنوان الرئيسي
        hdr = tk.Frame(inner, bg=BG, pady=24)
        hdr.pack(fill="x", padx=32)

        tk.Label(hdr, text="⚡ AbdoCode", bg=BG, fg=ACCENT,
                 font=title_font).pack(anchor="w")
        tk.Label(hdr, text="مكتبة Python متعددة الأغراض — توثيق تفاعلي شامل",
                 bg=BG, fg=MUTED, font=body_font).pack(anchor="w", pady=(4, 0))

        # إحصاء سريع
        total_items = sum(len(v["items"]) for v in DOCS.values())
        stats_frame = tk.Frame(inner, bg=BG, padx=32)
        stats_frame.pack(fill="x")

        for label, value in [
            ("📦 الأصناف", str(len(DOCS))),
            ("🔧 الدوال والفئات", str(total_items)),
            ("🎵 أصوات جاهزة", "160+"),
            ("⌨️ اختصارات", "31+"),
        ]:
            sf = tk.Frame(stats_frame, bg=CARD, padx=18, pady=10)
            sf.pack(side="left", padx=(0, 10), pady=(0, 20))
            tk.Label(sf, text=value, bg=CARD, fg=ACCENT2, font=head_font).pack()
            tk.Label(sf, text=label, bg=CARD, fg=MUTED, font=small_font).pack()

        # خط فاصل
        tk.Frame(inner, bg=CARD, height=1).pack(fill="x", padx=32, pady=(0, 16))

        # أزرار الأصناف
        tk.Label(inner, text="اختر صنفاً للاطلاع على توثيقه الكامل:",
                 bg=BG, fg=MUTED, font=small_font).pack(anchor="w", padx=32, pady=(0, 10))

        grid = tk.Frame(inner, bg=BG, padx=28)
        grid.pack(fill="x")

        cols = 3
        categories = list(DOCS.keys())
        for idx, cat in enumerate(categories):
            row_idx = idx // cols
            col_idx = idx % cols

            card = tk.Frame(grid, bg=CARD, cursor="hand2",
                            padx=16, pady=14)
            card.grid(row=row_idx, column=col_idx,
                      padx=6, pady=6, sticky="nsew")
            grid.columnconfigure(col_idx, weight=1)

            n_items = len(DOCS[cat]["items"])

            # أيقونة + اسم
            icon_text = cat.split(" ")[0]
            name_text = " ".join(cat.split(" ")[1:])

            top = tk.Frame(card, bg=CARD)
            top.pack(fill="x")
            tk.Label(top, text=icon_text, bg=CARD, fg=ACCENT,
                     font=head_font).pack(side="left")
            tk.Label(top, text=f"  {name_text}", bg=CARD, fg=TEXT,
                     font=head_font).pack(side="left")

            tk.Label(card, text=DOCS[cat]["description"],
                     bg=CARD, fg=MUTED, font=small_font,
                     wraplength=230, justify="left").pack(anchor="w", pady=(6, 8))

            tk.Label(card, text=f"{n_items} دالة / فئة",
                     bg=CARD, fg=ACCENT2, font=small_font).pack(anchor="w")

            # تأثير hover
            def _enter(e, c=card):
                c.config(bg="#2e3257")
                for w in c.winfo_children():
                    _set_bg(w, "#2e3257")

            def _leave(e, c=card):
                c.config(bg=CARD)
                for w in c.winfo_children():
                    _set_bg(w, CARD)

            def _set_bg(widget, color):
                try:
                    widget.config(bg=color)
                    for child in widget.winfo_children():
                        _set_bg(child, color)
                except Exception:
                    pass

            def _click(e, c=cat):
                build_category(c)

            card.bind("<Enter>", _enter)
            card.bind("<Leave>", _leave)
            card.bind("<Button-1>", _click)
            for child in card.winfo_children():
                child.bind("<Button-1>", _click)

        # padding أسفل
        tk.Frame(inner, bg=BG, height=30).pack()
        canvas.yview_moveto(0)

    # =====================================================================
    # بناء صفحة الصنف
    # =====================================================================
    def build_category(category_name):
        for widget in inner.winfo_children():
            widget.destroy()

        nav_page.config(text=category_name)
        back_btn.config(state="normal", command=build_home)

        cat_data = DOCS[category_name]

        # رأس الصنف
        hdr = tk.Frame(inner, bg=BG, pady=20)
        hdr.pack(fill="x", padx=32)

        tk.Label(hdr, text=category_name, bg=BG, fg=ACCENT,
                 font=title_font).pack(anchor="w")
        tk.Label(hdr, text=cat_data["description"],
                 bg=BG, fg=MUTED, font=body_font,
                 wraplength=820, justify="left").pack(anchor="w", pady=(4, 0))

        tk.Frame(inner, bg=CARD, height=1).pack(fill="x", padx=32, pady=(12, 4))

        # بطاقة لكل دالة
        for item in cat_data["items"]:
            card = tk.Frame(inner, bg=SURFACE, padx=20, pady=16)
            card.pack(fill="x", padx=32, pady=5)

            # اسم الدالة
            tk.Label(card, text=item["name"], bg=SURFACE, fg=TITLE_FG,
                     font=head_font).pack(anchor="w")

            # التوقيع (signature)
            sig_frame = tk.Frame(card, bg=CODE_BG)
            sig_frame.pack(fill="x", pady=(6, 6))
            tk.Label(sig_frame, text=item["signature"], bg=CODE_BG,
                     fg=CODE_FG, font=code_font,
                     justify="left", padx=10, pady=6).pack(anchor="w")

            # الشرح
            tk.Label(card, text=item["desc"], bg=SURFACE, fg=TEXT,
                     font=body_font, wraplength=820,
                     justify="left").pack(anchor="w", pady=(2, 6))

            # المثال
            ex_frame = tk.Frame(card, bg=CODE_BG)
            ex_frame.pack(fill="x")
            ex_label = tk.Label(ex_frame,
                                text="◀ مثال:\n" + item["example"],
                                bg=CODE_BG, fg=ACCENT2, font=code_font,
                                justify="left", padx=10, pady=6)
            ex_label.pack(anchor="w")

        # padding أسفل
        tk.Frame(inner, bg=BG, height=30).pack()
        canvas.yview_moveto(0)

    # =====================================================================
    # تشغيل
    # =====================================================================
    build_home()
    root.mainloop()

def show_abdocode():
    if __name__ == "__main__":
        row_show_abdocode()


# show_abdocode()
