#!/bin/bash
##########################################
# @author Brett Batie
# source code & documentation @ https://github.com/brettbatie/dotfiles/
##########################################


# SETTINGS

dotDir=$HOME/dotfiles
masterRepository=https://github.com/brettbatie/dotfiles.git

# comma delimited list for only creating a minimal set of symlinks
minimalSymlinkFiles=".vimrc,.gitconfig"

#########################################

function help {
cat << EOF
USAGE: $(basename $0) [-options] [-d /path/to/dotFileDirectory] [-r masterRepository]

    -d dotFileDirectory : The directory that contains the static files that will
                         be linked to from the ~ directory. If the 
                         dotFileDirectory is not specified then ~/dotfiles is
                         used by default.

    -r masterRepository : The url/path to the git repository that changes will 
                         be pushed and pulled from.

    -a : Will create symlinks for all files in the dot file directory without 
         asking questions. Special directories (bin, source, etc) will still be
         skipped.

    -m : Create symlinks using the minimal list

    -h : this help screen
EOF



}

function checkParameters {
    if [ "$1" ==  "--help" ]; then
        help
        exit 0
    fi

    local OPTIND=1 # Reset in case getopts has been used previously in the shell.

    while getopts ":d:r:amh" opt; do
      case $opt in
        d)
            dotDir=$OPTARG;
            ;;
        r)
            masterRepository=$OPTARG;
            ;;
        a)
            symlinkOption="all"
            ;;
        m)
            symlinkOption="minimal"
            ;;
        h|\?)
            help
            exit 0
            ;;
      esac
    done

    # Make sure the dotfile directory structure exists
    if [ ! -d "$dotDir" ]; then
        mkdir -v -p "$dotDir";
    fi

    # Trim the trailing slash from the dot directory (if there is one)
    if [[ $dotDir == */ ]]; then
        dotDir="${dotDir%?}"
    fi

    # Set directories based on defined dotDir.
    backupDir=$dotDir/dotfiles-backup
    customDir=$dotDir/custom
    binDir=$dotDir/bin
    sourceDir=$dotDir/source

}

function updateRepo {
    # Check if git is installed before doing git operations
    type git >/dev/null 2>&1 || { 
        echo "************************************"
        echo >&2 "Git is required to clone/pull dot files from a remote repository. "\
        "Symlinks can still be auto created to files in $dotDir but a remote repository will not be used.";
        echo "************************************"
        return;
    }

    # if there is repo here update it, otherwise create the repo
    if [ ! -d "$dotDir/.git" ]; then
        displayTitle "DOWNLOADING MASTER REPOSITORY ($masterRepository)"
        git clone $masterRepository $dotDir
    else
        displayTitle "Updating Master Repository"
        (cd $dotDir && git pull $masterRepository)
    fi
}

function createDirectories {
    # Verify that the backup directory exists
    if [ ! -d "$backupDir" ]; then
        mkdir -v -p "$backupDir"
    fi

    if [ ! -d "$sourceDir" ]; then
        mkdir -v -p "$sourceDir"
    fi

    if [ ! -d "$binDir" ]; then
        mkdir -v -p "$binDir"
    fi

    if [ ! -d "$customDir" ]; then
        mkdir -v -p "$customDir"
    fi

    # Download and save dotm to the bin directory if this script is running from
    # a remote location and the dotfiles directory was empty.
    if [ ! -f "$binDir/dotm" ] && [ ! hash dotm >/dev/null 2>&1 ]; then
        # FIXME: could add check here to see if wget exists and fall back to fetch or curl
        wget -nv -O $dotDir/bin/dotm https://raw.github.com/brettbatie/dotfiles/master/bin/dotm
        chmod +x $dotDir/bin/dotm
    fi
}

function symlinkOption {
    # no need to ask questions if the user specified the setting with a startup 
    # parameter
    if [ -n "$symlinkOption" ]; then
        return;
    fi

    echo -e "\n\n"
    PS3="Select above option regarding how to create the symlinks:"
    options=("Create all symlinks                         " #hack to get the columns on new lines. Is there a better way?
    "Ask before creating each"
     "Use minimal list ($minimalSymlinkFiles)"
     )
    select opt in "${options[@]}"
    do
        case $opt in
            "Create all symlinks                         ")
                symlinkOption="all"
                break;
                ;;
            "Ask before creating each")
                symlinkOption="ask"
                break;
                ;;
            "Use minimal list ($minimalSymlinkFiles)")
                symlinkOption="minimal"
                break;
                ;;
            *) echo invalid option;;
        esac
    done

    # Determine if we are going to use a minimal list of files to symlink
    if [ "$symlinkOption" == "minimal" ]; then
        if [ "$minimalSymlinkFiles" == "" ]; then
            echo "No minimal files to symlink, exiting."
            exit 1;
        fi
        minimalSymlinkFiles="-name ${minimalSymlinkFiles/,/ -o -name }"
    else
        minimalSymlinkFiles=""
    fi

    
    # FIXME: this find command should be using the same find arguments as in the 
    #        function createSymLink. Should create that as a variable earlier 
    #        in the code. I believe this can be accomplished by using eval but
    #        it might be evil http://mywiki.wooledge.org/BashFAQ/048

    # Check for files before going any further
    fileCount=$(find "$dotDir" \( -type f $minimalSymlinkFiles \
                -not -path "*/*\.lnk/*" \
                -not -path "*/.hg/*" \
                -not -path "*/.git/*" \
                -not -path "$dotDir/README.md*" \
                -not -path "$backupDir/*" \
                -not -path "$binDir/*" \
                -not -path "$customDir/*" \
                -not -path "$sourceDir/*" \) \
                -o -type d -path "*\.lnk" | wc -l);
    if [ "$fileCount" == "0" ]; then
        echo -e "\nThere are no files in the $dotDir. No symlinks will be created."
        exit 0;
    fi
}

function displayTitle {
    echo -e "\n\e[4m\033[1m$1\033[0m"
}

function createSymLinks {

    local symlinksCreated=0;

    displayTitle "Creating Symlinks in ~ for files in $dotDir"

    # create the symlinks for every file in the given dotfile directory.
    for file in $(find "$dotDir" \( -type f $minimalSymlinkFiles \
                -not -path "*/*\.lnk/*" \
                -not -path "*/.hg/*" \
                -not -path "*/.git/*" \
                -not -path "$dotDir/README.md*" \
                -not -path "$backupDir/*" \
                -not -path "$binDir/*" \
                -not -path "$customDir/*" \
                -not -path "$sourceDir/*" \) \
                -o -type d -path "*\.lnk" ); do

        # get the file with a path relative to the dotDir
        local relativeFile=${file#$dotDir} #remove $dotDir from $file

        # Remove the leading slash if there is one
        if [[ $relativeFile == /* ]]; then
            relativeFile=${relativeFile:1}
        fi

                # remove .lnk from the dirctory name
        if [ -d "$file" ]; then
            relativeFile=${relativeFile%.lnk}
        fi

        # If the symlink is already in place, move to the next file, nothing to do
        local destinationFile="$(readlink -f $HOME/$relativeFile)"
        if [ "$destinationFile" == "$file" ]; then
            continue;
        fi


        if [ "$symlinkOption" == "ask" ]; then
            read -p "Create symlink ~/$relativeFile -> $file? [y/n] " -n 1 -r
            echo 
            # if not yes, then skip it
            if [[ ! $REPLY =~ ^[Yy]$ ]]; then
                continue
            fi
        fi

        # Create a backup if there is a file or directory that will be overwritten
        if [ -f "$HOME/$relativeFile" -o -d "$HOME/$relativeFile" ]; then
            echo "Making backup of $relativeFile in $backupDir"
            # Strip the filename from the full path
            fullBackupPath=$backupDir/$(dirname ${relativeFile})

            # Make sure that the backup directory and sub directories exist
            if [ ! -d $fullBackupPath ]; then
                mkdir -p $fullBackupPath
            fi

            # backup the file
            mv $HOME/$relativeFile $backupDir/$relativeFile.$(date +"%Y%m%d%H%M")
        fi

        # Strip the filename from the relative Path
        local destinationDir=$HOME/$(dirname ${relativeFile})

        # Make sure that the destination directory exists
        if [ ! -d $destinationDir ]; then
            mkdir -p $destinationDir
        fi

        echo "Creating symlink ~/$relativeFile -> $file"
        ln -s "$file" "$HOME/$relativeFile"

        # If this is a bashrc file it will need to be sourced
        if [ "$relativeFile" == ".bashrc" ]; then
            # wish I could automate this for the parent shell.
            echo "**************************"
            echo 'To update your current shell run the command, source ~/.bashrc';
            echo "**************************"
        fi

        symlinksCreated=$(($symlinksCreated+1))
    done;

    echo "$symlinksCreated new symlinks created in ~"
}

checkParameters $@
updateRepo
createDirectories
symlinkOption
createSymLinks