#!/bin/bash

# antiX Memory Manager
# This script provides a GUI for managing zram and zswap in antiX Linux
# 3/2025 by anticapitalX and Robin for antiX comunity
# 
# GPL v.3

# dependencies: yad, antix-viewer|links2|(rxvt-unicode,links2), bc, sed, awk, util-linux, antix-goodies

# ver 1.0

# set private process group
[ "$$" != "$(ps -o pgid= "$$" 2>/dev/null | grep -o "[[:digit:]]*")" ] && exec setsid "$(readlink -f "$0")" "$@"

# Source gettext for proper internationalization
TEXTDOMAINDIR=/usr/share/locale
TEXTDOMAIN=antiX-memory-manager
export TEXTDOMAINDIR TEXTDOMAIN
source gettext.sh

# declare variables
ram=$(($(cat /proc/meminfo|grep MemTotal:|tr -s ' '|cut -d' ' -f2)/1024))  # installed RAM in MiB
cores=$(nproc)  # number of cpu cores reported by kernel
algorithms='zstd!lzo!lzo-rle!lz4!lz4hc'  # pool of valid compression algorithms, separated by ! yad separator
pools='zsmalloc!zbud!z3fold'  # pool of valid memory allocator methods, separated by ! yad separator
default_algorithm='zstd'  # preset which default algorithm will be displayed in yad puldowns
default_pool='zsmalloc'  # preset which default memory allocator method will be displayed in yad pulldowns
default_percent_zswap='20'  # preset which default percentage for zswap will be displayed in yad pulldowns.
default_streams="$cores"  # preset which default number of streams will be displayed in yad pulldowns.
export zram_num_devices=1  # number of zram devices to be used, hard coded to 1 here.
export zram_device_0='zram0'  # zram device name to be used for the fist zram device, hard coded here.
export zram_prio_0=100  # swap priority for zram device 0, hard coded to 100 here.
#zram_device_1='zram1'  # not in use currently
#zram_prio_1=100  # not in use currently; swap priority for zram device 1, hard coded to 100 here.
#zram_device_2='zram2'  # not in use currently
#zram_prio_2=100  # not in use currently; swap priority for zram device 2, hard coded to 100 here.
#zram_device_3='zram3'  # not in use currently
#zram_prio_3=100  # not in use currently; swap priority for zram device 3, hard coded to 100 here.
helpfile_01='/dev/shm/aZZM.html'  # declare temporary html helpfile path (Helpfile is gettext translatable).
current_locale=$(locale | grep LANG= | cut -d= -f2 | cut -d. -f1) # locale readout for html helpfile generation
export ICONS='/usr/share/icons/papirus-antix/48x48/apps' # some icons won't show up in all antiX versions when using named icons instead of a full path
export ICONS2='/usr/share/pixmaps'
lockfile='/dev/shm/aZZM.lock' # make sure only a single instance of antiX Memory Manager can run at a time
export fifo_queue_01='/dev/shm/aZZM.fifo.01' # fifo queue for yad progress window
tmp_fenetre_stat='/dev/shm/aZZM.loop' # file exists while kernel statistics readout loop is running.
tmp_fenetre_cb01='/dev/shm/aZZM.cb01'
tmp_fenetre_cb02='/dev/shm/aZZM.cb02'
export lock_return='/dev/shm/aZZM.lock02' # lock button while flushing swaps

# Adjust default_percent_zram defaults dynamically based on system specs (50% for systems with >8GB RAM, 100% for <4GB)
if [ "$ram" -lt 4096 ]; then
    default_percent_zram=100
elif [ "$ram" -gt 8192 ]; then
    default_percent_zram=50
else
    default_percent_zram=75
fi

# Shows a short info message text read from $2 and title from $1
info_box() {
    yad --center --borders=10 --undecorated --skip-taskbar \
        --window-icon="$ICONS/antiXmm.png" \
        --image="$ICONS2/info_blue.png" \
        --title="$1" \
        --text="$2" \
        --timeout=2 --no-buttons
}

# Error-dialog, shows an error dialog box with OK button. Title read from first Positional, Text read from second Positional.
error_box() {
  if ! [ $# -eq 3 ]; then button="$(gettext 'OK')"; else button="$3"; fi
  yad --center --borders=10 \
      --window-icon="$ICONS/antiXmm.png" \
      --image="dialog-error" \
      --title="$1" \
      --text="$2" \
      --button="$button:0"
}

# Check if running as root
if [ "$(id -u)" -ne 0 ]; then
  error_box "$(gettext 'Error')" "$(gettext 'This script must be run as root.')" "$(gettext 'Close')"
  exit 1
fi

# Check for running instance, map and raise it, then exit, if present. Additional lockfile speeds up startup if no instance is running.
if [ -e "$lockfile" ]; then
    for i in "$(gettext 'antiX Memory Manager')" "$(gettext 'zswap Status')" "$(gettext 'zram Status')" "$(gettext 'Memory Statistics')" "$(gettext 'Clear Memory')"; do
        if xdotool search --name "$i" > /dev/null; then
            (coproc (xdotool windowmap $(xdotool search --name "$i")
            xdotool windowraise $(xdotool search --name "$i"))) 2>/dev/null   
        fi
    done
    exit 1
fi

# kill child processes on exit and remove tempfiles
function cleanup {
  rm -f "$helpfile_01"
  rm -f "$fifo_queue_01"
  rm -f "$tmp_fenetre_stat"
  rm -f "$tmp_fenetre_cb01"
  rm -f "$tmp_fenetre_cb02"
  rm -f "$lock_return"
  rm -f "$lockfile"
  kill 0
}
trap cleanup EXIT

>"$lockfile"

# Function to clear page cache and swap
clear_memory() {
>"$lock_return"
  source gettext.sh
  rm -f "$fifo_queue_01"
  # Confirm before clearing memory
  yad --center --borders=10 \
      --window-icon="$ICONS/antiXmm.png" \
      --image="dialog-question" \
      --title="$(gettext 'Clear Memory')" \
      --text="<b>$(gettext 'This will clear page cache and swap memory.')</b>\n\n$(gettext 'The process may take more than 20 Minutes to complete,\ndepending on memory size, swap size fill status, and\ncan‘t be interrupted once started.')\n\n$(gettext 'Continue?')" \
      --button="$(gettext 'Cancel'):1" \
      --button="$(gettext 'Start'):2"
  x=$?
  if [ $x -eq 2 ]; then
    # read zram status and store current settings into variables
    local zram_status_prev=false
    if check_zram; then
        local zram_status_prev=true
        local algorithm="$(cat /sys/block/$zram_device_0/comp_algorithm | grep -oP '\[\K[^\]]+')"  # read compression algorithm currently in use from kernel
        local disksize="$(cat /sys/block/$zram_device_0/disksize)"  # read current zram device size from kernel (in bytes)
        local streams="$(cat /sys/block/$zram_device_0/max_comp_streams)"  # read from kernel current number of streams used
    fi    

    # First, sync to ensure data is written to disk
    sync
    
    # Drop page cache
    echo 1 > /proc/sys/vm/drop_caches

    # Clear swap by turning it off and on again
    a=$(LANG=C free -b | awk '/Swap:/ {print $2}')
    am=$((a/2**20))
    if [ $a -ne 0 ]; then
        status_info; sleep 1;  # some time needed to set up the parallel fifo connection on slow machines
        swapoff_failed=false
        swapoff -a &
        pid_swapoff=$!
        n=0
        while true; do
            b=$(LANG=C free -b | awk '/Swap:/ {print $2}')
            n=$(((a-b)*100/a))
            echo "# $n $(gettext "percent of $am MiB done")" 2>&1 >> $fifo_queue_01
            echo "$n" 2>&1 >> $fifo_queue_01
            sleep .1
            if [ $n -ge 100 ]; then
                break
            else
                (ps -aux | grep $pid_swapoff | grep -v grep >/dev/null) || (swapoff_failed=true;break) # bail out if 100% are not reached but swapoff process has died
            fi
        done
        sleep .1
        swapon -a
        if $zram_status_prev; then
            enable_zram "$algorithm" "$disksize" "$streams"
        fi
    fi
    # Show success message
    if $swapoff_failed; then
        error_box "$(gettext 'Error')" "<b>$(gettext 'Flushing Swap has failed!')</b>\n\n$(gettext 'Swap wasn’t 100% cleared.') $(gettext 'Please check whether you\nhave sufficient RAM for currently running programs.')\n$(gettext 'If not, close some programs before retrying.') "        
        echo 100 2>&1 > $fifo_queue_01 # close status_info window
    else
        info_box "$(gettext 'Success')" "$(gettext 'Memory cache and swap have been cleared.')"
    fi
  fi
  rm -f "$lock_return"
}

status_info() {
    source gettext.sh
    mkfifo "$fifo_queue_01"
    exec 3<> "$fifo_queue_01"
    coproc info (yad --progress --auto-close \
    --width=300 \
    --posx=75 --posy=100 --borders=10 \
    --window-icon="$ICONS/antiXmm.png" \
    --title="$(gettext 'Flush Memory') – $(gettext 'Clearing swap')" \
    --text="\n<b>$(gettext 'Swap is being cleared')</b>\n\t$(gettext 'Please wait')\n" \
    --no-buttons --undecorated --skip-taskbar --on-top <&3
    )
    exec 3>&-
}

check_real_usage() {
    # calculate whether swap can be turned off safely
    local zswap_size_b=0
    local zram_size_b=0
    local orig_data_b="$(swapon --show=USED --noheadings --bytes | paste -sd+ | bc)"
    if check_zswap; then 
        local zswap_size="$(grep -i "Zswap:" /proc/meminfo | awk '{print $2}')"
        local zswap_size_b=$((zswap_size*1024))
    fi
    if check_zram; then
        local zram_size_b="$(cat /sys/block/zram0/mm_stat | awk '{print $3}')"
    fi
    local free_ram_b="$(LANG=C free -b | awk '/Mem:/ {print $7}')"
    local mem_result=$((free_ram_b-orig_data_b+zswap_size_b+zram_size_b))
    #echo "  $free_ram_b (free RAM)" >&2
    #echo "- $orig_data_b (orig data)" >&2
    #echo "+ $zswap_size_b (ZSWAP device)" >&2
    #echo "+ $zram_size_b (ZRAM device)" >&2
    #echo "------------------------" >&2
    #echo "= $mem_result (Free after, Bytes)" >&2
    #echo "= $((mem_result/2**20)) (MiB)" >&2
    if [ $mem_result -le 0 ]; then return 1; fi
    return 0
}

check_real_usage_zram() {
    # calculate whether swap can be turned off safely
    local orig_data_b="$(cat /sys/block/$zram_device_0/mm_stat | awk '{print $1}')"
    local zram_size_b="$(cat /sys/block/zram0/mm_stat | awk '{print $3}')"
    local free_ram_b="$(LANG=C free -b | awk '/Mem:/ {print $7}')"
    local mem_result=$((free_ram_b-orig_data_b+zram_size_b))
    #echo "  $free_ram_b (free RAM)" >&2
    #echo "- $orig_data_b (orig data)" >&2
    #echo "+ $zram_size_b (ZRAM device)" >&2
    #echo "------------------------" >&2
    #echo "= $mem_result (Free after, Bytes)" >&2
    #echo "= $((mem_result/2**20)) (MiB)" >&2
    if [ $mem_result -le 0 ]; then return 1; fi
    return 0
}

check_real_usage_zswap() {
    # calculate whether swap can be turned off safely
    local orig_data="$(grep -i 'Zswapped' /proc/meminfo | awk '{print $2}')"
    local orig_data_b=$((orig_data*1024))
    local zswap_size="$(grep -i "Zswap:" /proc/meminfo | awk '{print $2}')"
    local zswap_size_b=$((zswap_size*1024))
    local free_ram_b="$(LANG=C free -b | awk '/Mem:/ {print $7}')"
    local mem_result=$((free_ram_b-orig_data_b+zswap_size_b))
    #echo "  $free_ram_b (free RAM)" >&2
    #echo "- $orig_data_b (orig data)" >&2
    #echo "+ $zswap_size_b (ZSWAP device)" >&2
    #echo "------------------------" >&2
    #echo "= $mem_result (Free after, Bytes)" >&2
    #echo "= $((mem_result/2**20)) (MiB)" >&2
    if [ $mem_result -le 0 ]; then return 1; fi
    return 0
}


read_mem_stats() {
    # Get current memory statistics
    local total_ram=$(LANG=C free -m | awk '/Mem:/ {print $2}')
    local used_ram=$(LANG=C free -m | awk '/Mem:/ {print $3}')
    local free_ram=$(LANG=C free -m | awk '/Mem:/ {print $4}')
    local shared_ram=$(LANG=C free -m | awk '/Mem:/ {print $5}')
    local cache_ram=$(LANG=C free -m | awk '/Mem:/ {print $6}')
    local avail_ram=$(LANG=C free -m | awk '/Mem:/ {print $7}')
  
    local total_swap=$(LANG=C free -m | awk '/Swap:/ {print $2}')
    local used_swap=$(LANG=C free -m | awk '/Swap:/ {print $3}')
    local free_swap=$(LANG=C free -m | awk '/Swap:/ {print $4}')
  
    # Get detailed swap info
    local swap_devices=$(LANG=C swapon --show=NAME,SIZE,USED --bytes | tail -n +2 | tr -s ' ' | cut -d' ' -f1 | tr '\n' '\t')
  
    # Get cache info
    local page_cache=$(cat /proc/meminfo | grep -i "^cached:" | awk '{print $2}')
    local page_cache_mb=$((page_cache/1024))

    echo "$total_ram"
    echo "$used_ram"
    echo "$free_ram"
    echo "$shared_ram"
    echo "$cache_ram"
    echo "$avail_ram"
    echo "$page_cache_mb"
    echo "$total_swap"
    echo "$used_swap"
    echo "$free_swap"
    echo "$swap_devices"
    if ! check_real_usage; then
        echo "@disabled@"
    else
        if [ -e "$lock_return" ]; then 
            echo "@disabled@"
        else
            echo 'bash -c "clear_memory"'
        fi
    fi
    if [ -e "$lock_return" ]; then 
        echo "@disabled@"
    else    
        echo 'bash -c "kill -USR1 $YAD_PID"'
    fi
}

# Function to show memory and swap status
show_memory_stats() {
  while true; do
    local back=false
    # Display memory statistics
>"$tmp_fenetre_stat"
    while [ -f "$tmp_fenetre_stat" ]; do read_mem_stats; sleep 1; done | \
    yad --center --borders=10 --width=500 \
        --window-icon="$ICONS/antiXmm.png" \
        --title="$(gettext 'Memory Statistics')" \
        --text="<big><b>$(gettext 'Memory and Swap Statistics')</b></big>\n" \
        --form --columns=2 \
        --field="<b>$(gettext 'RAM Total (MiB):')</b>":RO \
        --field="<b>$(gettext 'RAM Used (MiB):')</b>":RO \
        --field="<b>$(gettext 'RAM Free (MiB):')</b>":RO \
        --field="<b>$(gettext 'RAM Shared (MiB):')</b>":RO \
        --field="<b>$(gettext 'RAM Cache (MiB):')</b>":RO \
        --field="<b>$(gettext 'RAM Available (MiB):')</b>":RO \
        --field="<b>$(gettext 'Page Cache (MiB):')</b>":RO \
        --field="<b>$(gettext 'Swap Total (MiB):')</b>":RO \
        --field="<b>$(gettext 'Swap Used (MiB):')</b>":RO \
        --field="<b>$(gettext 'Swap Free (MiB):')</b>":RO \
        --field="<b>$(gettext 'Swap Devices:')</b>":RO \
        --field="$(gettext 'Clear Memory')!!$(gettext 'If this button is greyed out, close some programs until sufficient memory is available to flush the swaps.')":FBTN \
        --field="$(gettext 'Back')!!$(gettext 'Return to antiX Memory Manager main window.')":FBTN \
        --no-buttons \
        --cycle-read >/dev/null
  
    x=$?
    rm -f "$tmp_fenetre_stat"
    if [ $x -eq 0 ]; then
        break
    elif [ $x -eq 252 ]; then
        [ -e "$lock_return" ] || exit 0
    fi
  done
}

# Function to check if zram is already enabled
check_zram() {
  if ! grep -q '^/dev/zram' /proc/swaps; then
      return 1
  fi
  return 0
}

# Function to check if zswap is already enabled
check_zswap() {
  if ! [ "$(cat /sys/module/zswap/parameters/enabled 2>/dev/null)" = "Y" ]; then
    return 1
  fi
  return 0
}

# Function to check if zswap persistent setting exist
check_persistent_zswap() {
  [ -f /usr/local/bin/zswap-setup.sh ] && return 0
  return 1
}

# Function to check if zram persistent setting exist
check_persistent_zram() {
  [ -f /usr/local/bin/zram-setup.sh ] && return 0
  return 1
}

# Function to enable zram
enable_zram() {
  local zram_algorithm=$1  # user selected compression algorithm
  local zram_size=$2  # user selected device size
  local zram_streams=$3  # user selected streams count
  
  # Load zram module
  modprobe zram num_devices=$zram_num_devices
  
  # Set compression algorithm
  echo "$zram_algorithm" > /sys/block/$zram_device_0/comp_algorithm
  
  # Set memory limit
  echo "$zram_size" > /sys/block/$zram_device_0/disksize
  
  # Set number of compression streams
  echo "$zram_streams" > /sys/block/$zram_device_0/max_comp_streams
  
  # Format as swap and enable
  mkswap /dev/$zram_device_0
  swapon -p $zram_prio_0 /dev/$zram_device_0
}

# Function to disable zram
disable_zram() {
  # check whether we can do that safely
  if check_real_usage_zram; then
      a="$(cat /sys/block/$zram_device_0/mm_stat | awk '{print $1}')"
      a=$((a-4096)) # mm-stat returns additional 4096 bytes for some strange reason after mkswap.
      am=$((a/2**20))
      # Check if zram is in use
      if swapon -s | grep -q $zram_device_0; then
          if test $a -gt 0; then # avoid division by zero
              status_info; sleep 1;  # some time needed to set up the parallel fifo connection on slow machines
              swapoff_failed=false
              swapoff /dev/$zram_device_0 &
              pid_swapoff=$!
              n=0
              while true; do
                  b="$(cat /sys/block/$zram_device_0/mm_stat | awk '{print $1}')"
                  b=$((b-4096))
                  n=$(((a-b)*100/a))
                  echo "# $n $(gettext "percent of $am MiB done")" 2>&1 >> $fifo_queue_01
                  echo "$n" 2>&1 >> $fifo_queue_01
                  sleep .1
                  if [ $n -ge 100 ]; then
                      break
                  else
                      (ps -aux | grep $pid_swapoff | grep -v grep >/dev/null) || (swapoff_failed=true;break)
                  fi
              done
          else
              swapoff_failed=false
              swapoff /dev/$zram_device_0
          fi
          sleep .1
          # Show success message
          if $swapoff_failed; then
              error_box "$(gettext 'Error')" "<b>$(gettext 'Flushing Zram has failed!')</b>\n\n$(gettext 'Zram wasn’t 100% cleared.') $(gettext 'Please check whether you\nhave sufficient RAM for currently running programs.')\n$(gettext 'If not, close some programs before retrying.') "        
              echo 100 2>&1 > $fifo_queue_01 # close status_info window
          else
              info_box "$(gettext 'Success')" "$(gettext 'Zram has been cleared.')"
          fi
      fi  

      # Unload module
      rmmod zram
  else
      error_box "$(gettext 'Insufficient Memory')" "$(gettext 'Disabling Zram failed.')\n\n$(gettext 'Please free up some space in\nmemory by closing some running\nprograms before retrying.')"
      return 1
  fi
  return 0
}

# Function to enable zswap
enable_zswap() {
  local zswap_algorithm=$1
  local zswap_pool=$2
  local zswap_max_pool_percent=$3
  
  # Set parameters
  echo "$zswap_pool" > /sys/module/zswap/parameters/zpool
  echo "$zswap_algorithm" > /sys/module/zswap/parameters/compressor
  echo "$zswap_max_pool_percent" > /sys/module/zswap/parameters/max_pool_percent
  echo 1 > /sys/module/zswap/parameters/enabled
  
  # Ensure swap is enabled (from fstab)
  swapon -a
}

# Function to disable zswap
disable_zswap() {
  # check whether we can do that safely
#  if check_real_usage_zswap; then
      # Disable zswap
      echo 0 > /sys/module/zswap/parameters/enabled
#  else
#      error_box "$(gettext 'Insufficient Memory')" "$(gettext 'Disabling Zswap failed.')\n\n$(gettext 'Please free up some space in\nmemory by closing some running\nprograms before retrying.')"
#  fi
}

# Function to make zram settings permanent
make_zram_permanent() {
  local zram_algorithm=$1   # currently enabled compression algorithm
  local zram_disksize=$2  # currently enabled max disksize in bytes
  local zram_streams=$3  # currently enabled number of streams
  local zram_percent=$((zram_disksize*100/(ram*2**20)))   # calculated currently enabled percentage

  # Create the setup script
  cat > /usr/local/bin/zram-setup.sh << EOF
#!/bin/bash

# Load zram module
modprobe zram num_devices=$zram_num_devices

# Set compression algorithm
echo "$zram_algorithm" > /sys/block/$zram_device_0/comp_algorithm

# Set memory limit (${zram_percent}% of RAM)
echo "$zram_disksize" > /sys/block/$zram_device_0/disksize

# Set number of compression streams
echo "$zram_streams" > /sys/block/$zram_device_0/max_comp_streams

# Format as swap and enable
mkswap /dev/$zram_device_0
swapon -p $zram_prio_0 /dev/$zram_device_0
EOF

  # Make the script executable
  chmod +x /usr/local/bin/zram-setup.sh

  # Add to rc.local for boot-time configuration
  if [ -f /etc/rc.local ]; then
    # Check if already configured
    if ! grep -q "zram-setup.sh" /etc/rc.local; then
      # Add before the exit line
      sed -i '/^[[:blank:]]*exit 0[[:blank:]]*$/i \/usr\/local\/bin\/zram-setup.sh' /etc/rc.local
    fi
  fi
}

# Function to make zswap settings permanent
make_zswap_permanent() {
  local zswap_algorithm=$1  # user selected algorithm
  local zswap_pool=$2  # user selected allocation method
  local zswap_max_pool_percent=$3  # user selected percentage of RAM to be used for the allocator

  # Create the setup script
  cat > /usr/local/bin/zswap-setup.sh << EOF
#!/bin/bash

# Set parameters
echo "$zswap_pool" > /sys/module/zswap/parameters/zpool
echo "$zswap_algorithm" > /sys/module/zswap/parameters/compressor
echo "$zswap_max_pool_percent" > /sys/module/zswap/parameters/max_pool_percent
echo 1 > /sys/module/zswap/parameters/enabled

# Ensure swap is enabled (from fstab)
swapon -a
EOF

  # Make the script executable
  chmod +x /usr/local/bin/zswap-setup.sh

  # Add to rc.local for boot-time configuration
  if [ -f /etc/rc.local ]; then
    # Check if already configured
    if ! grep -q "zswap-setup.sh" /etc/rc.local; then
      # Add before the exit line
      sed -i '/^[[:blank:]]*exit 0[[:blank:]]*$/i \/usr\/local\/bin\/zswap-setup.sh' /etc/rc.local
    fi
  fi
}

# Function to remove permanent settings
remove_permanent_settings_zswap() {

  # Remove common files
  rm -f /usr/local/bin/zswap-setup.sh

  # Remove from rc.local if present
  if [ -f /etc/rc.local ]; then
    sed -i '/zswap-setup.sh/d' /etc/rc.local
  fi
}

# Function to remove permanent settings
remove_permanent_settings_zram() {

  # Remove common files
  rm -f /usr/local/bin/zram-setup.sh

  # Remove from rc.local if present
  if [ -f /etc/rc.local ]; then
    sed -i '/zram-setup.sh/d' /etc/rc.local
  fi
}

#Main menu
main_menu() {
  while true; do
    yad --center --borders=10 --width=500 --height=170 \
      --window-icon="$ICONS/antiXmm.png" \
      --title="$(gettext 'antiX Memory Manager')" \
      --form --columns=2 \
      --field="                    <b>$(gettext 'Current Status')</b>":LBL " " \
      --field="":LBL "" \
      --field="  <b>$(gettext 'zram:')</b>        ":RO "$(if check_zram; then gettext 'Enabled'; else gettext 'Disabled'; fi)" \
      --field="":LBL "" \
      --field="  <b>$(gettext 'zswap:')</b>        ":RO "$(if check_zswap; then gettext 'Enabled'; else gettext 'Disabled'; fi)" \
      --field="":LBL "" \
      --field="<b>$(gettext 'System Startup Status')</b>":LBL " " \
      --field="":LBL "" \
      --field=' ':RO "$(if check_persistent_zram; then gettext 'Enabled'; else gettext 'Disabled'; fi;)" \
      --field="":LBL "" \
      --field=' ':RO "$(if check_persistent_zswap; then gettext 'Enabled'; else gettext 'Disabled'; fi;)" \
      --field="":LBL "" \
      --button="$(gettext 'Zram')!!$(gettext 'Display detailed Zram usage statistics if applicable. Engage or Disengage Zram or make it permanent in system startup Configuration.'):2" \
      --button="$(gettext 'Zswap')!!$(gettext 'Display detailed Zswap usage statistics if applicable. Engage or Disengage Zswap or make it permanent in system startup Configuration.'):4" \
      --button="$(gettext 'Memory')!!$(gettext 'Display detailed Memory and general Swap usage statistics. Flush Swaps and Caches.'):6" \
      --button="$(gettext 'Help')!!$(gettext 'Open the antiX Memory Manager manual with detailed usage instructions and background information.'):0" \
      --button="$(gettext 'Exit')!!$(gettext 'Leave antiX Memory Manager with current status.'):1" >/dev/null
    
    exit_code=$?
    if [ $exit_code -eq 1 ] || [ $exit_code -eq 252 ]; then
      break
    elif [ $exit_code -eq 0 ]; then
      if xdotool search --name "$(gettext 'antiX Memory Manager Help')" > /dev/null; then
        (coproc winmanage (xdotool windowmap $(xdotool search --name "$(gettext 'antiX Memory Manager Help')")
        xdotool windowraise $(xdotool search --name "$(gettext 'antiX Memory Manager Help')"))) 2>/dev/null
      else
        help
      fi
    elif [ $exit_code -eq 2 ]; then
      zram_settings
    elif [ $exit_code -eq 4 ]; then
      zswap_settings
    elif [ $exit_code -eq 6 ]; then
      show_memory_stats
    fi
  done
}

read_zram_stats() {
            # initialise variables with initial value to be displayed
            local compress_ratio="$(gettext 'N/A')" 

            # Read dynamic values from system into local variables and calculate derived values
            local current_algorithm="$(cat /sys/block/$zram_device_0/comp_algorithm | grep -oP '\[\K[^\]]+')"  # read compression algorithm currently in use from kernel
            local current_disksize="$(cat /sys/block/$zram_device_0/disksize)"  # read current zram device size from kernel (in bytes)
            local current_streams="$(cat /sys/block/$zram_device_0/max_comp_streams)"  # read from kernel current number of streams used
            local mem_used="$(cat /sys/block/$zram_device_0/mm_stat | awk '{print $3}')"  # read current usage stats (used_mem, compressed, with overhead) from kernel
            local data_size="$(cat /sys/block/$zram_device_0/mm_stat | awk '{print $1}')"  # read current usage stats (data_size, original uncompressed) from kernel

            # calculate values and fill in text strings for yad display
            local current_disksize_mb=$((current_disksize/2**20))  # current disksize calculated in MiB
            local ram_bytes=$((ram*2**20))   # ram size calculated in bytes
            local current_percent=$((current_disksize*100/ram_bytes))  # current calculated percentage
            local mem_used_mb=$((mem_used/2**20))  # mem_used calculated in MiB
            local data_size_mb=$((data_size/2**20))  # data_size calculated in MiB
            if [ "$data_size" -gt 0 ] && [ "$mem_used" -gt 0 ] ; then  # avoid division by zero error
                local compress_ratio=$(echo "scale=2; $data_size / $mem_used" | bc | awk '{printf "%.2f", $0}')
            fi

            echo "$(eval_gettext "Device: /dev/\${zram_device_0}")\n\
$(eval_gettext "Size: \${current_disksize_mb} MiB")\n\
    $(eval_gettext "(Equivalent to \${current_percent}% of RAM size)")\n\
$(eval_gettext "Algorithm: \${current_algorithm}")\n\
$(eval_gettext "Streams: \${current_streams}")\n"
            echo "$(eval_gettext "Memory Used: \${mem_used_mb} MiB")\n\
$(eval_gettext "Original Data Size: \${data_size_mb} MiB")\n\
$(eval_gettext "Compression Ratio: \${compress_ratio}x")\n"
            echo ""
            if [ -f "$tmp_fenetre_cb01" ]; then cat "$tmp_fenetre_cb01"; else echo ""; fi
            if [ -f "$tmp_fenetre_cb02" ]; then cat "$tmp_fenetre_cb02"; else echo ""; fi
            echo ""
            rm -f "$tmp_fenetre_cb01" >/dev/null 2>&1 # only set in first cycle
            rm -f "$tmp_fenetre_cb02" >/dev/null 2>&1 
}

# zram status and settings menu
zram_settings() {
    while true; do
        local back=false

        if check_zram; then
            # zram is enabled, show current status and disable option
    
            # manage permanent settings status display and buttons
            if ! check_persistent_zram; then
                local txt_persist="$(gettext 'but') <span foreground=\"red\"><b>$(gettext 'not permanent')</b></span> $(gettext 'in system startup.')"
                local checkbox_permanence="--field=$(gettext 'Make current settings permanent in system startup'):CHK"
                local checkbox_switch="--field=$(gettext 'Disable Zram for current session'):CHK"
                echo 'FALSE' > "$tmp_fenetre_cb01" # permanence
                echo 'TRUE' > "$tmp_fenetre_cb02" # switch
            else
                local txt_persist="$(gettext 'and') <span foreground=\"green\"><b>$(gettext 'permanent')</b></span> $(gettext 'in system startup.')"
                local checkbox_permanence="--field=$(gettext 'Remove permanent settings from system startup'):CHK"
                local checkbox_switch="--field=$(gettext 'Disable Zram for current session'):CHK"
                echo 'TRUE' > "$tmp_fenetre_cb01" # permanence
                echo 'TRUE' > "$tmp_fenetre_cb02" # switch
            fi

>"$tmp_fenetre_stat"
            choice=$(while [ -f "$tmp_fenetre_stat" ]; do read_zram_stats; sleep 1; done | yad \
                --center --borders=10 --width=500 \
                --hscroll-policy=never \
                --window-icon="$ICONS/antiXmm.png" \
                --title="$(gettext 'zram Status')" \
                --text="<big><b>$(gettext 'zram Status:')</b></big>\n\n\
$(gettext 'zram is currently') <span foreground=\"green\"><b>$(gettext 'enabled')</b></span>,\n\
$txt_persist\n" \
                --form \
                --field="<u>$(gettext 'Settings')</u>":TXT \
                --field="<u>$(gettext 'Usage Statistics')</u>":TXT \
                --field=:LBL \
                "$checkbox_permanence" \
                "$checkbox_switch" \
                --field=:LBL \
                --button="$(gettext 'Apply')!!$(gettext 'Before pressing Apply make sure to have ticked or unticked the appropriate checkboxes.') $(gettext 'Disabling Zram for the current session will immediately decompress the data and can be only processed if enough free space in RAM is available.'):2" \
                --button="$(gettext 'Back')!!$(gettext 'Return to antiX Memory Manager main window.'):1" \
                --cycle-read)
    
            exit_code=$?
            rm -f "$tmp_fenetre_stat"
            if [ $exit_code -eq 2 ]; then
                IFS='|' read -r _ _ _ permanence switch _ <<< "$choice"
                if ${permanence,,}; then
                    if check_persistent_zram; then
                        remove_permanent_settings_zram
                    else
                        make_zram_permanent "$current_algorithm" "$current_disksize" "$current_streams"
                    fi
                fi
                if ${switch,,}; then
                    disable_zram
                fi
            elif [ $exit_code -eq 1 ]; then
                back=true
            elif [ $exit_code -eq 252 ]; then
                exit 0
            fi

        else
        # zram is disabled, show enable options

            # manage permanent settings status display and buttons
            if check_persistent_zram; then
                button_permanent="--button="$(gettext 'Remove zram permanent settings')!!$(gettext 'This button will not affect the zram status of the current session.'):4""
                txt_persist=",\n$(gettext 'but') <span foreground=\"green\"><b>$(gettext 'permanent')</b></span> $(gettext 'in system startup.')"
                checkbox_permanence="--field=$(gettext 'Update permanent settings when enabling'):CHK"
                permanence='TRUE'
            else
                button_permanent=''
                txt_persist='.'
                checkbox_permanence="--field=$(gettext 'Make settings permanent when enabling'):CHK"
                permanence='TRUE'
            fi

            choice=$(yad --center --borders=10 --width=400 --height=350 \
                --window-icon="$ICONS/antiXmm.png" \
                --title="$(gettext 'zram Settings')" \
                --text="<big><b>$(gettext 'zram Status:')</b></big>\n\n\
$(gettext 'zram is currently') <span foreground=\"red\"><b>$(gettext 'disabled')</b></span>$txt_persist\n" \
                --form \
                --field="$(gettext 'RAM Size (MiB)')":RO "$ram" \
                --field="$(gettext 'Compression Algorithm')":CB "$algorithms" \
                --field="$(gettext 'Size (% of RAM)')":NUM "$default_percent_zram!10..200!1" \
                --field="$(gettext 'CPU Cores')":RO "$cores" \
                --field="$(gettext 'Compression Streams')":NUM "$default_streams!1..$cores!1" \
                --field=:LBL \
                "$checkbox_permanence" \
                --field=:LBL \
                FALSE $permanence FALSE \
                "$button_permanent" \
                --button="$(gettext 'Enable zram with above settings')!!$(gettext 'Before pressing Enable make sure to have selected all appropriate settings from the pulldowns, entry fields and checkboxes.'):2" \
                --button="$(gettext 'Back')!!$(gettext 'Return to antiX Memory Manager main window.'):1")
    
            exit_code=$?
                if [ $exit_code -eq 2 ]; then
                    IFS='|' read -r _ algorithm percent _ streams _ permanence _<<< "$choice"
                    # Calculate needed size in bytes from percentage of RAM
                    disksize=$((ram*2**20*percent/100))  # global $ram is in MiB            
                    # Remove zram module if present
                    if lsmod | grep -q ^zram[[:blank:]]; then
                        rmmod zram # we can do this here safely since we know zswap is disabled already
                    fi
                    # Enable zram immediately
                    enable_zram "$algorithm" "$disksize" "$streams"
                    if ${permanence,,}; then
                        if check_persistent_zram; then
                            # update persistent settings
                            remove_permanent_settings_zram
                            make_zram_permanent "$algorithm" "$disksize" "$streams"
                            info_box "$(gettext 'Success')" "$(gettext 'System startup entries have been updated.')"
                        else
                            # enter persistent settings
                            make_zram_permanent "$algorithm" "$disksize" "$streams"
                        fi
                    fi
                elif [ $exit_code -eq 4 ]; then
                    remove_permanent_settings_zram
                elif [ $exit_code -eq 1 ]; then
                    back=true
                elif [ $exit_code -eq 252 ]; then
                    exit 0
                fi
            fi
        if $back; then break; fi
    done
}

read_zswap_stats() {
            # initialise variables with initial value to be displayed
            local compressed_size="$(gettext 'N/A')"
            local original_size="$(gettext 'N/A')"
            local pages_in="$(gettext 'N/A')"
            local pages_out="$(gettext 'N/A')"
            local ratio="$(gettext 'N/A')"
    
            # Read dynamic values from system into local variables
            local current_algorithm="$(cat /sys/module/zswap/parameters/compressor)"  # read currently used algorithm from kernel
            local current_pool="$(cat /sys/module/zswap/parameters/zpool)"  # read currently used allocator method from kernel
            local current_max_percent="$(cat /sys/module/zswap/parameters/max_pool_percent)"   # read currently set max pool percentage from kernel
            local zswap_size="$(grep -i "Zswap:" /proc/meminfo | awk '{print $2}')"
            local zswapped_size="$(grep -i "Zswapped" /proc/meminfo | awk '{print $2}')"
            local zswpin="$(grep -i "zswpin" /proc/vmstat | awk '{print $2}')"
            local zswpout="$(grep -i "zswpout" /proc/vmstat | awk '{print $2}')"

            # calculate human readable values and fill in text strings for yad display
            if [ -n "$zswap_size" ] && [ -n "$zswapped_size" ] && [ "$zswap_size" -gt 0 ]; then
                ratio=$(echo "scale=2; $zswapped_size / $zswap_size" | bc)
            fi
            if [ -n "$zswap_size" ]; then
                compressed_size="${zswap_size} $(gettext 'KiB')"
            fi
            if [ -n "$zswapped_size" ]; then
                original_size="${zswapped_size} $(gettext 'KiB')"
            fi
            if [ -n "$zswpin" ]; then
                pages_in="$zswpin"
            fi
            if [ -n "$zswpout" ]; then
                pages_out="$zswpout"
            fi

            echo "$(eval_gettext "Algorithm: \${current_algorithm}")\n\
$(eval_gettext "Pool: \${current_pool}")\n\
$(eval_gettext "Max Pool Percent: \${current_max_percent}%")\n"
            echo "$(eval_gettext "Compressed Size: \${compressed_size}")\n\
$(eval_gettext "Original Size: \${original_size}")\n\
$(eval_gettext "Compression Ratio: \${ratio}x")\n\
$(eval_gettext "Pages in: \${pages_in}")\n\
$(eval_gettext "Pages out: \${pages_out}")\n"
            echo ""
            if [ -f "$tmp_fenetre_cb01" ]; then cat "$tmp_fenetre_cb01"; else echo ""; fi
            if [ -f "$tmp_fenetre_cb02" ]; then cat "$tmp_fenetre_cb02"; else echo ""; fi
            echo ""
            rm -f "$tmp_fenetre_cb01" >/dev/null 2>&1 # only set in first cycle
            rm -f "$tmp_fenetre_cb02" >/dev/null 2>&1 
}

# zswap status and settings menu
zswap_settings() {
    while true; do
        local back=false
  
        if check_zswap; then
            # zswap is enabled, show current status and disable option

            # manage permanent settings status display and buttons
            if ! check_persistent_zswap; then
                local txt_persist="$(gettext 'but') <span foreground=\"red\"><b>$(gettext 'not permanent')</b></span> $(gettext 'in system startup.')"
                local checkbox_permanence="--field=$(gettext 'Make current settings permanent in system startup'):CHK"
                local checkbox_switch="--field=$(gettext 'Disable Zswap for current session'):CHK"
                echo 'TRUE' > "$tmp_fenetre_cb01" # permanence
                echo 'FALSE' > "$tmp_fenetre_cb02" # switch
            else
                local txt_persist="$(gettext 'and') <span foreground=\"green\"><b>$(gettext 'permanent')</b></span> $(gettext 'in system startup.')"
                local checkbox_permanence="--field=$(gettext 'Remove permanent settings from system startup'):CHK"
                local checkbox_switch="--field=$(gettext 'Disable Zswap for current session'):CHK"
                echo 'TRUE' > "$tmp_fenetre_cb01" # permanence
                echo 'TRUE' > "$tmp_fenetre_cb02" # switch
            fi

>"$tmp_fenetre_stat"
            choice=$(while [ -f "$tmp_fenetre_stat" ]; do read_zswap_stats; sleep 1; done | yad \
                --center --borders=10 --width=500 \
                --hscroll-policy=never \
                --window-icon="$ICONS/antiXmm.png" \
                --title="$(gettext 'zswap Status')" \
                --text="<big><b>$(gettext 'zswap Status:')</b></big>\n\n\
$(gettext 'zswap is currently') <span foreground=\"green\"><b>$(gettext 'enabled')</b></span>,\n\
$txt_persist\n" \
                --form \
                --field="<u>$(gettext 'Settings')</u>":TXT \
                --field="<u>$(gettext 'Usage Statistics')</u>":TXT \
                --field=:LBL \
                "$checkbox_permanence" \
                "$checkbox_switch" \
                --field=:LBL \
                --button="$(gettext 'Apply')!!$(gettext 'Before pressing Apply make sure to have ticked or unticked the appropriate checkboxes.') $(gettext 'Disabling Zswap for the current session will not immediately move all currently compressed data back to the uncompressed memory area. This can be only done by using the Clear Swaps function from the Memory menu.'):2" \
                --button="$(gettext 'Back')!!$(gettext 'Return to antiX Memory Manager main window.'):1" \
                --cycle-read)

            exit_code=$?
            rm -f "$tmp_fenetre_stat"
            if [ $exit_code -eq 2 ]; then
                IFS='|' read -r _ _ _ permanence switch _ <<< "$choice"
                if ${permanence,,}; then
                    if check_persistent_zswap; then
                        remove_permanent_settings_zswap
                    else
                        make_zswap_permanent "$current_algorithm" "$current_pool" "$current_max_percent"
                    fi
                fi
                if ${switch,,}; then
                    disable_zswap
                fi
            elif [ $exit_code -eq 1 ]; then
                back=true
            elif [ $exit_code -eq 252 ]; then
                exit 0
            fi
        else
            # zswap is disabled, show enable options

            # manage permanent settings status display and buttons
            if check_persistent_zswap; then
                button_permanent="--button="$(gettext 'Remove zswap permanent settings')!!$(gettext 'This button will not affect the zswap status of the current session.'):4""
                txt_persist=",\n$(gettext 'but') <span foreground=\"green\"><b>$(gettext 'permanent')</b></span> $(gettext 'in system startup.')"
                checkbox_permanence="--field=$(gettext 'Update permanent settings when disabling'):CHK"
                permanence='TRUE'
            else
                button_permanent=''
                txt_persist='.'
                checkbox_permanence="--field=$(gettext 'Make settings permanent when enabling'):CHK"
                permanence='TRUE'
            fi

            choice=$(yad --center --borders=10 --width=400 --height=300 \
                --window-icon="$ICONS/antiXmm.png" \
                --title="$(gettext 'zswap Settings')" \
                --text="$(gettext 'zswap is currently') <span foreground=\"red\"><b>$(gettext 'disabled')</b></span>$txt_persist\n" \
                --form \
                --field="$(gettext 'Compression Algorithm')":CB "$algorithms" \
                --field="$(gettext 'Memory Pool')":CB "$pools" \
                --field="$(gettext 'Max Pool Percent')":NUM "$default_percent_zswap!5..50!1" \
                --field=:LBL \
                "$checkbox_permanence" \
                --field=:LBL \
                FALSE $permanence FALSE \
                "$button_permanent" \
                --button="$(gettext 'Enable zswap with above settings')!!$(gettext 'Before pressing Enable make sure to have selected all appropriate settings from the pulldowns, entry fields and checkboxes.'):2" \
                --button="$(gettext 'Back')!!$(gettext 'Return to antiX Memory Manager main window.'):1")

            exit_code=$?

            if [ $exit_code -eq 2 ]; then
                IFS='|' read -r algorithm pool percent _ permanence _<<< "$choice"
                # Enable zswap immediately
                enable_zswap "$algorithm" "$pool" "$percent"
                if ${permanence,,}; then
                    if check_persistent_zswap; then
                        # update persistent settings
                        remove_permanent_settings_zswap
                        make_zswap_permanent "$algorithm" "$pool" "$percent"
                        info_box "$(gettext 'Success')" "$(gettext 'System startup entries have been updated.')"
                    else
                        # enter persistent settings
                        make_zswap_permanent "$algorithm" "$pool" "$percent"
                    fi
                fi
            elif [ $exit_code -eq 4 ]; then
                remove_permanent_settings_zswap
            elif [ $exit_code -eq 1 ]; then
                back=true
            elif [ $exit_code -eq 252 ]; then
                exit 0
            fi
        fi
        if $back; then break; fi
    done
}

help() {
    cat > "$helpfile_01" << EOF
<!DOCTYPE html>
<html lang="${current_locale/_/-}">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    <title>$(gettext 'antiX Memory Manager Help')</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 900px;
            margin: 20px auto;
            padding: 20px;
            background-color: #f5f5f5;
            color: #333;
            line-height: 1.6;
        }
        h1 {
            color: #2c3e50;
            border-bottom: 3px solid #3498db;
            padding-bottom: 10px;
            margin-bottom: 20px;
        }
        h2 {
            color: #2980b9;
            margin-top: 25px;
            border-bottom: 1px solid #ddd;
            padding-bottom: 5px;
        }
        h3 {
            color: #e74c3c;
            margin-top: 20px;
        }
        h4 {
            color: #8e44ad;
            margin-top: 15px;
        }
        p, li {
            margin-bottom: 10px;
        }
        .section {
            background-color: #fff;
            padding: 20px;
            margin: 15px 0;
            border-radius: 8px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }
        .toggle {
            cursor: pointer;
            background-color: #3498db;
            color: white;
            padding: 12px 15px;
            border-radius: 5px;
            margin-bottom: 10px;
            font-weight: bold;
            transition: background-color 0.3s;
        }
        .toggle:hover {
            background-color: #2980b9;
        }
        .content {
            display: none;
            padding: 15px;
            background-color: #f9f9f9;
            border-radius: 5px;
        }
        .content.active {
            display: block;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin: 15px 0;
        }
        th, td {
            padding: 12px 15px;
            text-align: left;
            border: 1px solid #ddd;
        }
        th {
            background-color: #3498db;
            color: white;
        }
        tr:nth-child(even) {
            background-color: #ecf0f1;
        }
        tr:hover {
            background-color: #e8f4f8;
        }
        .warning {
            background-color: #ffe6e6;
            border-left: 5px solid #e74c3c;
            padding: 15px;
            border-radius: 5px;
            margin-top: 15px;
        }
        .note {
            background-color: #e8f7f2;
            border-left: 5px solid #27ae60;
            padding: 15px;
            border-radius: 5px;
            margin-top: 15px;
        }
        .key-benefit {
            font-weight: bold;
            color: #27ae60;
        }
        .key-drawback {
            font-weight: bold;
            color: #e67e22;
        }
        .comparison {
            display: flex;
            justify-content: space-between;
            margin: 20px 0;
        }
        .comparison-column {
            flex: 1;
            padding: 15px;
            background-color: #f9f9f9;
            border-radius: 5px;
            margin: 0 10px;
        }
        .comparison-title {
            text-align: center;
            font-weight: bold;
            margin-bottom: 10px;
            color: #3498db;
        }
    </style>
  </head>
  <body>
    <h1><b>$(gettext 'antiX Memory Manager Help')</b></h1>
    <h3>$(gettext 'Memory Compression Technologies - Statistics - Maintenance')</h3>
    <p>$(gettext 'This tool helps you manage Linux memory compression features to improve system performance and responsiveness, especially on systems with limited RAM.')</p>

    <!-- Overview Section -->
    <div class="section">
        <h2>$(gettext 'Overview')</h2>
        <p>$(gettext 'The antiX Memory Manager provides control over two key Linux memory compression technologies:')</p>
        <ul>
            <li><b>ZRAM</b> - $(gettext 'Creates compressed RAM disks that work like swap but remain in memory')</li>
            <li><b>ZSWAP</b> - $(gettext 'Creates a compressed cache for pages that would otherwise be written to disk swap')</li>
        </ul>
        <p>$(gettext 'Both technologies help improve system performance by reducing memory pressure, but they work differently and have different requirements.')</p>
        
        <div class="comparison">
            <div class="comparison-column">
                <div class="comparison-title">ZRAM</div>
                <p><span class="key-benefit">$(gettext 'Benefits:')</span> $(gettext 'No disk required, faster than physical swap')</p>
                <p><span class="key-drawback">$(gettext 'Drawbacks:')</span> $(gettext 'Uses some RAM for the compressed data, increases CPU usage')</p>
                <p>$(gettext 'Best for systems with no swap partition or SSD wear concerns')</p>
            </div>
            <div class="comparison-column">
                <div class="comparison-title">ZSWAP</div>
                <p><span class="key-benefit">$(gettext 'Benefits:')</span> $(gettext 'Reduces disk writes, improves responsiveness')</p>
                <p><span class="key-drawback">$(gettext 'Drawbacks:')</span> $(gettext 'Requires swap partition/file, increases CPU usage')</p>
                <p>$(gettext 'Best for systems with slow disks or SSDs with limited write cycles')</p>
            </div>
        </div>
    </div>

    <!-- ZRAM Section -->
    <div class="section">
        <div class="toggle" onclick="toggleContent('zram')">$(gettext 'What is ZRAM?')</div>
        <div id="zram" class="content">
            <p>$(gettext 'ZRAM creates a compressed block device in RAM (sometimes called a "RAM disk") that functions as a swap device. When physical memory runs low, the kernel compresses data and stores it in this RAM disk instead of writing it to a physical swap partition.')</p>
            
            <h4>$(gettext 'How ZRAM Works:')</h4>
            <p>$(gettext 'When applications request memory that exceeds available RAM, instead of immediately paging to disk, the kernel:')</p>
            <ol>
                <li>$(gettext 'Compresses the least-used memory pages')</li>
                <li>$(gettext 'Stores these compressed pages in the ZRAM device')</li>
                <li>$(gettext 'Retrieves and decompresses pages as needed')</li>
            </ol>
            
            <h4>$(gettext 'Key Benefits:')</h4>
            <ul>
                <li>$(gettext 'Much faster than swap on physical disks (even SSDs)')</li>
                <li>$(gettext 'No swap partition required')</li>
                <li>$(gettext 'Reduces write wear on SSDs')</li>
                <li>$(gettext 'Improves system responsiveness under memory pressure')</li>
            </ul>
            
            <h4>$(gettext 'Limitations:')</h4>
            <ul>
                <li>$(gettext 'Uses some RAM for compressed data storage')</li>
                <li>$(gettext 'Increases CPU usage due to compression/decompression')</li>
                <li>$(gettext 'Static size once configured (requires recreation to resize)')</li>
                <li>$(gettext 'Limited by available physical RAM')</li>
            </ul>
            
            <h4>$(gettext 'Compression Algorithms:')</h4>
            <table>
                <tr>
                    <th>$(gettext 'Algorithm')</th>
                    <th>$(gettext 'Compression Ratio')</th>
                    <th>$(gettext 'Speed')</th>
                    <th>$(gettext 'CPU Usage')</th>
                    <th>$(gettext 'Best For')</th>
                </tr>
                <tr>
                    <td><b>ZSTD</b></td>
                    <td>$(gettext 'High (2.5-3.5x)')</td>
                    <td>$(gettext 'Good')</td>
                    <td>$(gettext 'Moderate')</td>
                    <td>$(gettext 'General use; good balance of compression and speed')</td>
                </tr>
                <tr>
                    <td><b>LZO</b></td>
                    <td>$(gettext 'Moderate (2-2.5x)')</td>
                    <td>$(gettext 'Very Fast')</td>
                    <td>$(gettext 'Low')</td>
                    <td>$(gettext 'Older CPUs or systems where minimizing CPU use is important')</td>
                </tr>
                <tr>
                    <td><b>LZO-RLE</b></td>
                    <td>$(gettext 'Moderate (2-2.5x)')</td>
                    <td>$(gettext 'Very Fast')</td>
                    <td>$(gettext 'Low')</td>
                    <td>$(gettext 'Similar to LZO, better for data with repeated patterns')</td>
                </tr>
                <tr>
                    <td><b>LZ4</b></td>
                    <td>$(gettext 'Low (1.5-2x)')</td>
                    <td>$(gettext 'Extremely Fast')</td>
                    <td>$(gettext 'Minimal')</td>
                    <td>$(gettext 'Very old or CPU-limited systems where speed is critical')</td>
                </tr>
                <tr>
                    <td><b>LZ4HC</b></td>
                    <td>$(gettext 'Moderate-High (2.5-3x)')</td>
                    <td>$(gettext 'Fast')</td>
                    <td>$(gettext 'Moderate')</td>
                    <td>$(gettext 'Systems with some CPU headroom wanting better compression than LZ4')</td>
                </tr>
            </table>
            
            <div class="note">
                <h4>$(gettext 'Recommendation:')</h4>
                <p>$(gettext 'For most modern systems, ZSTD offers the best balance of compression and speed. For older systems with limited CPU resources, LZ4 is recommended.')</p>
            </div>
        </div>
    </div>

    <!-- ZSWAP Section -->
    <div class="section">
        <div class="toggle" onclick="toggleContent('zswap')">$(gettext 'What is ZSWAP?')</div>
        <div id="zswap" class="content">
            <p>$(gettext 'ZSWAP is a compressed cache for swap pages. When the system needs to swap out memory pages to disk, ZSWAP intercepts these pages, compresses them, and keeps them in RAM as long as possible. Only when the ZSWAP cache fills up are the oldest or least used pages written to the physical swap device.')</p>
            
            <h4>$(gettext 'How ZSWAP Works:')</h4>
            <p>$(gettext 'When applications require more memory than physically available:')</p>
            <ol>
                <li>$(gettext 'The kernel identifies pages to be swapped out')</li>
                <li>$(gettext 'ZSWAP intercepts and compresses these pages')</li>
                <li>$(gettext 'Compressed pages stay in RAM until the ZSWAP cache fills up')</li>
                <li>$(gettext 'When the cache is full, least recently used pages are written to disk')</li>
                <li>$(gettext 'Pages are decompressed when accessed again')</li>
            </ol>
            
            <h4>$(gettext 'Key Benefits:')</h4>
            <ul>
                <li>$(gettext 'Significantly reduces disk I/O')</li>
                <li>$(gettext 'Improves responsiveness under memory pressure')</li>
                <li>$(gettext 'Extends SSD lifespan by reducing writes')</li>
                <li>$(gettext 'Works with existing swap partitions/files')</li>
                <li>$(gettext 'Dynamically adjusts based on memory pressure')</li>
            </ul>
            
            <h4>$(gettext 'Limitations:')</h4>
            <ul>
                <li>$(gettext 'Requires a configured swap device (partition or file)')</li>
                <li>$(gettext 'Increases CPU usage for compression/decompression')</li>
                <li>$(gettext 'Still relies on disk swap when under heavy memory pressure')</li>
            </ul>
            
            <h4>$(gettext 'Memory Pools:')</h4>
            <table>
                <tr>
                    <th>$(gettext 'Pool Type')</th>
                    <th>$(gettext 'Memory Efficiency')</th>
                    <th>$(gettext 'Objects per Page')</th>
                    <th>$(gettext 'Best For')</th>
                </tr>
                <tr>
                    <td><b>ZSMALLOC</b></td>
                    <td>$(gettext 'High')</td>
                    <td>$(gettext 'Variable (optimized)')</td>
                    <td>$(gettext 'Most systems; efficiently handles various object sizes')</td>
                </tr>
                <tr>
                    <td><b>ZBUD</b></td>
                    <td>$(gettext 'Low (50% max)')</td>
                    <td>2</td>
                    <td>$(gettext 'Simple systems; lower overhead but less efficient memory usage')</td>
                </tr>
                <tr>
                    <td><b>Z3FOLD</b></td>
                    <td>$(gettext 'Moderate-High (75% max)')</td>
                    <td>3</td>
                    <td>$(gettext 'Good compromise between ZSMALLOC complexity and ZBUD simplicity')</td>
                </tr>
            </table>
            
            <div class="note">
                <h4>$(gettext 'Recommendation:')</h4>
                <p>$(gettext 'For most systems, ZSMALLOC provides the best memory efficiency. For very limited systems where every bit of memory overhead matters, consider Z3FOLD.')</p>
            </div>
        </div>
    </div>

    <!-- Configuration Recommendations -->
    <div class="section">
        <div class="toggle" onclick="toggleContent('config')">$(gettext 'Configuration Recommendations')</div>
        <div id="config" class="content">
            <h4>$(gettext 'ZRAM Configuration:')</h4>
            <ul>
                <li><b>$(gettext 'Size Setting:')</b> $(gettext '50-100% of physical RAM is typical')
                    <ul>
                        <li>$(gettext 'Systems with 2GB or less RAM: Consider 100%')</li>
                        <li>$(gettext 'Systems with 4-8GB RAM: Consider 50-75%')</li>
                        <li>$(gettext 'Systems with more than 8GB RAM: Consider 25-50%')</li>
                    </ul>
                </li>
                <li><b>$(gettext 'Algorithm Selection:')</b>
                    <ul>
                        <li>$(gettext 'For general use: ZSTD')</li>
                        <li>$(gettext 'For older CPUs: LZ4')</li>
                        <li>$(gettext 'For maximum compression: ZSTD')</li>
                    </ul>
                </li>
            </ul>
            
            <h4>$(gettext 'ZSWAP Configuration:')</h4>
            <ul>
                <li><b>$(gettext 'Max Pool Percent:')</b> $(gettext 'The percentage of RAM that can be used for the ZSWAP pool')
                    <ul>
                        <li>$(gettext 'Recommended starting point: 20%')</li>
                        <li>$(gettext 'For systems with plenty of RAM: 10-15%')</li>
                        <li>$(gettext 'For memory-constrained systems: 25-30%')</li>
                    </ul>
                </li>
                <li><b>$(gettext 'Memory Pool:')</b>
                    <ul>
                        <li>$(gettext 'For most systems: ZSMALLOC')</li>
                        <li>$(gettext 'For very memory-constrained systems: Z3FOLD')</li>
                    </ul>
                </li>
            </ul>
            
            <h4>$(gettext 'Using ZRAM and ZSWAP Together:')</h4>
            <p>$(gettext 'ZRAM and ZSWAP can be used together for additional benefits:')</p>
            <ul>
                <li>$(gettext 'ZSWAP handles initial memory pressure by keeping compressed pages in RAM')</li>
                <li>$(gettext 'ZRAM provides a fast, in-memory swap device when ZSWAP needs to write out pages')</li>
                <li>$(gettext 'Physical swap serves as the final fallback for extreme memory pressure')</li>
            </ul>
            <p>$(gettext 'When using both, consider reducing individual sizes:')</p>
            <ul>
                <li>$(gettext 'ZRAM: 25-50% of RAM')</li>
                <li>$(gettext 'ZSWAP max pool: 10-15% of RAM')</li>
            </ul>
            
            <div class="note">
                <h4>$(gettext 'System-Specific Tuning:')</h4>
                <p>$(gettext 'Monitor system performance using the Memory Statistics tab in this tool. You may need to adjust settings based on your specific workload and hardware.')</p>
            </div>
        </div>
    </div>

    <!-- Memory Statistics Section -->
    <div class="section">
        <div class="toggle" onclick="toggleContent('stats')">$(gettext 'Understanding Memory Statistics')</div>
        <div id="stats" class="content">
            <p>$(gettext 'The Memory Statistics tab provides real-time information about your system memory usage and compression effectiveness.')</p>
            
            <h4>$(gettext 'Key Metrics:')</h4>
            <ul>
                <li><b>$(gettext 'Physical Memory:')</b> $(gettext 'Total RAM, used and free amounts')</li>
                <li><b>$(gettext 'ZRAM Statistics:')</b> $(gettext 'Size, used space, compression ratio')</li>
                <li><b>$(gettext 'ZSWAP Statistics:')</b> $(gettext 'Pool size, used space, compression ratio')</li>
                <li><b>$(gettext 'Swap Usage:')</b> $(gettext 'Physical swap usage, if configured')</li>
            </ul>
            
            <h4>$(gettext 'Interpreting Compression Ratios:')</h4>
            <ul>
                <li>$(gettext 'Higher ratios (3.0+) indicate good compression efficiency')</li>
                <li>$(gettext 'Typical ranges for most workloads: 2.0-3.5')</li>
                <li>$(gettext 'Lower ratios may indicate data that does not compress well')</li>
            </ul>
            
            <h4>$(gettext 'Monitoring Tips:')</h4>
            <ul>
                <li>$(gettext 'Check statistics during your normal workload')</li>
                <li>$(gettext 'Monitor the system when it feels sluggish')</li>
                <li>$(gettext 'Look for high compression ratios and low swap I/O as indicators of effective settings')</li>
            </ul>
        </div>
    </div>

    <!-- Flush Memory Section -->
    <div class="section">
        <div class="toggle" onclick="toggleContent('flush')">$(gettext 'About Memory Flushing')</div>
        <div id="flush" class="content">
            <p>$(gettext 'The "Clear Memory" function performs several operations to free up memory:')</p>
            <ol>
                <li>$(gettext 'Syncs all file systems to ensure data is written to disk')</li>
                <li>$(gettext 'Clears various memory caches')</li>
                <li>$(gettext 'Temporarily disables all swap devices (including ZRAM)')</li>
                <li>$(gettext 'Re-enables and reinitializes swap devices')</li>
            </ol>
            
            <p>$(gettext 'This process can help recover memory that has become fragmented or is being held by the system unnecessarily. However, it requires sufficient free physical RAM to temporarily hold all active data.')</p>
            
            <div class="warning">
                <h3><u><b>$(gettext 'WARNING!')</b></u></h3>
                <p>$(gettext 'Do not use the "Clear Memory" function when:')</p>
                <ul>
                    <li>$(gettext 'Your system is already under heavy memory pressure')</li>
                    <li>$(gettext 'A large amount of data is stored in compressed memory (ZRAM/ZSWAP)')</li>
                    <li>$(gettext 'System resources are critical (during important work)')</li>
                </ul>
                <p><b>$(gettext 'Insufficient physical memory can cause process termination, data loss, or system instability during this operation.')</b></p>
            </div>
            
            <h4>$(gettext 'When to Use Memory Flushing:')</h4>
            <ul>
                <li>$(gettext 'After closing memory-intensive applications')</li>
                <li>$(gettext 'When the system feels sluggish despite having closed applications')</li>
                <li>$(gettext 'As a maintenance step when sufficient free RAM is available')</li>
            </ul>
        </div>
    </div>

    <!-- Troubleshooting -->
    <div class="section">
        <div class="toggle" onclick="toggleContent('troubleshoot')">$(gettext 'Troubleshooting')</div>
        <div id="troubleshoot" class="content">
            <h4>$(gettext 'Common Issues:')</h4>
            
            <p><b>$(gettext 'System still feels slow after configuring ZRAM/ZSWAP:')</b></p>
            <ul>
                <li>$(gettext 'Check if compression ratio is low (under 2.0)')</li>
                <li>$(gettext 'Try a different compression algorithm')</li>
                <li>$(gettext 'Increase ZRAM size or ZSWAP pool percentage')</li>
                <li>$(gettext 'Ensure you have some physical swap configured for extreme cases')</li>
            </ul>
            
            <p><b>$(gettext 'High CPU usage after enabling compression:')</b></p>
            <ul>
                <li>$(gettext 'Switch to a faster algorithm (LZ4 has lowest CPU impact)')</li>
                <li>$(gettext 'Reduce ZRAM size or ZSWAP pool percentage')</li>
                <li>$(gettext 'Check if other processes are competing for CPU resources')</li>
            </ul>
            
            <p><b>$(gettext 'System becomes unresponsive under heavy memory load:')</b></p>
            <ul>
                <li>$(gettext 'Ensure physical swap is configured in addition to ZRAM')</li>
                <li>$(gettext 'Reduce the swappiness value in System Settings')</li>
                <li>$(gettext 'Consider adding more physical RAM if possible')</li>
            </ul>
            
            <h4>$(gettext 'Checking Status from Terminal:')</h4>
            <p>$(gettext 'For advanced troubleshooting, you can check ZRAM/ZSWAP status from terminal:')</p>
            <ul>
                <li><code>cat /proc/swaps</code> - $(gettext 'Shows all active swap devices including ZRAM')</li>
                <li><code>cat /sys/module/zswap/parameters/enabled</code> - $(gettext 'Shows if ZSWAP is enabled (Y/N)')</li>
                <li><code>cat /proc/zram0/mm_stat</code> - $(gettext 'Shows detailed ZRAM statistics')</li>
                <li><code>cat /sys/kernel/debug/zswap/stored_pages</code> - $(gettext 'Number of pages stored in ZSWAP')</li>
            </ul>
        </div>
    </div>

    <script>
        // Initialize with the first section open
        document.addEventListener('DOMContentLoaded', function() {
            toggleContent('zram');
        });
        
        function toggleContent(id) {
            const content = document.getElementById(id);
            content.classList.toggle('active');
        }
    </script>
  </body>
</html>
EOF

    if which antix-viewer >/dev/null 2>&1; then # default: antiX viewer
        (coproc viewer (sudo -u $(logname) -- antix-viewer "$helpfile_01" "antiX zram-zswap Manager Help" 2>/dev/null)) 2>/dev/null
    elif false; then  # prepared for cli
        (coproc viewer (sudo -u $(logname) -- urxvt -T "$(gettext 'antiX Memory Manager Help')" -e bash -c 'links2 -dump /dev/shm/aZZM.html; echo -e "\n\n\033[1;33m$(gettext "Scroll up or use Shift+PgUp/PgDown to read. Press any key to close this window.")\033[0;37m"; while read -n1 k; do [[ "$k" =~ [[:alnum:]] ]] && break; done' 2>/dev/null)) 2>/dev/null
    else # fallback to links2
        (coproc viewer (sudo -u $(logname) -- links2 -g /dev/shm/aZZM.html 2>/dev/null)) 2>/dev/null
    fi
}

export -f info_box error_box clear_memory status_info enable_zram check_zram

# Start the main menu
main_menu

