#!/usr/bin/env bash

# Handles dotfiles
# Yes there are tons of similar scipts yet I wanted no more nor less than what I needed

# Config

if [ -z "$DOTHOME" ]; then
    DOTHOME="$HOME"
fi
if [ -z "$DOTREPO" ]; then
    DOTREPO="$HOME/.dotfiles"
fi

# Common functions

# From http://stackoverflow.com/a/12498485
function relativePath {
    # both $1 and $2 are absolute paths beginning with /
    # returns relative path to $2/$target from $1/$source
    source=$1
    target=$2

    common_part=$source # for now
    result="" # for now

    while [[ "${target#$common_part}" == "${target}" ]]; do
        # no match, means that candidate common part is not correct
        # go up one level (reduce common part)
        common_part="$(dirname $common_part)"
        # and record that we went back, with correct / handling
        if [[ -z $result ]]; then
            result=".."
        else
            result="../$result"
        fi
    done

    if [[ $common_part == "/" ]]; then
        # special case for root (no common path)
        result="$result/"
    fi

    # since we now have identified the common part,
    # compute the non-common part
    forward_part="${target#$common_part}"


    # and now stick all parts together
    if [[ -n $result ]] && [[ -n $forward_part ]]; then
        result="$result$forward_part"
    elif [[ -n $forward_part ]]; then
        # extra slash removal
        # result="${forward_part:1}" # Removes the . in the beginning...
        result="${forward_part#/}"
    fi

    echo "$result"
}


# Script common functions

function _dotfiles-install-dir { # dir
    local dir
    local absSource
    local absTarget
    local relTarget

    dir="${1%/}"
    dir="${dir#/}"

    ls -A "$DOTREPO/$dir" | while read file; do
        if [[ -z "$dir" && $(echo $file | grep '^\(\.\|LICENSE\|README\)') ]]; then
            continue
        fi
        if [[ $(echo $file | grep '^.dfrecur$') ]]; then
            continue
        fi

        if [ -z "$dir" ]; then
            absSource="$DOTHOME/.$file"
            absTarget="$DOTREPO/$file"
        else
            absSource="$DOTHOME/.$dir/$file"
            absTarget="$DOTREPO/$dir/$file"
        fi
        relTarget="$(relativePath "$DOTHOME/$dir" "$absTarget")"
        recurIndicator="$absTarget/.dfrecur"

        if [[ -h "$absTarget" ]]; then
            if [ -e "$absSource" ]; then
                if [ -h "$absSource" ]; then
                    cmd="cp --no-dereference --force $absTarget $absSource"
                    if [ $DRY_RUN ]; then
                        echo $cmd
                    else
                        yes | $cmd
                    fi
                else
                    echo "[ERROR] $absSource already exists, but is not a link"
                fi
            else
                cmd="cp --no-dereference --force $absTarget $absSource"
                if [ $DRY_RUN ]; then
                    echo $cmd
                else
                    yes | $cmd
                fi
            fi
        elif [[ -f "$absTarget" || ( -d $absTarget && ! -f $recurIndicator ) ]]; then
            if [ -e "$absSource" ]; then
                if [ -h "$absSource" ]; then
                    cmd="ln --symbolic --no-dereference --force $relTarget $absSource"
                    if [ $DRY_RUN ]; then
                        echo $cmd
                    else
                        $cmd
                    fi
                else
                    echo "[ERROR] $absSource already exists, but is not a symbolic link"
                fi
            else
                cmd="ln --symbolic --no-dereference $relTarget $absSource"
                if [ $DRY_RUN ]; then
                    echo $cmd
                else
                    $cmd
                fi
            fi
        elif [[ -d "$absTarget" && -f $recurIndicator ]]; then
            if [ -e "$absSource" ]; then
                if [ -d "$absSource" ]; then
                    # echo "Directory $absSource already exists"
                    _dotfiles-install-dir $dir/$file
                else
                    echo "[ERROR] $absSource already exists, but is not a directory"
                fi
            else
                cmd="mkdir $absSource"
                if [ $DRY_RUN ]; then
                    echo $cmd
                else
                    $cmd
                fi
                _dotfiles-install-dir $dir/$file
            fi
        else
            echo "[WARNING] Skipped $absTarget"
        fi
    done

}

# Script functions

function dotfiles_link_help {
    echo "Usage: $0 link DOTFILE"
    echo
    echo "Arguments:"
    echo "    DOTFILE  Path to the dotfile"
    return 0

}
function dotfiles_link { # file
    if [ -z $1 ]; then
        dotfiles_link_help
        return 1
    fi
    absSource="$(realpath $1 2> /dev/null)"
    if [[ $? != 0 || ! -e "$absSource" ]]; then
        echo "[ERROR] $1: no such file or directory"
        return 1
    fi
    relSource="$(relativePath $DOTHOME $absSource)"

    absTarget="$DOTREPO/$relSource"
    relTarget="$(relativePath "$DOTHOME/$dir" "$absTarget")"

    if [ -f "$absTarget" ]; then
        echo "[ERROR/UNIMPLEMENTED] $relSource is already linked to ... something"
        return 2
    fi

    if [ -f "$absSource" ]; then
        if [ -d "$(dirname "$absTarget")" ]; then
            cmd="mv $absSource $absTarget"
            cmd2="ln --symbolic --no-dereference $relTarget $absSource"
            if [ $DRY_RUN ]; then
                echo $cmd
                echo $cmd2
            else
                $cmd
                $cmd2
            fi

        else
            echo "[UNIMPLEMENTED] Linking a file in a directory that don't already exists"
        fi
    else
        echo "[UNIMPLEMENTED] Linking things other than a file"
        return 12
    fi

}

function dotfiles_install {
    _dotfiles-install-dir /
}

function dotfiles_help {
    command="$1"
    if [ -n "$command" ]; then
        if type "dotfiles_${command}_help" &> /dev/null; then
            shift
            "dotfiles_${command}_help" "$@"
            return $?
        fi
    fi
    echo "Usage: $0 COMMAND"
    echo
    echo "Commands:"
    echo "    install  Install dotfiles from repository"
    echo "    link     Add dotfile to repository"
    echo "    help     Get help with commands"
    echo
    echo "Environment variables:"
    echo "    DOTHOME  Where to install dotfiles"
    echo "    DOTREPO  Where do the dotfiles comes from"
    return 0
}

# MAIN
command="$1"
shift
if type "dotfiles_$command" &> /dev/null; then
    "dotfiles_$command" "$@"
else
    dotfiles_help
fi

# TODO dotfiles-{link,unlink,clean,uninstall}, better handling of DRY_RUN (use functions probably), clarify source/target thingy
# Link and Unlink should have a clever behavior regarding
# recusive folders
# Ex : linking config/i3 should make config recursible
# Ex : linking config if some files in it are linked should unlink those