{ pkgs, ... }:
let
  i3toggle = pkgs.writeShellScriptBin "i3toggle" ''
    print_help() {
      printf 'Usage: %s [[-a <anchor>] [-d <size>] [-p <pos>] [-s <screen>]] [-m <edge>] [-t] [-u [-w][-o <opts>]] [-v] [-V] <command>\n' "$script_name"
      printf 'Executes a program in a positioned scratchpad, optionally wrapped in a URxvt window.\n'
      printf 'Caches optional URxvt wrapper script and stores window id at XDG_RUNTIME_DIR based on command,\n'
      printf 'so executing the same command will re-use the existing window, if it still exists.\n'
      printf '\nArguments:\n'
      printf ' -a <anchor>   Sets where to calculate position from. Valid values are\n'
      printf '               top-left, top-center, top-right\n'
      printf '               center-left, center-center, center-right\n'
      printf '               bottom-left, bottom-center, bottom-right\n'
      printf '               Can be shortened as: tl, tc, tr, cl, cc, cr, bl, bc, br\n'
      printf '               Position will be calculated from anchor point of screen to anchor\n'
      printf '               point of window. Default is center-center.\n'
      printf ' -d <size>     Dimensions of window in pixels, in WIDTHxHEIGHT format.\n'
      printf '               Percentages of the screen dimensions can be used as well. Default is 50%%x50%%\n'
      printf ' -h            Prints this help page.\n'
      printf ' -m <edge>     Animates the movement to target position from specified edge.\n'
      printf '               Valid values are top, left, bottom, right, or short t, l, b, r\n'
      printf ' -o <opts>     Extra URxvt options to pass.\n'
      printf ' -p <pos>      Position of terminal on pixels, in X,Y format.\n'
      printf '               Negative values can be used as well. Default is 0,0\n'
      printf ' -s <screen>   Screen identifier, as listed in xrandr. Falls back to primary screen.\n'
      printf ' -t            Toggles the window.\n'
      printf ' -u            Use URxvt terminal to launch the command - for command line apps.\n'
      printf ' -v            Verbose, for debugging.\n'
      printf ' -V            Print version information.\n'
      printf ' -w            Hides the cursor and waits for keypress before closing the\n'
      printf '               terminal window. Useful for commands immediately returning.\n'
      printf '\nExample:\n'
      printf ' # Calendar at the bottom right of primary screen with 32px bottom margin:\n'
      printf ' $ %s -d200x200 -abr -p0,-32 -wtu cal\n' "$script_name"
    }

    main() {
      local screen_width screen_height offset_x offset_y width height x_pos y_pos \
            urxvt move wait command urxvt_opts toggle verbose \
            wid_file pid_dir animation_frame_delay=0.01

      pid_dir=''${XDG_RUNTIME_DIR="/tmp"}

      parse_aruments "$@"

      if already_running; then
        exit 0
      fi

      start_and_save_wid_and_pid
    }

    parse_aruments() {
      local script_name dim='\([0-9]\+%\?\)' pos='\(-\?[0-9]\+\)' anchor='cc'
      script_name="$(basename "$0")"
      while getopts a:d:hm:o:p:s:tuvVw arg; do
        case $arg in
          a) anchor="$OPTARG";;
          d) read -r width height < <(sed -n "s/^''${dim}x''${dim}$/\1 \2/p" <<< "$OPTARG");;
          h) print_help; exit 0;;
          m) move="$OPTARG";;
          o) urxvt_opts="$OPTARG";;
          p) read -r x_pos y_pos < <(sed -n "s/^''${pos},''${pos}$/\1 \2/p" <<< "$OPTARG");;
          s) screen="$OPTARG";;
          t) toggle=true;;
          u) urxvt=true;;
          v) verbose=true;;
          V) printf 'Version %s\n' "$VERSION"; exit 0;;
          w) wait=true;;
          *) print_help; exit 1;;
        esac
      done
      shift $((OPTIND - 1))
      command="$*"
      if [ -z "$command" ]; then
        printf 'Command argument is missing. See %s -h.\n' "$script_name" >&2
        exit 1
      fi
      wid_file="$pid_dir/i3-sp-$(md5sum <<< "$command" | cut -f1 -d' ')"

      if [[ "$anchor" =~ ^(top|center|bottom)-(left|center|right)$ ]]; then
        anchor=$(sed -n 's/^\([tcb]\)[^-]\+-\([lcr]\).*$/\1\2/p' <<< "$anchor")
      fi

      if [[ ! "$anchor" =~ ^([tcb])([lcr])$ ]]; then
        printf 'Anchor "%s" is invalid. See %s -h.\n' "$anchor" "$script_name" >&2
        exit 1
      fi

      if [[ "$move" =~ ^(top|bottom|left|right)$ ]]; then
        move=$(sed -n 's/^\([tblr]\).*$/\1/p' <<< "$move")
      fi

      if [[ -n "$move" && ! "$move" =~ ^[tblr]$ ]]; then
        printf 'Move "%s" is invalid. See %s -h.\n' "$move" "$script_name" >&2
        exit 1
      fi

      ensure_screen
      parse_dimensions
    }

    ensure_screen() {
      if [ -n "$screen" ] && [ "$(xrandr --listactivemonitors | sed -n '2,$s/^.*\s\(\S\+\)$/\1/p' | grep -xc "$screen")" -eq 0 ]; then
        unset screen
      fi

      if [ -z "$screen" ]; then
        screen=$(xrandr | sed -n 's/^\(\S\+\).*primary.*$/\1/p')
      fi
    }

    parse_dimensions() {
      width=''${width:-50%}
      height=''${height:-50%}
      x_pos=''${x_pos:-0}
      y_pos=''${y_pos:-0}

      read -r screen_width screen_height offset_x offset_y < <(xrandr | sed -n "s/^$screen .*connected[^0-9]* \([0-9]\+\)x\([0-9]\+\)\([+-][0-9]\+\)\([+-][0-9]\+\).*$/\1 \2 \3 \4/p")

      if [[ "$width" =~ %$ ]]; then
        width=$((screen_width * ''${width//%/} / 100))
      fi
      if [[ "$height" =~ %$ ]]; then
        height=$((screen_height * ''${height//%/} / 100))
      fi

      case "$anchor" in
        t?) y_pos=$((y_pos + offset_y));;
        c?) y_pos=$((y_pos + ((screen_height - height) / 2) + offset_y));;
        b?) y_pos=$((y_pos + screen_height - height + offset_y));;
      esac

      case "$anchor" in
        ?l) x_pos=$((x_pos + offset_x));;
        ?c) x_pos=$((x_pos + ((screen_width - width) / 2) + offset_x));;
        ?r) x_pos=$((x_pos + screen_width - width + offset_x));;
      esac

      debug 'Calculated window dimensions are [' "x: $x_pos" "y: $y_pos" "w: $width" "h: $height" ']'
    }

    already_running() {
      local window_id window_state pid window_pid
      debug 'Checking wid file' "$wid_file"
      if [ -f "$wid_file" ] && [ -r "$wid_file" ]; then
        read -r window_id pid < "$wid_file"
      fi
      if [ -n "$window_id" ]; then
        debug 'Last window id was' "$window_id"
        if [ "$(wmctrl -l | grep -c "^$window_id ")" -gt 0 ]; then
          debug 'Window still exist'
          if [ -n "$pid" ]; then
            window_pid=$(xdotool getwindowpid "$window_id")
            debug 'Pid of window is' "$window_pid"
            if [ "$pid" -ne "$window_pid" ]; then
              debug 'PID does not match, window id reused?'
              return 1
            fi
          else
            debug 'Last pid not found'
          fi
          if [ -n "$toggle" ]; then
              window_state=$(xprop -id "$window_id" | sed -n 's/^.*window state: \(.*\)$/\1/p')
              debug 'Toggle mode on, current window state is' "$window_state"
              if [[ "$window_state" != 'Withdrawn' ]]; then
                debug 'Moving to scratchpad'
                hide_window
                return 0
              fi
          fi
          show_window_in_position
          return 0
        fi
      fi
      return 1
    }

    start_and_save_wid_and_pid() {
      local window_id pid

      if [ -n "$urxvt" ]; then
        create_urxvt_wrapper
      fi

      debug 'Launching command' "$command"
      window_id=$(printf '0x%08x\n' "$(i3-msg -q "exec --no-startup-id \"$command\"" && i3-msg -t subscribe '[ "window" ]' | sed -n 's/.*"window":\([0-9]\+\).*/\1/p')")
      if [ -z "$window_id" ]; then
        printf 'Can not find window with id %s\n' "$window_id" >&2
        rm -f "$wid_file"
        return 1
      fi

      pid="$(xdotool getwindowpid "$window_id")"
      if [ -z "$pid" ]; then
        printf 'Can not find pid for window id %s\n' "$window_id" >&2
        rm -f "$wid_file"
        return 1
      fi
      debug 'PID of window is' "$pid"

      debug 'Saving window_id' "$window_id" 'and pid' "$pid" 'to wid file' "$wid_file"
      printf '%s %s\n' "$window_id" "$pid" > "$wid_file"

      show_window_in_position
    }

    show_window_in_position() {

      if [ -z "$move" ]; then
        debug 'Moving to scratchpad, showing and resizing'
        i3-msg -q -- "[id=\"$window_id\"] move to scratchpad;[id=\"$window_id\"] scratchpad show;[id=\"$window_id\"] move position $x_pos px $y_pos px;[id=\"$window_id\"] resize set $width px $height px"
        return 0
      fi

      local x x_inc=0 x_end y y_inc=0 y_end
      x=$x_pos
      y=$y_pos
      x_end=$x_pos
      y_end=$y_pos
      case "$move" in
        t) y=$((offset_y - height + 1));        y_inc=$(((y_pos - y) / 4));;
        b) y=$((offset_y + screen_height - 1)); y_inc=$(((y_pos - y) / 4));;
        l) x=$((offset_x - width + 1));         x_inc=$(((x_pos - x) / 4));;
        r) x=$((offset_x + screen_width - 1));  x_inc=$(((x_pos - x) / 4));;
      esac
      i3-msg -q -- "[id=\"$window_id\"] move to scratchpad;[id=\"$window_id\"] scratchpad show;[id=\"$window_id\"] move position $x px $y px;[id=\"$window_id\"] resize set $width px $height px"
      i3-msg -q -- "[id=\"$window_id\"] move position $x_pos px $y_pos px"
    }

    hide_window() {
      if [ -n "$move" ]; then
        [ -z "$BASH_LOADABLES_PATH" ] && BASH_LOADABLES_PATH=$(pkg-config bash --variable=loadablesdir 2>/dev/null)
        local x x_inc=0 x_end y y_inc=0 y_end
        x=$x_pos
        y=$y_pos
        x_end=$x_pos
        y_end=$y_pos
        case "$move" in
          t) y_end=$((offset_y - height));            y_inc=$(((y_end - y_pos) / 20));;
          b) y_end=$((offset_y + screen_height - 1)); y_inc=$(((y_end - y_pos) / 20));;
          l) x_end=$((offset_x - width));             x_inc=$(((x_end - x_pos) / 20));;
          r) x_end=$((offset_x + screen_width - 1));  x_inc=$(((x_end - x_pos) / 20));;
        esac
      fi

      i3-msg -q -- "[id=\"$window_id\"] move to scratchpad;"
    }

    debug() {
      if [ -n "$verbose" ]; then
        printf '%s\n' "$*"
      fi
    }

    # [ -z "$BASH_LOADABLES_PATH" ] && BASH_LOADABLES_PATH=$(pkg-config bash --variable=loadablesdir 2>/dev/null)
    # sleep

    main "$@"
  '';
in
# TODO Find a cleaner solution to this
{
  home.packages = with pkgs; [
    xdotool
    wmctrl
    i3toggle
  ];
}
