filebin() {
    basePath=/srv/www/files
    ssh ymir find /srv/www/files -type f -printf "$'%T@ %TY-%Tm-%TdT%TH:%TM %P\\\\0'" | sort -zn | cut -z -d ' ' -f 2- \
	| while IFS= read -r -d $'\0' l; do
	    IFS=' ' read -r t p <<<"${l}"
	    printf "%s https://f.141.li/%s\n" "${t}" "${p}"
	  done
}

push2bin() {
    if [[ ${#@} -eq 1 && ! -r ${1} ]]; then
        uux -p 'ymir!push2bin' $(echo -n "${1:t}" | tr -c $'[:alnum:]+-=.' '_')
    else
        for f (${@}); do
	    uux -p 'ymir!push2bin' $(echo -n "${f:t}" | tr -c $'[:alnum:]+-=.' '_') <${f}
	done
    fi
}

genmail() {
    local baseName=""
    local target=""
    if [[ ${#@} -ge 1 ]]; then
        target=${1}
        shift
    fi

    if [[ ${#@} -ge 1 ]]; then
        baseName=$(pwgen ${@})
    else
        baseName=$(pwgen -v -A -0 16 1)
    fi
    baseName=$(tr -cd $'[:alnum:]!#$%&*+-/=?^_{|}~.' <<<${baseName})
    address=${baseName}@141.li
    insertAddr() {
      echo "${baseName} gkleen" | ssh ymir tee -a /srv/mail/spm 1>/dev/null \
    }

    printf "%s\n" ${address}
    read -q 'cont?Continue [y/N]? ' || return

    insertAddr
}

s() {
    dir=$(pwd)
    [[ ${#@} -ge 1 ]] && dir=$1

    shellFile=$(findNix ${@})
    [[ ${#@} -ge 1 ]] && shift

    typeset -a cmd
    if [[ -d ${dir}/.nix-gc-roots ]]; then
        cmd=(persistent-nix-shell ${shellFile} ${S_EXTRA_ARGS} ${@})
    else
        cmd=(nix-shell ${shellFile} ${S_EXTRA_ARGS} ${@})
    fi

    if [[ -n "${S_SYSTEMD}" ]]; then
        systemd-run --user --slice=development.slice --collect -E PATH=${PATH} -p WorkingDirectory=${dir} -- ${cmd}
    else
        (
            cd ${dir}

            exec ${cmd}
        )
    fi
}

sz() {
    typeset -a S_EXTRA_ARGS
    S_EXTRA_ARGS=(--run "env __ETC_ZSHENV_SOURCED=1 zsh") s ${@}
}
st() {
    typeset -a S_EXTRA_ARGS
    S_EXTRA_ARGS=(--run "tmux new-session env __ETC_ZSHENV_SOURCED=1 zsh") s ${@}
}
stt() {
    typeset -a S_EXTRA_ARGS
    S_SYSTEMD=true S_EXTRA_ARGS=(--run "urxvt -e tmux -S .tmux.sock new-session env __ETC_ZSHENV_SOURCED=1 zsh") s ${@}
}
se() {
    typeset -a S_EXTRA_ARGS
    S_SYSTEMD=true S_EXTRA_ARGS=(--run "emacs") s ${@}
}

findNix() {
    if [[ $#@ -eq 0 ]]; then
        findNix $(pwd)
    elif [[ -f "$1" ]]; then
        print ${1:a}
    elif [[ -d "$1" && -f "$1"/shell.nix ]]; then
        print ${1:a}/shell.nix
    elif [[ -d "$1" && -f "$1"/default.nix ]]; then
        print ${1:a}/default.nix
    elif [[ -d "$1" && "$1" != "/" ]]; then
        findNix ${1:h}
    else
        printf "Traversed directories to ‘/’ and found no shell specification\n" >&2
        return 1
    fi
}

dir() {
    curlArchive=false
    templateArchive=""
    repoUrl=""
    nixShell=""
    findNix=false
    dir=""
    forceShell=false
    wormhole=false
    gitWorktree=""
    # notmuchMsg=""
    quickserve=false

    while getopts ':t:a:s:Sd:ir:wqg:n:' arg; do
        case $arg in
            "t") ;;
            "a")
                if [[ ${OPTARG} =~ "://" ]]; then
                    templateArchive=${OPTARG}
                    curlArchive=true
                else
                    templateArchive=${OPTARG:a}
                fi
                ;;
            "s") nixShell=${OPTARG:a} ;;
            "S") findNix=true ;;
            "d") dir=${OPTARG} ;;
            "i") forceShell=true ;;
            "r") repoUrl=${OPTARG} ;;
            "w") wormhole=true ;;
            "g") gitWorktree=${OPTARG} ;;
            # "n") notmuchMsg=${OPTARG} ;;
	    "q") quickserve=true ;;
            *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;;
        esac
    done

    shift $((OPTIND - 1))

    if [[ -z ${dir} && ${#@} -ge 1 ]]; then
        dir=${1}
        shift
    fi

    [[ -n ${dir} ]] || return 2;

    if [[ ! -e ${dir} ]]; then
        if [[ -z "${gitWorktree}" ]]; then
            mkdir -vp ${dir}
        else
            git -C ${gitWorktree} worktree add ${dir}
        fi
    else
        gitWorktree=""
    fi

    (
        cd ${dir}
        export dir;

        ${findNix} && { nixShell=$(findNix) || return $? }

        [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} .

        if [[ -n ${templateArchive} ]]; then
            (
                archiveFile=""
                cleanup() {
                    [[ -n "${archiveFile}" ]] && rm -fv ${archiveFile}
                }
                trap cleanup EXIT

                if ${curlArchive}; then
                    archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}")

                    curl -L -o ${archiveFile} ${templateArchive}

                    templateArchive=${archiveFile}
                fi

		unpack=true
		while ${unpack}; do
		    case $(file --brief --mime-type --dereference ${templateArchive}) in
			application/zip)
			    unzip ${templateArchive}
			    unpack=false
			;;
			application/vnd.debian.binary-package)
			    nix shell nixos#binutils --command ar x ${templateArchive}
			    mkdir control data
			    tar -C control -xvaf control.*
			    tar -C data -xvaf data.*
			    unpack=false
			;;
			application/x-rpm)
			    cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio")
			    nix shell nixos#busybox --command rpm2cpio ${templateArchive} > ${cpioArchive}
			    templateArchive=${cpioArchive}
			    unpack=true
			;;
			application/x-cpio)
			    cpio --extract --make-directories --no-absolute-filenames -F ${templateArchive}
			    unpack=false
			;;
			*)
			    tar -xvaf ${templateArchive}
			    unpack=false
			;;
		    esac
		done
            )
        fi


        # if [[ -n ${notmuchMsg} ]]; then
        #     getMimeTypes() {
        #         nix-shell -p mailcap --run "find \${buildInputs} -path '*/etc/mime.types' | head -n 1 | xargs -- cat"
        #     }

        #     typeset -a messages
        #     messages=(${(z)$(notmuch search --output=messages ${notmuchMsg})})

        #     for message (${messages}); do
        #       typeset -A notmuchAtts
        #       notmuchAtts=()

        #       while IFS= read -r -d $'\n' line; do
        #           [[ ${line} =~ '(attachment|part)\{ ID: ([0-9]+)' ]] || continue
        #           attId=${match[2]}

        #           [[ ${line} =~ 'Content-type: multipart/' ]] && continue

        #           fName="part_${attId}"
        #           [[ ${line} =~ 'Filename: (([^,]|,[^ ])+)' ]] && fName=${match[1]}

        #           if [[ ${#messages} -gt 1 ]]; then
        #               fName="${message}/${fName}"
        #           fi

        #           fExt="${fName:e}"
        #           [[ -n "${fExt}" ]] && fName="${fName:r}"

        #           if [[ -z "${fExt}" && ${line} =~ 'Content-type: (([^,]|,[^ ])+)$' ]]; then
        #               fExt=$(getMimeTypes | grep ${match[1]}$'\t' | head -n 1 | awk '{ print $2; }')
        #           fi

        #           mkdir -p ${fName:h}
        #           if [[ -n "${fExt}" ]]; then
        #               fName=$(mktemp -p . "${fName}.XXXXXX.${fExt}")
        #           else
        #               fName=$(mktemp -p . "${fName}.XXXXXX")
        #           fi

        #           notmuchAtts[${attId}]=${fName}
        #       done <<(notmuch show --decrypt=false -- ${message} | tr -d $'\f')

        #       for attId fName in ${(kv)notmuchAtts}; do
        #           [[ -d ${fName:h} ]] || mkdir -p ${fName:h}
        #           printf "#%d → ‘%s’\n" "${attId}" "${fName}" >&2

        #           notmuch show --decrypt=false --part=${attId} -- ${message} | pv -W -D 2 -i 0.1 >${fName}
        #       done
        #     done
        # fi


        ${wormhole} && wormhole receive

	if ${quickserve}; then
	  quickserve --root . --upload . --show-hidden --tar gz
	fi


        if [[ ${#@} -eq 0 ]] || ${forceShell}; then
            if [[ ${#@} -gt 0 ]]; then
                if [[ -z ${nixShell} ]]; then
                    ${@}
                else
                    nix-shell ${nixShell} --run "${@}"
                fi
            fi

            cd $(pwd) # Needed for mounting to work

            isSingleDir() {
                typeset -a contents
                contents=(*(N) .*(N))

                if [[ ${#contents} -eq 1 && -d ${contents[1]} ]]; then
                    print ${contents[1]}
                    return 0
                else
                    return 1
                fi
            }
            while d=$(isSingleDir); do cd ${d}; done


            if [[ -z ${nixShell} ]]; then
                exec -- zsh
            else
                exec -- nix-shell ${nixShell} --run zsh
            fi
        else
            if [[ -z ${nixShell} ]]; then
                exec -- ${@}
            else
                exec -- nix-shell ${nixShell} --run "${@}"
            fi
        fi
    )
}

tmpdir() {
    cleanup()
    {
      cd /
        unmount() {
            printf "Unmounting %s\n" ${1} >&2
            fusermount -u ${1} || umount ${1} || sudo umount ${1}
        }

        if mountpoint -q -- ${dir}; then
            unmount ${dir} || return $?
        else
            while read -d $'\0' subDir; do
                mountpoint -q -- ${subDir} || continue
                unmount ${subDir} || return $?
            done <<<$(find ${dir} -xdev -type d -print0 | sort -zr)
        fi

        rm -rfv --one-file-system -- ${dir}
    }

    local tmpdir=""

    while getopts ':t:a:s:Sd:ir:wqg:n:' arg; do
        case $arg in
            "t") tmpdir="=${OPTARG}" ;;
            "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;;
        esac
    done

    (
        trap cleanup EXIT


        local dir=$(mktemp -ud --tmpdir${tmpdir} ${0}.XXXXXXXXXX || return $?)

        dir -d ${dir} ${@}
    )
}

inhibit-sleep() {
    if systemctl --user is-active prevent-suspend.service 1>/dev/null; then
        echo "Allowing suspend"
        systemctl --user stop prevent-suspend.service
    else
        echo "Inhibiting suspend"
        systemctl --user start prevent-suspend.service
    fi
}

qr() {
    qrencode -l M -o - -t ANSIUTF8 $@
}

clock() {
    tty-clock -sSbc -C 7 -d 0 -a 100000000
}

public-ip() {
    curl -s -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip'
}

nix-ghci() {
    pkgExpr=""
    if [[ ${#@} -gt 0 ]]; then
        pkgExpr="${1}"
        shift
    fi

    nix-shell -p "with (import <nixpkgs> {}); pkgs.haskellPackages.ghcWithPackages (p: with p; [${pkgExpr}])" --run "ghci ${@}"
}

swap() {
    f1=${1}
    f2=${2}

    if [[ -z "${f1}" || ! -e "${f1}" ]]; then
        printf "‘%s’ does not exist\n" "${f1}" >&2
        return 2
    fi
    if [[ -z "${f2}" || ! -e "${f2}" ]]; then
        printf "‘%s’ does not exist\n" "${f2}" >&2
        return 2
    fi

    tmpfile=$(mktemp --dry-run --tmpdir=${f1:h} .swap.XXXXXXXXXX)
    mv -v ${f1} ${tmpfile}
    mv -v ${f2} ${f1}
    mv -v ${tmpfile} ${f2}
}

l() {
  exa --binary --git --time-style=iso --long --all --header --group-directories-first --colour=always $@ | less --mouse -FR
}

re() {
  systemctl --restart $@
}

ure() {
  systemctl --user --restart $@
}

u2wdb() {
  ssh -t postgres@uniworxdb2 psql uni2work
}

ssh-installer() {
  ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@
}

dichotomic_search() {
  min=$1; shift
  max=$1; shift

  while [[ $min -lt $max ]]; do
    # Compute the mean between min and max, rounded up to the superior unit
    current=$(( (min + max + 1 ) / 2 ))
    if $@ $current; then
      min=$current
    else
      max=$((current - 1))
    fi
  done

  echo $min
}

while read -r -d $'\0' d _ s; do
  hash -d ${s}=${d}
done < <(find ~/projects ~/uni -regextype posix-extended -maxdepth 2 -type d -regex '\S+/[0-9]{2}[ws]/\S+' -print0 2>/dev/null | \
       sed -zr 's|(.*/([0-9]{2}[ws])/(.+))|\1 \2 \3|' | \
       sort -z -r -k2 | sort -z -s -k3 | uniq -z -f 2)

alias '..'='cd ..'
alias -g L='| less'
alias -g S='&> /dev/null'
alias -g G='| grep'
alias -g B='&> /dev/null &'
alias -g BB='&> /dev/null &!'

export DEFAULT_USER=gkleen
export EDITOR=emacsclient

bindkey -e
bindkey ';5C' emacs-forward-word
bindkey ';5D' emacs-backward-word