#!/usr/bin/env sh
# for busybox ash, dash, bash, and zsh.

VERBOSE="${VERBOSE-1}"
NAME="$0"

die() {
    printf '%s:' "$NAME"
    printf ' %s' "$@"
    printf '\n'
    exit 1
} >&2

pl() { # print lines
    printf '%s\n' "$@"
}

backup() {
    : "${1:?missing argument}"
    pl "backing up $1"
    mkdir -p "${backup_dir:?backup_dir unset}/${1%/*}" || die "failed to create backup directory"
    ! [ -e "$backup_dir/$1" ] || die "backup already exists: $backup_dir/$1"
    mv "$1" "$backup_dir/$1" || die "failed to backup $1"
}

hardlink() {
    : "${1:?missing argument}"
    : "${2:?missing argument}"
    if [ -e "$1" ]; then
        ! [ "$1" -ef "$2" ] || return 0
        if [ -h "$1" ]; then
            pl "removing symbolic link $1"
            rm "$1" || die "failed to remove symbolic link"
        fi
        if [ -s "$1" ]; then
            #diff -U3 "$1" "$2" >>/tmp/installed.patch
            backup "$1"
        fi
    fi

    ln "$2" "$1" || die "failed to hardlink $1"
}

softlink_nix() {
    : "${1:?missing argument}"
    : "${2:?missing argument}"
    if [ -e "$1" ]; then
        if [ -h "$1" ]; then
            [ "$(readlink "$1")" != "$2" ] || return 0
            pl "removing symbolic link $1"
            rm "$1" || die "failed to remove symbolic link"
        else
            die "$1 already exists and is not a symbolic link"
        fi
    fi

    ln -s "$2" "$1" || die "failed to symlink $1"
}

list_files() {
    find "${1:-.}" -maxdepth 1 -printf "%P\n" | while read -r f; do
        [ "${#f}" != 0 ] || continue
        pl "$f"
    done
}

softlink_pseudo() (
    : "${1:?missing argument}"
    : "${2:?missing argument}"
    [ -d "$2" ] || die "$1 is not a directory to softlink"
    [ -d "$1" ] || mkdir "$1" || die "failed to mkdir $1"

    list_files "$2" | while read -r f; do
        d1="$1/$f"
        d2="$2/$f"

        if [ -d "$d2" ]; then
            if [ "$d1" != ".vim/bundle" ]; then # buggy on Windows
                [ "$VERBOSE" -lt 2 ] || printf '\033[34m / \033[0m %s %s\n' "$d1" "$d2" >&2
                softlink_pseudo "$d1" "$d2" || exit
            fi
        elif [ -f "$d2" ]; then
            [ "$VERBOSE" -lt 2 ] || printf '\033[34m * \033[0m %s\n' "$d1" >&2
            hardlink "$d1" "$d2" || exit
        else
            die "i don't know how to pseudo-symlink $d2"
        fi
    done || exit
)

find_new_files() (
    : "${1:?missing argument}"
    : "${2:?missing argument}"

    list_files "$1" | while read -r f; do
        d1="$1/$f"
        d2="$2/$f"

        case "$d1" in
        (.vim/.netrwhist) continue;;
        (.vim/backup) continue;;
        (.vim/bundle) continue;;
        (.vim/swp) continue;;
        (.vim/undo) continue;;
        esac

        if [ -d "$d2" ]; then
            find_new_files "$d1" "$d2" || exit
        elif ! [ "$d1" -ef "$d2" ]; then
            [ -d "$d1" ] && ind=/ || ind=
            if [ "$VERBOSE" -lt 1 ]; then
                printf ' + %s%s\n' "$d1" "$ind" >&2
            else
                #printf 'new destination file. consider manually moving it:\n' >&2
                printf '\033[32m + \033[0m %s%s\n' "$d1" "$ind" >&2
            fi
        fi
    done
)

softlink() {
    if [ -n "$MSYSTEM" ]; then
        # MSYS2 does not have nor emulate symbolic links.
        softlink_pseudo "$@" || exit
        # to make up for git status not seeing new files:
        find_new_files "$@" || exit
    else
        softlink_nix "$@"
    fi
}

readlink -e / >/dev/null || die 'failed sanity check (check your $PATH)'

unset CDPATH
rc="$(readlink -f "$0")" && rc="${rc%/*}" && cd "$rc" || die "failed to determine rc directory"
cd "${HOME:?HOME variable empty or unset}" || die "failed to change directory"

backup_dir="$rc/backup-$(date -u +%s)" || die "failed to determine date"
! [ -d "$backup_dir" ] || die "backup directory already exists"

for f in \
    .shrc .bashrc .zshrc .prep .prezto-compinit .ls_colors \
    .vimrc .inputrc .Xresources .screenrc .tmux.conf \
; do
    hardlink "$f" "$rc/home/${f#.}"
done

for d in sh .vim .mpv; do
    softlink "$d" "$rc/${d#.}"
done

# ensure that .bashrc gets executed
if ! [ -e .bash_profile ] || ! grep -qF .bashrc .bash_profile; then
    pl '' '! [ -f ~/.bashrc ] || . ~/.bashrc' >>.bash_profile
fi

# delete any directory structure that may have been included with the OS.
for d in Desktop Documents Downloads Music Pictures Public Templates Video Videos; do
    ! [ -d "$d" ] || rmdir "$f"
done

# create instead my preferred directory structure.
mkdir -p opt/local/bin src work play || die "failed to create directories"