#!/bin/sh

# fclone - Clone an existing remote Fossil repository

FOSSIL=`which fossil 2>/dev/null`

if [ -z "${FOSSIL}" ];then
    echo no fossil
    exit
else
    echo fossil is ${FOSSIL}
fi

ROOT_REPOS_NAME=nest
REMOTE_SERVER=redantig@kuu.se
REMOTE_PORT=7822
REMOTE_REPOS_DIR=/home/redantig/fossil/repos

SSH_COMMAND=ssh
if [ ${REMOTE_PORT} -ne 22 ];then
    SSH_COMMAND="ssh -p ${REMOTE_PORT}"
    fossil settings --global ssh-command "${SSH_COMMAND}"
fi


REPOS_NAME_LIST=$@

# MS Windows specific function (do not include Cygwin here)
is_windows()
{
    uname -a| grep -q '^MSYS\|^MINGW'
}

is_windows
RC=$?
if [ "${RC}" -eq "0" ]; then
    # MSYS/MSYS2/MINGW32/MINGW64 (Cygwin has no /c directory)
    BASE=/c
else
    # UNIX/Mac OSX/Cygwin
    BASE=${HOME}
fi

# Common variables
LOCAL_REPOS_DIR=${BASE}/fossil/repos
LOCAL_SRC_MAIN=${BASE}/fossil/${ROOT_REPOS_NAME}

# Info
info()
{
    echo
    echo "Info:"
    echo "  This script clones and updates one or more remote repositories from a server."
    echo "  First, the script checks if there is a local copy of the main repository, called 'nest'."
    echo "  If not, the necessary directories are created, and the 'nest' repository is cloned and opened."
    echo "  After that, all other existing repositories (or those indicated on the command line) are cloned."
    echo "  Finally, the repositories are opened inside the 'nest' repository, with the command 'fossil open --nested <RESPOSITORY-NAME>'."
    echo
    echo "Limitation:"
    echo "  A nested repository cannot have the same name as the main repository (currently 'nest')."
    echo
    echo "See also:"
    echo "  Use 'flist' to list all existing respositories on the centralized server."
    echo "  Use 'fsetup' to create a new project on the server and to clone it locally."
    echo "  Use 'fossil-setup-standalone.sh' to setup a 'non-nested', stand-alone respository."
    echo
    echo "TODO2: Add option 'ls' to list all existing respositories, calling 'flist'"
    echo "TODO3: Put ROOT_REPOS_NAME=nest, REMOTE_SERVER=redantig@kuu.se, REMOTE_REPOS_DIR=/home1/redantig/repos , REMOTE_PORT=7822 in a separate file."
    echo "  Then inlcude this small file, easy to be edited."
    echo "TODO4: Add flag '-a' to clone all, -main to clone only 'nest', -nested to clone all subrepos except 'nest'."
    echo
}

# Usage
usage()
{
    if [ $# -eq 0 ]; then
        echo
        echo "Usage:"
        echo "  $0 <respository-name> [<respository-name2>] ..."
        echo
        echo "Example:"
        echo "  $0 armario b64 fiesta2015 markdowndocbook qt-tut"
        echo
        echo "To clone 'nest' itself:"
        echo "  $0 nest"
        info
        exit 1
    fi
}

# Argument check
check_for_main_in_repos_list()
{
    if [ $# -gt 1 ]; then
        echo ${REPOS_NAME_LIST} | grep -q "${ROOT_REPOS_NAME}" && printf "\nERROR: The command line includes the main respository '${ROOT_REPOS_NAME}' together with one or more other respositories:\n  ${REPOS_NAME_LIST}\nPlease either select only '${ROOT_REPOS_NAME}', or a list of respositories excluding '${ROOT_REPOS_NAME}'.\n\n" && usage && exit 1
    fi
}

# SSH keys (setup once, possibly already done, outside this script)
# Note that for the SSH keys, the HOME dir (~) is always the same, even on MS Windows.
ssh_keys()
{
    if [ ! -f ~/.ssh/id_rsa.pub ];then
        echo
        echo "Setup SSH keys..."
        ssh-keygen -q -t rsa -N "" -f ~/.ssh/id_rsa
        cat ~/.ssh/id_rsa.pub | ${SSH_COMMAND} ${REMOTE_SERVER} "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
    fi
}

# Setup main repos
clone_main()
{
    echo
    mkdir -p ${LOCAL_REPOS_DIR} ${LOCAL_SRC_MAIN}
    if [ ! -f ${LOCAL_REPOS_DIR}/${ROOT_REPOS_NAME}.fossil ];then
        echo "Cloning main repository '${ROOT_REPOS_NAME}'..."
        fossil clone ssh://${REMOTE_SERVER}/${REMOTE_REPOS_DIR}/${ROOT_REPOS_NAME}.fossil ${LOCAL_REPOS_DIR}/${ROOT_REPOS_NAME}.fossil
    else
        echo "Main repository '${ROOT_REPOS_NAME}' already exists locally, skip cloning..."
        printf "Opening main repository '${ROOT_REPOS_NAME}'...\t"
        cd ${LOCAL_SRC_MAIN} && FOSSIL_OPEN_OUTPUT=`fossil open ${LOCAL_REPOS_DIR}/${ROOT_REPOS_NAME}.fossil 2> /dev/null` 
        if [ $? -eq 0 ]; then
            printf "OK\n"
            printf "Repository info for '${ROOT_REPOS_NAME}':\n"
            printf "=============================================\n"
            printf "%s\n" ${FOSSIL_OPEN_OUTPUT}| grep -A 1 '^project-name\|^repository\|^local-root'|grep -v '^project-name\|^repository\|^local-root'
        else
            printf "ALREADY OPEN\n"
        fi
        printf "Updating main repository '${ROOT_REPOS_NAME}'...\t"
        cd ${LOCAL_SRC_MAIN} && fossil update > /dev/null 2>&1 && printf "OK\n" || printf "Error when updating '${ROOT_REPOS_NAME}', please try 'cd ${LOCAL_SRC_MAIN} && fossil update' manually.\n" 
    fi
}

# Get list of existing remote repos, excluding the main repository.
get_remote_repos_list()
{
    printf "Get list of existing remote repos, excluding the main repository '${ROOT_REPOS_NAME}'...\t"
    REPOS_NAME=${ROOT_REPOS_NAME}
    LOCAL_SRC_DIR=${BASE}/${ROOT_REPOS_NAME}
    REMOTE_REPOS_LIST=`for i in \`${SSH_COMMAND} ${REMOTE_SERVER} "ls /home/redantig/fossil/repos/*fossil"\`; do if [ "$i" != "${REMOTE_REPOS_DIR}/${ROOT_REPOS_NAME}.fossil" ];then basename "$i" .fossil; fi; done`
    # Get list of repository (one or many)
    if [ -z "${REMOTE_REPOS_LIST}" ]; then
        echo
        echo "ERROR: Something went wrong when trying to get the remote repository list!"
        printf "To get the list manually, try this command:\n\n  for i in \`${SSH_COMMAND} ${REMOTE_SERVER} \"ls /home/redantig/fossil/repos/*fossil\"\`; do if [ \"\$i\" != \"${REMOTE_REPOS_DIR}/${ROOT_REPOS_NAME}.fossil\" ];then basename \"\$i\" .fossil; fi; done\n\n"
        exit 1
    fi
    printf "OK\n"
}

# Check for existing remote repos
# Return 1 if a remote repos exists, 0 if it doesn't exist.
remote_repos_exists()
{
    if [ -z "$1" ];then
        return 0
    fi
    RESULT=`${SSH_COMMAND} ${REMOTE_SERVER} "ls ${REMOTE_REPOS_DIR}/*.fossil 2> /dev/null" | grep '$1.fossil' | grep -v grep`
    if [ -z "${RESULT}" ];then
        return 0
    fi
    return 1
}

# Clone nested repository (one or many)
clone_nested()
{
    REMOTE_REPOS_LIST=$@
    # Clone remote repository and open nested
    for r in ${REMOTE_REPOS_LIST}; do
        echo
        REMOTE_REPOS_FULL_PATH=${REMOTE_SERVER}/${REMOTE_REPOS_DIR}/${r}.fossil
        REMOTE_REPOS_FILE=${REMOTE_REPOS_DIR}/${r}.fossil
        LOCAL_REPOS_FULL_PATH=${LOCAL_REPOS_DIR}/${r}.fossil
        # Do not create new remote fossil repos if it doesn't exist, use 'fsetup' for that
        MISSING_REMOTE_REPOS=`${SSH_COMMAND} ${REMOTE_SERVER} "if [ ! -f "${REMOTE_REPOS_FILE}" ];then echo ${REMOTE_REPOS_FILE}; fi"`
        # Exit on clone error
        if [ -n "${MISSING_REMOTE_REPOS}" ]; then
            echo
            echo "ERROR: Repository '${r}' not found!"
            printf "Tips:\n"
            printf "Run 'flist' to list existing repositories.\n\n"
            printf "Try to clone manually:\n\nfossil clone ssh://${REMOTE_REPOS_FULL_PATH} ${LOCAL_REPOS_FULL_PATH}\n\n"
            printf "To setup a new, non-existing repository on server, run:\n\nfsetup ${r}\n\n"
            exit 1
        fi
        # Check if local repository exist
        if [ ! -f ${LOCAL_REPOS_DIR}/${r}.fossil ];then
            # Create local repository dir if it doesn't exist
            mkdir -p ${LOCAL_REPOS_DIR}
            printf "Cloning nested repository '${r}'..."
            fossil clone ssh://${REMOTE_REPOS_FULL_PATH} ${LOCAL_REPOS_FULL_PATH} > /dev/null 2>&1
            # Exit on clone error
            if [ $? -ne 0 ]; then
                echo
                echo "Error when trying to clone repository '${r}'!"
                printf "Try to clone manually:\nfossil clone ssh://${REMOTE_REPOS_FULL_PATH} ${LOCAL_REPOS_FULL_PATH}\n"
                exit 1
            fi
            echo OK
        else
            echo "Local repository '${LOCAL_REPOS_DIR}/${r}.fossil' already exists, skip cloning."
        fi
        # Create local src dir if it doesn't exist
        LOCAL_SRC_DIR=${LOCAL_SRC_MAIN}/${r}
        mkdir -p ${LOCAL_SRC_DIR}
        # Open repository nested, ignore errors (if it was already opened, for example)
        # TODO: Check for the following error (which means that repository is already opened, and everything is ok). 
        #   SQLITE_ERROR: table vvar already exists
        #   fossil: table vvar already exists
        printf "Opening nested repository '${r}'...\t"
        cd ${LOCAL_SRC_DIR} && FOSSIL_OPEN_OUTPUT=`fossil open --nested ${LOCAL_REPOS_DIR}/${r}.fossil 2> /dev/null` 
        if [ $? -eq 0 ]; then
            printf "OK\n"
            printf "Repository info for '${r}':\n"
            printf "============================================\n"
            printf "%s\n" ${FOSSIL_OPEN_OUTPUT}| grep -A 1 '^project-name\|^repository\|^local-root'|grep -v '^project-name\|^repository\|^local-root'
        else
            printf "ALREADY OPEN\n"
        fi
        printf "Updating nested repository '${r}'...\t"
        cd ${LOCAL_SRC_DIR} && fossil update > /dev/null 2>&1 && printf "OK\n" || printf "Error when updating '${r}', please try 'cd ${LOCAL_SRC_DIR} && fossil update' manually.\n" 
    done
}

# Optional, recommendations
recommendations()
{
    echo
    echo
    echo "Recommended settings, applies to all repositories:"
    echo "=================================================="
    echo "- Meld as graphical diff tool:"
    echo "  fossil setting --global gdiff-command meld"
    echo
    echo "- ssh command when using a SSH port number other than 22:"
    echo "  (This setting is mandatory on MS Windows, recommended on all platforms):"
    echo '  fossil setting --global ssh-command "ssh -p 7822"'
    echo
}

# ----------
# MAIN BEGIN
# ----------

# Check args, setup SSH keys if missing.
usage $@
check_for_main_in_repos_list $@
ssh_keys

# Setup the main repos, and get a list of existing nested repos.
if [ "$1" = "nest"  ]; then
    clone_main
    get_remote_repos_list
    clone_nested ${REMOTE_REPOS_LIST}
else
    # Setup one or more nested repos, with names given from the command line (new or existing).
    clone_nested ${REPOS_NAME_LIST}
fi

recommendations

# ----------
# MAIN END
# ----------
