#!/bin/bash
#
# This script was written to keep track of changes that occur to the CanESM source 
# code, along with the various config files needed to run the model.
#
# This script:
#   1. Checks for clean source code, printing warnings if uncommitted changes 
#       are noted. Unless the '-d' (or development) flag is given, this will
#       also cause a non-zero exit status.
#   2. Checks that the commits in the source code match those in .setup-canesm.log, 
#       and updates them if not
#   3. Checks if the config files, provided as arguments, have been changed
#       and adds/commits/pushes them to a config repo if they have.
#   4. Lastly, it updates a run log database by giving it an entry each time
#       this script is called. This database keeps track of the CanESM sha1
#       and Config repo sha1, in addition to the 'event' that called the script.
# define error function
bail(){
    printf "\n*** STRICT CHECKING ERROR ***\n\t $1\n"
    exit 1
}

# make sure script has access to pertinent vars
[ -z "$WRK_DIR" ] && bail "setup up directory not inherited!" 
[ -z "$CCRNSRC" ] && bail "CCRNSRC not inherited!"

#----------------- process flags and arguments
# default behaviour 
PRODUCTION=1
HTML_DUMP=0
while getopts tdx opt
do
    case $opt in
    d) PRODUCTION=0 ;;
    t) HTML_DUMP=1 ;;
    x) set -x ;;
    -) shift; break ;; # end of options
    ?) bail "$opt: UNKNOWN OPTION" ;;
    esac
done
shift $((OPTIND-1))
RUNID=$1 ; shift
EVENT=$1 ; shift 
CFG_FILES2UPDATE=$@

#----------------- define important vars
if [ -z "$CANESM_DFFSFX" ]; then
    LOG_DB="$CCRNSRC/../${RUNID}-log.db"
    CFG_BRANCH="${RUNID}"
else
    LOG_DB="$CCRNSRC/../${RUNID}_${CANESM_DFFSFX}-log.db"
    CFG_BRANCH="${RUNID}_${CANESM_DFFSFX}"
fi

SRC_DIR="$CCRNSRC/CanESM"
LOG_F=".setup-canesm.log"
FILES2UPDATE="README.md env_setup_file $LOG_F $CFG_FILES2UPDATE"

SUBMODS=("CanAM" "CanDIAG" "CanCPL" "CanNEMO" "CCCma_tools")
SPR_REPO_ID="CanESM"
CFG_REPO="$WRK_DIR/.config-repo"
LOG_DISPLAY_DIR='/home/scrd104/public_html/canesm-run-logs'

#----------------- Check for clean source code
cd $SRC_DIR
printf "Checking for clean source code in\n\t $SRC_DIR ...\n"

#-- check for modified content
#   - catches untracked content in submodules and uncommitted changes
#     in super-repo and submodules
git diff-index --quiet HEAD || mods="yes"
if [ -n "$mods" ] ; then
    printf "\t UNCOMMITED OR UNTRACKED CONTENT FOUND IN\n"
    printf "\t\t $SRC_DIR\n"
    [[ "$PRODUCTION" -eq "1" ]] && bail "ALL PRODUCTION RUNS MUST HAVE THEIR CODE COMMITTED!" 
fi
#-- check for untracked content in top level of super-repo
untracked_files=`git ls-files --other --exclude-standard`
if [ -n "$untracked_files" ] ; then
    mods="yes"
    printf "\t UNTRACKED CONTENT FOUND IN\n\t\t $SRC_DIR\n"
    [[ "$PRODUCTION" -eq "1" ]] && bail "ALL PRODUCTION RUNS MUST HAVE THEIR CODE COMMITTED!"
fi
[ -n "$mods" ] || printf "Source code clean...\n"

#----------------- Check that commits match those in .setup-canesm.log
printf "Comparing source code hashes to .setup-canesm.log\n"

#-- compare CanESM hash
SRC_CANESM_SHA1=`git rev-parse HEAD`
# extract most recent hash for super-repo and trip whitespace with awk
LOG_CANESM_SHA1=`sed -n -e "/^$SPR_REPO_ID:/ s/$SPR_REPO_ID://p" $WRK_DIR/$LOG_F | tail -1 | awk '{print $1}'`
if [[ -z "$LOG_CANESM_SHA1" ]]; then 
    if [[ $PRODUCTION -eq 1 ]]; then 
        # hard fail
        bail "Failed to extract $SPR_REPO_ID SHA1 from $LOG_F"
    else
        # give warning and skip checking of hashes
        printf "\n*** STRICT CHECKING WARNING ***\n"
        printf "\t Failed to extract $SPR_REPO_ID SHA1 from ${LOG_F}\n"
        printf "\t Has something strange been done to the log file?\n"
        printf "\t ... skipping source code hash comparisons ... \n"
    fi
else
    # compare hashes 
    if [ "$SRC_CANESM_SHA1" == "$LOG_CANESM_SHA1" ]; then
        #-- hash agrees, and source code clean, log must be up to date
        printf ".setup-canesm.log up to date with run source code\n"
    else
        #-- logs need updating
        printf "Logged SHA1s out of date with source code\n\t..updating logs\n"

        #-- get hashes from submodules
        counter=0
        for MODULE in "${SUBMODS[@]}"; do
            REPO_DIR="$SRC_DIR/$MODULE"
            cd $REPO_DIR
            SRC_MODHASHES[$counter]=`git rev-parse HEAD`
            counter=$((counter+1))
            cd $SRC_DIR
        done

        #-- update .setup-canesm.log
        echo "~~~Source Code Update" >> $WRK_DIR/$LOG_F
        echo `date` >> $WRK_DIR/$LOG_F
        echo "New code corresponds to SHA1s:" >> $WRK_DIR/$LOG_F
        echo "$SPR_REPO_ID: $SRC_CANESM_SHA1" >> $WRK_DIR/$LOG_F
        for ((i=0;i<${#SUBMODS[@]};++i)); do
            echo "${SUBMODS[i]}: ${SRC_MODHASHES[i]}" >> $WRK_DIR/$LOG_F
        done
    fi
fi

#----------------- update config repo with desired file list
printf "Updating config repo... \n"
cd $CFG_REPO
FILES_UPDATED=""
for cfg_f in $FILES2UPDATE; do
    # list files in case the input has a wild card in it
    for wd_f in `ls ${WRK_DIR}/${cfg_f}`; do 
        f=$(basename $wd_f)

        # update file via hard copy
        cp -f $wd_f $f

        # only add files if they are untracked or have changed
        git ls-files --error-unmatch $f >> /dev/null 2>&1 || untracked="yes"
        if [ "$untracked" = "yes" ] ; then  
            # add
            printf "$f: untracked .. adding\n"
        else
            # check for differences
            DIFF=`git diff $f`
            [ -z "$DIFF" ] && { printf "$f: no diff\n" ; continue ; } # skip file if no diff is present
            printf "$f: diff detected\n"  
        fi

        # files cant be staged from mom nodes, so jump to headnode if necessary
        if [[ "$HOST" == *"mom"* ]]; then
            ssh $HDNODE "cd $CFG_REPO && git add -f $f"
        else
            git add -f $f
        fi
        FILES_UPDATED="$FILES_UPDATED $f"
    done
done

# Commit/Push Changes
#   - must be committed on the head node
CMMT_MSG="$EVENT : Updated $FILES_UPDATED"
if [[ "$HOST" == *"mom"* ]] ; then
    ssh $HDNODE "
        cd $CFG_REPO 
        git commit -m \"$CMMT_MSG\" || printf \"Nothing to update.. \n\"
        git push origin $CFG_BRANCH || exit 1
        "
        [[ $? -eq 1 ]] && pushfail="yes"
else
    git commit -m "$CMMT_MSG" || printf "Nothing to update... \n"
    git push origin $CFG_BRANCH || pushfail="yes"

fi
    
# handle push errors
if [[ -n "$pushfail" ]]; then 
    if [[ $PRODUCTION -eq 1 ]]; then
        # hard fail
        bail "Failed to push config repo modifications! Make sure account $(whoami) is added to the CanESM gitlab group!"
    else
        # send warning
        printf "\n*** STRICT CHECKING WARNING ***\n"
        printf "\tA non-zero exit status was returned when trying to push the config repo in ${CFG_REPO}\n"
        printf "\tMake sure account $(whoami) is part of the CanESM gitlab group!\n"
    fi
fi

CFG_SHA1=`git rev-parse HEAD`

#----------------- update run log database
printf "Updating run log database $LOG_DB ... \n"
datef=`date +"%Y-%m-%d %T"`
run_log_db_update="INSERT INTO events (Date, Event, Config_SHA1, CanESM_SHA1, Production)\
                    values('$datef','$EVENT','$CFG_SHA1','$SRC_CANESM_SHA1','$PRODUCTION')"
sqlite3 $LOG_DB "$run_log_db_update"

#----------------- dump run log database to html text file
if [ $HTML_DUMP -eq 1 ]; then
    RUN_DISP_DIR=${HPFX_RUNLOG_DIR}/${CFG_BRANCH}
    COLUMN_TITLES=`sqlite3 $LOG_DB "Pragma table_info(events)" | awk -F"|" '{print $2}'`

    # check if the hpfx runlog display directory has been created.. it shouldve been created in 'adv-setup'
    if [[ ! -d $RUN_DISP_DIR ]]; then
        if [[ $PRODUCTION -eq 1 ]]; then
            # hard fail
            bail "hpfx run-log dir, $RUN_DISP_DIR, doesn't exist! Did something fail during the setup of this run?"
        else
            # send warning
            printf "\n*** STRICT CHECKING WARNING ***\n"
            printf "\t hpfx run-log dir, $RUN_DISP_DIR, doesn't exist! Did something fail during the setup of this run?\n" 
            printf "\t skipping the population of hpfx runlog\n"
        fi
    else
        # runlog display dir exists, populate the runlog
        TABLE=$RUN_DISP_DIR/table

        # insert titles
        echo "<TR>" > $TABLE
        for title in $COLUMN_TITLES; do
            echo "<TH>${title}</TH>" >> $TABLE
        done
        echo "</TR>" >> $TABLE

        # Dump database
        sqlite3 -html $LOG_DB "select * from events" >> $TABLE
    fi
fi
