# This file is part of ctkarch-sysconfig.
#
# ctkarch-sysconfig is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.

menu() {
  [ -x "${systemroot}/usr/bin/X" ] || { mydialog --msgbox "$_xnotinstalled" 10 72; return 0; }

  local items=('chxkeyboard' "$_xkeyboard"
               'chxtouchpad' "$_xtouchpad")
  local item="${items[0]}" err _hint="$_livechanges"
  while
    item="$(dialogmenu --default-item "$item" 2>&1>&3)"
    [[ "$item" != '' ]]
  do
    err="$(unset _hint; "$item" 2>&1>&3)" || mydialog --msgbox "${err}" 10 72
  done
  return 0
}

defaults() {
  # Set the keyboard layout defined in the localization file used
  setxkblayoutvariant "$_defaultxkblayout" ''

  # Do nothing for the keyboard model.

  # Author's preferred Xkb options.
  setxkboption compose menu || echo 'setxkboption FAILED' >&2
  setxkboption terminate ctrl_alt_bksp || echo 'setxkboption FAILED' >&2
  setxkboption keypad pointerkeys || echo 'setxkboption FAILED' >&2

  # Author's preferred synaptics options.
  setpadtwofingeremupressure 50 || echo 'setpadtwofingeremupressure FAILED' >&2
  setpadoption VertTwoFingerScroll 1 || echo 'sedpadoption VertTwoFingerScroll FAILED' >&2
  setpadoption HorizTwoFingerScroll 1 || echo 'sedpadoption HorizTwoFingerScroll FAILED' >&2
}

chxkeyboard() {
  local items=('chxkblayoutvariant' "$_xkblayoutvariant"
               'chxkbmodel'         "$_xkbmodel"
               'chxkboptions'       "$_xkboptions"
               'rmxkbconfig'        "$_rmconfig")
  local item="${items[0]}" err _title="$_xkeyboard"
  while
    item="$(dialogmenu --default-item "$item" 2>&1>&3)"
    [[ "$item" != '' ]]
  do
    err="$("$item" 2>&1>&3)" || mydialog --msgbox "${err}" 10 72
  done
  return 0
}

# Create if necessary and set a value in a one-section X config file; if value is empty, remove the option
# parms: option value file
setXoption() {
  [ -z "$1" ] && { echo 'the option name is an empty string' >&2; return 1; }

  # Create the file if non-existent
  [ -f "${systemroot}/etc/X11/xorg.conf.d/$3" ] || { cp "${moduledir}/res/$3" "${systemroot}/etc/X11/xorg.conf.d/$3" || return 1; }

  if grep -qE "^[[:space:]]*Option[[:space:]]+\"${1}\"[[:space:]]+\"[^\"]*\"[[:space:]]*\$" "${systemroot}/etc/X11/xorg.conf.d/$3";then
    # The option exists (uncommented)
    if [ -n "$2" ];then
      # Edit it with the given, non-empty value
      sed -Ei "s/^([[:space:]]*Option[[:space:]]+\"${1}\"[[:space:]]+\")[^\"]*(\"[[:space:]]*)\$/\1${2}\2/" "${systemroot}/etc/X11/xorg.conf.d/$3" || return 1
    else
      # Given value is empty, remove the option
      sed -Ei "/^[[:space:]]*Option[[:space:]]+\"${1}\"[[:space:]]+\"[^\"]*\"[[:space:]]*\$/d" "${systemroot}/etc/X11/xorg.conf.d/$3" || return 1
    fi
  else
    if [ -n "$2" ];then
      # The option doesn't exist (we don't care about comments), add it to the first section, at the end if it has a non-empty value
      sed -i "1,/^[[:space:]]*EndSection[[:space:]]*\$/s/^\([[:space:]]*EndSection[[:space:]]*\)\$/        Option \"${1}\" \"${2}\"\n\1/" "${systemroot}/etc/X11/xorg.conf.d/$3" || return 1
    fi
  fi
}

# Print the value of the option, or nothing if anything fails
# parm: option file
getXoption() {
  sed -n "s/^[[:space:]]*Option[[:space:]]\+\"${1}\"[[:space:]]\+\"\([^\"]*\)\"[[:space:]]*\$/\1/p;ta;b;:a;q" "${systemroot}/etc/X11/xorg.conf.d/$2" 2>/dev/null
}

chxkblayoutvariant() {
  local i items=() IFS=$'\n'

  for i in $(sed -n '/^! layout$/,/^$/s/^  \([^ ]\+\)\( \+.*\)$/\1\t\1\2/p' "${systemroot}/usr/share/X11/xkb/rules/base.lst" |sort |tr '\t' '\n');do
    items+=("$i")
  done

  local layout variant _title="$_xkblayout" defaultentry
  defaultentry="$(getXoption XkbLayout 20-evdev-custom.conf)"
  [ -z "$defaultentry" ] && defaultentry="$_defaultxkblayout"
  if layout="$(dialogmenu --default-item "$defaultentry" 2>&1>&3)";then
    items=('' "basic           $_xkbbasicvariant")
    for i in $(sed -n "/^! variant\$/,/^\$/s/^  \([^ ]\+\)\( \+\)${layout}: \(.*\)\$/\1\t\1\2\3/p" "${systemroot}/usr/share/X11/xkb/rules/base.lst" |sort |tr '\t' '\n');do
      items+=("$i")
    done
    unset IFS
    _title="$_xkbvariant"
    if variant="$(dialogmenu --default-item "$(getXoption XkbVariant 20-evdev-custom.conf)" 2>&1>&3)";then
      setxkblayoutvariant "$layout" "$variant" || return 1
      mydialog --msgbox "$(printf "$_explain_xkboptions" XkbLayout XkbVariant)" 10 72
    fi
  fi
}

# Set the layout and variant for X11
# parms: layout variant
setxkblayoutvariant() {
  if [ -n "$1" ];then
    setxkbmap -layout "$1" -variant "$2" &>/dev/null
    setXoption XkbLayout "$1" 20-evdev-custom.conf || return 1
    setXoption XkbVariant "$2" 20-evdev-custom.conf || return 1
    echo "xkb layout and variant: ${1} ${2}"
  fi
}

chxkbmodel() {
  local i IFS=$'\n'

  local items=('' "$_xkbdefaultmodel")
  for i in $(sed -n '/^! model$/,/^$/s/^  \([^ ]\+\)\( \+.*\)$/\1\t\1\2/p' "${systemroot}/usr/share/X11/xkb/rules/base.lst" |tr '\t' '\n');do
    items+=("$i")
  done
  unset IFS

  local model _title="$_xkbmodel"
  if model="$(dialogmenu --default-item "$(getXoption XkbModel 20-evdev-custom.conf)" 2>&1>&3)";then
    setxkbmodel "$model" || return 1
    mydialog --msgbox "$(printf "$_explain_xkboption" XkbModel)" 10 72
  fi
}

# Set the keyboard model
# parm: model
setxkbmodel() {
  setxkbmap -model "$1" &>/dev/null
  setXoption XkbModel "$1" 20-evdev-custom.conf || return 1
  echo "xkb keyboard model: $1"
}

chxkboptions() {
  local i items option optstring optval _title _hint
  while
    local IFS=$'\n'
    items=()
    for i in $(sed -n '/^! option$/,/^$/s/^  \([^: ]\+\( [^: ]\+\)*\)\(   *.*\)$/\1\n\1\3/p' "${systemroot}/usr/share/X11/xkb/rules/base.lst");do
      items+=("$i")
    done
    unset IFS
    _title="$_xkboptions"
    _hint="$(getXoption XkbOptions 20-evdev-custom.conf |sed -E 's/(([^:,]+):[^,]+)+/\2/g')"
    [[ "$_hint" =~ [a-z] ]] && _hint="$_enabled $_hint"
    option="$(dialogmenu --default-item "$option" 2>&1>&3)"
  do
    optstring="$(sed -n "/^! option\$/,/^\$/{/^  ${option}  /,/^  [^: ]\+ /s/^  \([^:]\+\):[^ ]\+ \+.*\$/\1/p;ta;bb;:a;q;:b}" "${systemroot}/usr/share/X11/xkb/rules/base.lst")"
    local IFS=$'\n'
    items=()
    for i in $(sed -n "/^! option\$/,/^\$/s/^  \(${optstring}:\([^ ]\+\)\)\( \+.*\)\$/\1\n\2\3/p" "${systemroot}/usr/share/X11/xkb/rules/base.lst");do
      items+=("$i")
    done
    unset IFS
    items=("${optstring}:" "$_disableopt" "${items[@]}")

    unset _hint
    _title="${_xkboptions}: ${option} (${optstring})"
    if optval="$(dialogmenu --default-item "${optstring}:$(getXoption XkbOptions 20-evdev-custom.conf |sed -En "s/^([^:]+:[^,]+,)*${optstring}:([^,]+)(,[^:]+:[^,]+)*/\2/p")" 2>&1>&3)";then
      setxkboption "${optval%:*}" "${optval#*:}" || return 1
      mydialog --msgbox "$(printf "$_explain_xkboption" XkbOptions)" 10 72
    fi
  done
}

# Set an extra keyboard options to a value or disable it if the value is empty
# parms: option value
setxkboption() {
  [ -z "$1" ] && { echo 'empty option name' >&2; return 1; }

  local i options=()
  for i in $(sed -n 's/^[[:space:]]*Option[[:space:]]*"XkbOptions"[[:space:]]*"\([^"]\+\)"[[:space:]]*$/\1/;ta;b;:a;{s/,/ /g;p};q' "${systemroot}/etc/X11/xorg.conf.d/20-evdev-custom.conf");do
    if [ "${i%:*}" = "$1" ];then
      # It's our option, set its value. Don't keep the option if our value is empty.
      [ -n "$2" ] && options+=("$1:$2")
      local done=1
    else
      # Keep the option unmodified
      options+=("$i")
    fi
  done
  # If it wasn't found, add our option with its value if it's not empty
  [ -n "$2" ] && [ -z "$done" ] && options+=("$1:$2")

  # Load that now, failing silently if that must happen
  setxkbmap -option &>/dev/null
  setxkbmap -option "$(echo "${options[*]}" |tr ' ' ',')" &>/dev/null

  # Write that to the config
  setXoption XkbOptions "$(echo "${options[*]}" |tr ' ' ',')" 20-evdev-custom.conf || return 1
  echo "xkb extra option ${1}: ${2}"
}

rmxkbconfig() {
  rm -f "${systemroot}/etc/X11/xorg.conf.d/20-evdev-custom.conf" || return 1
  mydialog --msgbox "$(printf "$_explain_xrmconfig" 20-evdev-custom.conf)" 10 72
}

chxtouchpad() {
  if type xinput &>/dev/null;then
    local padprops=($(xinput --list-props "$(xinput --list --name-only 2>/dev/null |sed -n '/TouchPad/I{p;q}')" 2>/dev/null |sed -En 's/^[[:space:]]*Synaptics Capabilities[^:]*:[[:space:]]*(0|1), (0|1), (0|1), (0|1), (0|1), (0|1), (0|1)/\1 \2 \3 \4 \5 \6 \7/p'))
  else
    local _notouchpad="$_xinput_notinstalled"
  fi

  local items=('chpadtapclicks'    "$_padtapclicks"
               'chpadtwofingeremu' "$_padtwofingeremu"
               'chpadscrolling'    "$_padscrolling"
               'rmpadconfig'       "$_rmconfig")
  local item="${items[0]}" err _title="$_xtouchpad" _hint
  [[ $padprops ]] || _hint="$_notouchpad"
  while
    item="$(dialogmenu --default-item "$item" 2>&1>&3)"
    [[ "$item" != '' ]]
  do
    err="$("$item" 2>&1>&3)" || mydialog --msgbox "${err}" 10 72
  done
  return 0
}

chpadtapclicks() {
  local items=('1' "${_fingertap[1]}"
               '2' "${_fingertap[2]}$([ "${padprops[3]}" = 0 ] && echo " (${_emulable})")"
               '3' "${_fingertap[3]}$([ "${padprops[4]}" = 0 ] && echo " (${_unsupported})")")
  local taps="${items[0]}" err _title="$_padtapclicks"
  while
    taps="$(dialogmenu --default-item "$taps" 2>&1>&3)"
    [[ "$taps" != '' ]]
  do (
    local click items _title="$_padtapclicks - ${_fingertap[taps]}" err
    items=('0' "$_noclick"
           '1' "${_click[1]}$([ "${padprops[0]}" = 0 ] && echo " (${_nopadbutton})")"
           '2' "${_click[2]}$([ "${padprops[1]}" = 0 ] && echo " (${_nopadbutton})")"
           '3' "${_click[3]}$([ "${padprops[2]}" = 0 ] && echo " (${_nopadbutton})")")
    click="$(getXoption "TapButton${taps}" 60-synaptics-custom.conf)"
    [ -z "$click" ] && click="$taps"
    if click="$(dialogmenu --default-item "$click" 2>&1>&3)";then
      if err="$(setpadtapclicks "$taps" "$click" 2>&1>&3)"; then
        mydialog --msgbox "$(printf "$_explain_synoption" "TapButton${taps}")" 10 72
      else
        mydialog --msgbox "${err}" 10 72
      fi
    fi
  ) done
}

# Set the type of click for a tap with nbfingers
# parms: nbfingers clickindex
setpadtapclicks() {
  case "$1" in
    1);; 2);; 3);;
    *) echo "invalid finger nb '$1'" >&2; return 1;;
  esac
  case "$2" in
    0);; 1);; 2);; 3);;
    *) echo "invalid button index '$2'" >&2; return 1;;
  esac

  synclient "TapButton${1}=${2}" &>/dev/null
  setXoption "TapButton${1}" "$2" 60-synaptics-custom.conf || return 1
  echo "${1} taps: click event ${2}"
}

chpadtwofingeremu() {
  local items=('50' "$_enabletwofingeremu"
               ''   "$_disabletwofingeremu")
  local opt _title="$_padtwofingeremu" _hint
  opt="$(getXoption EmulateTwoFingerMinZ 60-synaptics-custom.conf)"
  [ "${padprops[3]}" = 1 ] && _hint="$_emunotneeded"
  [ "${padprops[3]}" = 0 ] && { _hint="$_emupossible"; [ -z "$opt" ] && opt="${items[0]}"; }
  [[ $padprops ]] || _hint="$_notouchpad"
  if opt="$(dialogmenu --default-item "$opt" 2>&1>&3)";then
    setpadtwofingeremupressure "$opt" || return 1
    [ -n "$opt" ] && mydialog --msgbox "$(printf "$_explain_synoption" EmulateTwoFingerMinZ)" 10 72
  fi
  return 0
}

# Set the pressure Z above which two-finger touch is emulated, unset it if the parameter is empty
# parms: pressure|''
setpadtwofingeremupressure() {
  if [ "$#" -gt 0 ]; then
    [[ "$1" =~ ^[0-9]*$ ]] || { echo 'NaN' >&2; return 1; }
    synclient "EmulateTwoFingerMinZ=${1:-282}" &>/dev/null
    setXoption EmulateTwoFingerMinZ "$1" 60-synaptics-custom.conf || return 1
  fi
  echo "pressure for two-finger emulation: ${1:-disabled}"
}

chpadscrolling() {
  local items item opt value _title err
  while
    _title="$_padscrolling"
    items=('edgescroll'    "$_edgescrolling"
           'twofingscroll' "$_twofingerscrolling"
           'circscroll'    "$_circscrolling")
    item="$(dialogmenu --default-item "$item" 2>&1>&3)"
    [[ "$item" != '' ]]
  do
    case "$item" in
      'edgescroll')
        _title="$_edgescrolling"
        items=('VertEdgeScroll'  "$_vertedgescrolling"
               'HorizEdgeScroll' "$_horizedgescrolling")
      ;;
      'twofingscroll')
        _title="$_twofingerscrolling"
        items=('VertTwoFingerScroll'  "$_verttwofingerscrolling"
               'HorizTwoFingerScroll' "$_horiztwofingerscrolling")
      ;;
      'circscroll')
        _title="$_circscrolling"
        items=('CircularScrolling' "$_circscrolling"
               'CircScrollTrigger' "$_circscrollingedge")
      ;;
    esac
    opt="${items[0]}"
    while
      opt="$(dialogmenu --default-item "$opt" 2>&1>&3)"
      [[ "$opt" != '' ]]
    do
      if value="$([ "$opt" = "$items" ] && _title="${items[1]}" || _title="${items[3]}"
                  case "$opt" in
                    'CircScrollTrigger')
                      unset items
                      for i in "${!_circtrigger[@]}";do items+=("$i" "${_circtrigger[i]}");done
                    ;;
                    *)
                      items=('1' "$_enable" '' "$_disable")
                    ;;
                  esac
                  dialogmenu --default-item "$(getXoption "$opt" 60-synaptics-custom.conf)" 2>&1>&3
                )"
      then
        if err="$(setpadoption "$opt" "$value" 2>&1>&3)" ;then
          [ -n "$value" ] && mydialog --msgbox "$(printf "$_explain_synoption" "$opt")" 10 72
        else
          mydialog --msgbox "$err" 10 72
        fi
      fi
    done
  done
  return 0
}

# Set an option for the touchpad
# parms: option value
setpadoption() {
  synclient "${1}=${2}" &>/dev/null
  setXoption "$1" "$2" 60-synaptics-custom.conf || return 1
  echo "synaptics touchpad option ${1}: ${2}"
}

rmpadconfig() {
  rm -f "${systemroot}/etc/X11/xorg.conf.d/60-synaptics-custom.conf" || return 1
  mydialog --msgbox "$(printf "$_explain_xrmconfig" 60-synaptics-custom.conf)" 10 72
}
