#!/bin/sh

    # TODO:
    # Create new repos using --template
    # Create fcfg if it doesn't exist
    # Source any existing f* script except fsetup, mark as ignored if not found
    # DONE: Update 'nest/index.html' if -new/-hide/-unhide
    # push 'nest/index.html' changes
    # create repos/README.md
    # config repos to point home page to repos/README.md
    # push 'repos/README.md'
    # sync push repos config
    # exit

    # echo TODO BUGFIX
    # echo "If there are changes in a local checkout, the findex does not work as expected (index.html changes not commited)."
    # echo

# Dir where this script is running
SCRIPT_NAME=`readlink -f "$0"`
SCRIPT_DIR=`dirname "${SCRIPT_NAME}"`

# Repos config
ROOT_REPOS_NAME=nest
REMOTE_SERVER=redantig@kuu.se
REMOTE_PORT=7822
REMOTE_REPOS_DIR=/home/redantig/fossil/repos
REMOTE_REPOS_DIR_CLONE=fossil/repos
REMOTE_CGI_DIR=/home/redantig/www/kuu.se/fossil

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

# Repos command line and server lists
REPOS_LIST_CLI=
REPOS_LIST_SERVER=

# Flags
FLAG_NEW=
FLAG_HIDE=
FLAG_UNHIDE=

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

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

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


# Usage
usage()
{
cat << EOF    

Usage:
  $0 [-n|--new] [-h|--hide|-u|--unhide] <respository-name> [<respository-name2>] ...

Setup/update existing repos:
  $0 b64 markdowndocbook qt-tut

Setup a new repos:
  $0 -n mynewrepos

Mark a repos (new or existing) as hidden:
  $0 -h hidemyrepos

Unhide a hidden repos (new or existing):
  $0 -u unhidemyrepos

To setup the 'nest' repos:
  $0 nest
EOF
}

# Info
info()
{
cat << EOF    

Info:
  This script performs the following steps:
    1. Setup a working copy of the required main repository 'nest' (this repository).
    2. Get a list of existing repos on the server, calling the 'flist' script.
    3. Check for options:
       a. If the -n|--new option is given, create one or more remote repositories on a remote server
          A CGI script for each new repos is created on the server.
          The new repos must not exist on the server.
       b. If the -h|--hide option is given, add the repository name to the .hidden file on the server
          Also remove any existing CGI script for the repos to hide.
       c. If the -u|--unhide option is given, remove the repository name to the .hidden file on the server
          Also create a CGI script for the repos to unhide.
       d. If either of the -n/-h/-u options was given, update 'index.html' locally.
          Then push the changes to the server repos ('index.html' belongs to this repos, 'nest')
       e. If no options were given, the given repos must exist on the server.
    3. Clone the remote repositories given from the command line.
    4.
       a. If a local working copy does not exist, create it in a subdirectory to 'nest':
            'fossil open --nested <RESPOSITORY-NAME>'
       b. If a local working copy already exists, update it:
            'fossil update'

Limitations:
  A nested repository cannot have the same name as the main repository (currently 'nest').
    
Alternatives:
  Use 'fossil-setup-standalone.sh' instead if you want to setup a 'non-nested', stand-alone respository.
    
TODO: Put server settings in a separate file:
ROOT_REPOS_NAME=nest, REMOTE_SERVER=redantig@redantigua.com, REMOTE_REPOS_DIR=/home1/redantig/repos , REMOTE_PORT=7822

EOF
}

help_and_exit()
{
    usage
    info
    exit 1
}

# Check that main repos ('nest') and other repos are not mixed in the same command.
check_for_main_in_repos_list()
{
    if [ $# -gt 1 ]; then
        echo ${REPOS_LIST_CLI} | 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_LIST_CLI}\nPlease either select only '${ROOT_REPOS_NAME}', or a list of respositories excluding '${ROOT_REPOS_NAME}'.\n\n" && help_and_exit
    fi
}

# Returns an list of any existing repos, empty list if no repos exist.  
check_for_existing_repos()
{
    EXISTING_REPOS=
    for r_cli in $@; do
        echo "${REPOS_LIST_SERVER}" | grep -q "${r_cli}"
        if [ $? -eq 0 ];then
            EXISTING_REPOS="${EXISTING_REPOS} ${r_cli}"
        fi
    done
    echo "${EXISTING_REPOS}"
}


# Check for new repos
# If '-n' given, create new repos, must not exist on server
# The implementation of the '-n' flag prevents of creating a new repos by mistake (typo) when the intention was to update an existing repo.
# This is a rather slow process, as a list of all repos has to be fetched from the server.
# The advantage is that is only done once in a lifetime per repos.
check_for_new_repos()
{
    if [ -n "$FLAG_NEW" ];then
        printf "Fetching repos list from server...\t\t"
        REPOS_LIST_SERVER=`"${SCRIPT_DIR}/flist"`
        if [ -z "$REPOS_LIST_SERVER" ];then
            printf "\nERROR:\n"
            printf "Could not fetch repos list from server.\n"
            printf "Internet or server connection problem?\n"
            exit 1
        fi
        printf "OK\n"
        printf "Checking for existing repos on server...\t"
        EXISTING_REPOS=`check_for_existing_repos ${REPOS_LIST_CLI}`
        if [ -n "${EXISTING_REPOS}" ];then
            printf "ERROR\n"
            printf "\nERROR: The following repos already exist on the server: ${EXISTING_REPOS}\n"
            printf "\nTIP:\n"
            printf "Remove the '$FLAG_NEW' option to setup/update existing repositories.\n"
            printf "Run 'flist' to see all existing repositories.\n"
            usage;exit 1
        fi
        printf "OK\n"
    fi
}

# Check if a repos should be hidden or unhidden
# If '-h' given:
#  - add this repo name to '$REMOTE_REPOS_DIR/.hidden'.
#  - remove CGI script for this repo
# If '-u' given:
#  - remove this repo name to '$REMOTE_REPOS_DIR/.hidden'.
#  - add CGI script for this repo
check_if_hide_or_unhide_repos()
{
    if [ -n "$FLAG_HIDE" ];then
        "${SCRIPT_DIR}/fhide" ${REPOS_LIST_CLI}
        "${SCRIPT_DIR}/fcgi" -d ${REPOS_LIST_CLI}
    elif [ -n "$FLAG_UNHIDE" ];then
        "${SCRIPT_DIR}/fhide" -u ${REPOS_LIST_CLI}
        "${SCRIPT_DIR}/fcgi" ${REPOS_LIST_CLI}
    fi
}

# Argument check
check_args()
{
    if [ $# -lt 1 ]; then
        help_and_exit
    fi
    for arg in $@; do
        case "$arg" in
            -*)
                if [ "$arg" = "-n" -o "$arg" = "-new" -o "$arg" = "--new" ];then
                    FLAG_NEW="$arg"
                elif [ "$arg" = "-h" -o "$arg" = "-hide" -o "$arg" = "--hide" -o "$arg" = "-hidden" -o "$arg" = "--hidden" ];then
                    FLAG_HIDE="$arg"
                elif [ "$arg" = "-u" -o "$arg" = "-unhide" -o "$arg" = "--unhide" -o "$arg" = "-unhidden" -o "$arg" = "--unhidden" ];then
                    FLAG_UNHIDE="$arg"
                else
                    printf "\nERROR: Unknown option '$arg'.\n"; help_and_exit
                fi
                ;;
            *)
                REPOS_LIST_CLI="${REPOS_LIST_CLI} $1"
                ;;
        esac
        shift
    done
    if [ -n "$FLAG_HIDE" -a -n "$FLAG_UNHIDE" ];then
        printf "\nERROR: The options '$FLAG_HIDE' and '$FLAG_UNHIDE' are mutually exclusive.\n"
        usage;exit 1
    fi
    set -- ${REPOS_LIST_CLI}
    check_for_main_in_repos_list ${REPOS_LIST_CLI}
    check_for_new_repos ${REPOS_LIST_CLI}
    check_if_hide_or_unhide_repos ${REPOS_LIST_CLI}
}


# 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
setup_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}'...\t\t"
        fossil clone ssh://${REMOTE_SERVER}/${REMOTE_REPOS_DIR_CLONE}/${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\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\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\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
}

# Setup nested repository (one or many)
setup_nested()
{
    REMOTE_REPOS_LIST=$@
    # Create and 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_FULL_PATH_CLONE=${REMOTE_SERVER}/${REMOTE_REPOS_DIR_CLONE}/${r}.fossil
        REMOTE_REPOS_FILE=${REMOTE_REPOS_DIR}/${r}.fossil
        LOCAL_REPOS_FULL_PATH=${LOCAL_REPOS_DIR}/${r}.fossil
        if [ -n "$FLAG_NEW" ];then
            # Create new remote fossil repos if it doesn't exist
            ${SSH_COMMAND} ${REMOTE_SERVER} "if [ ! -f "${REMOTE_REPOS_FILE}" ];then fossil new ${REMOTE_REPOS_FILE}; fi"
        fi
        # Optionally, hide/unhide
        check_if_hide_or_unhide_repos
        if [ -n "$FLAG_HIDE" ];then
            # Create CGI script for web access to remote repos, and make it executable (755)
            ${SSH_COMMAND} ${REMOTE_SERVER} "if [ ! -f '${REMOTE_CGI_DIR}/${r}.cgi' ];then echo '#!/home/redantig/bin/fossil' > '${REMOTE_CGI_DIR}/${r}.cgi'; echo 'repository: ${REMOTE_REPOS_DIR}/${r}.fossil' >> '${REMOTE_CGI_DIR}/${r}.cgi'; chmod 755 '${REMOTE_CGI_DIR}/${r}.cgi'; fi"
        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}'...\t\t"
            fossil clone ssh://${REMOTE_REPOS_FULL_PATH_CLONE} ${LOCAL_REPOS_FULL_PATH} > /dev/null 2>&1
            # Exit on clone error
            if [ $? -ne 0 ]; then
                echo "ERROR"
                echo "Error when trying to clone repository '${r}'!"
                # Check if remote repo exists
                # Repo MUST NOT exist if '--new' flag was given
                # Repo MUST exist if '--new' flag wasn't given
                REMOTE_REPOS_EXISTS=`${SSH_COMMAND} ${REMOTE_SERVER} "test -f ${REMOTE_REPOS_FILE} && echo YES || echo NO"`
                if [ "${REMOTE_REPOS_EXISTS}" = "NO" ];then
                    if [ -z "$FLAG_NEW" ];then
                        echo "ERROR"
                        echo "Tried to clone '${r}', but this repository does not exist!"
                        echo "If you want to setup a new repository, use the '-n|--new' option, like this:"
                        echo
                        echo "  `basename $0` --new $@"
                        echo
                    fi
                else
                    printf "Hmmm, unexpected error.\n"
                    printf "It seems like the repos file '${REMOTE_REPOS_FILE}' exists on server '${REMOTE_SERVER}', but could not be cloned."
                    printf "Try to clone it manually:\nfossil clone ssh://${REMOTE_REPOS_FULL_PATH_CLONE} ${LOCAL_REPOS_FULL_PATH}\n"
                fi
                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\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\t"
        cd ${LOCAL_SRC_DIR} && timeout 10 fossil update > /dev/null 2>&1 && printf "OK\n" || printf "ERROR\nError when updating '${r}', please try 'cd ${LOCAL_SRC_DIR} && fossil update' manually.\n"
        # Create local README, only for new repos
        if [ ! -f ${LOCAL_SRC_DIR}/README.md ];then
           printf "Creating README template '${LOCAL_SRC_DIR}/README.md'...\t\t"
           cd ${LOCAL_SRC_DIR} && FOSSIL_CREATE_README_OUTPUT=`echo "README for ${r}" >  README.md 2> /dev/null`
           if [ $? -eq 0 ]; then
               printf "OK\n"
           else
               printf "ERROR\n%s\n" "${FOSSIL_CREATE_README_OUTPUT}"
           fi
        else
           printf "README.md does already exist for this project, skip creating template.\n"
        fi
        # Add missing entries to the SQL table 'config'
        SQL_PROJECT_NAME_EXISTS=`fossil sql --readonly "SELECT COUNT(*) FROM config WHERE name='project-name'" 2> /dev/null`
        SQL_PROJECT_DESC_EXISTS=`fossil sql --readonly "SELECT COUNT(*) FROM config WHERE name='project-description'" 2> /dev/null`
        SQL_INDEX_PAGE_EXISTS=`fossil sql --readonly "SELECT COUNT(*) FROM config WHERE name='project-description'" 2> /dev/null`

    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 "  fossil setting --global gdiff-command \\\"/c/Program\ Files\ \(x86\)/Meld/Meld.exe\""\\
    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.
check_args $@
ssh_keys

# Setup the main repos, and get a list of existing nested repos.
if [ "$1" = "nest"  ]; then
    setup_main
    get_remote_repos_list
    setup_nested ${REMOTE_REPOS_LIST}
else
    # Setup one or more nested repos, with names given from the command line (new or existing).
    setup_nested ${REPOS_LIST_CLI}
    # If this is a new repos, create CGI on server, update index.html, and create a README template if it doesn't exist.
    if [ -n "$FLAG_NEW" ];then
        "${SCRIPT_DIR}/fcgi" --no-verify ${REMOTE_REPOS_LIST}
        "${SCRIPT_DIR}/findex" --add ${REMOTE_REPOS_LIST}
    fi
fi

recommendations

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