#!/bin/bash
# Can be changed via config and keys
LOGDIR=/var/log/notamocks/$(date +%m.%d.%H.%M)
PARALLEL=3 
TIMEOUT=0.2
MAXITER=10
REWRITE="\e[25D\e[1A\e[K"

# Can be changed via keys
DIRS=$(find ./ -maxdepth 1 -mindepth 1 -type d)
CHAIN='no'

# Can be changed via environment
SUCCESLOG=${SUCCESLOG:-_SUCCES.log}
FAILLOG=${FAILLOG:-_FAILURE.log}
NOTAMOCKSLOG=${NOTAMOCKSLOG:-_NOTAMOCKS.log}
CHAINLOG=${CHAINLOG:-_CHAIN_LOG}
REPOLOG=${REPOLOG:-_REPO_LOG}
CHAINREP=${CHAINREP:-"/tmp/notamocks_rep-$$"}

# internal variables
declare -A procs
RPIDS=''
aaa='==='
REGEXP='.'
ADDREP=''
SEPARATOR="###################################################################"

unset BREAK
unset WITH_TMUX
unset KEEP_OPEN

NOTAMOCKLIB="/usr/share/notamock/libnotamock"
[ -f './libnotamock' ] && NOTAMOCKLIB=./libnotamock
source "$NOTAMOCKLIB"

CFG=/etc/notamock.cfg
# special config for project
[ -f './notamock.cfg' ] && CFG=$(realpath ./notamock.cfg)

source "$CFG"

if [ -d "$CFGD" ] ; then
    for a in $(ls -1 ${CFGD}/. |sort ) ; do
        source ${CFGD}/$a
    done
fi

HLP(){
echo "$0 - "
echo "Usage:" 
echo "cd /dir/with/projects  ; $0 <$(basename $0) pars> <VAR=VALUE for notamock>"
echo "Parameters:
    -h | --help           -  this help
    -d | --projectdirs    -  dirs with projects, default dirs in ./
    -l | --logdir         -  dir for notamock logs, default:  /var/log/notamocks/<month.day.hour.min>/
    -t | --timeout        -  timout before run next notamock process in sec, default $TIMEOUT
    -p | --parallel       -  a max number of notamock processes, default $PARALLEL
    -s | --skip-built     -  do not run build process if project dir contain *.rpm files
    -f | --skip-fallen    -  do not run build process if project dir contain project*.buildlog files
    -R | --regexp         -  filter in a grep format, to select projects from list (default REGEXP='.')
    -w | --whitelist      -  file with whitlist for projects
    -n | --chain          -  chain build
    -m | --maxiter        -  maximum number of iterations for chain build
    -T |--tmux            -  run all notamock processes in separate tmux windows:
                             creates session \"notamocks\" when run outside tmux
                             uses current tmux session when run inside tmux
                             allows interactive monitoring of build processes
    -k |--tmux-keep-open  -  keep tmux windows open after notamok processes
    "
echo "Keys for notamock:
    all keys and parameters except those listed will be sent to notamock at each use"
echo "Variables:
    all notamock variables specified from notamock config file can be changed from the cmdline of $(basename $0) 
    in KEY=VALUE format"
exit
}

while [ -n "$1" ] ; do
    case "${1}" in  
    "-h" | "--help") HLP ;;
    "-d" | "--projectdirs" ) shift ; DIRS="$1" ;;
    "-l" | "--logdir") shift ; LOGDIR="$1" ;;
    "-t" | "--timeout") shift ; TIMEOUT="$1" ;;
    "-p" | "--parallel") shift ; PARALLEL="$1" ;;
    "-R" | "--regexp") shift ; REGEXP="$1" ;;
    "-s" | "--skip-built") SKIP_BUILT=yes ;;
    "-f" | "--skip-fallen") SKIP_FALLEN=yes ;;
    "-n" | "--chain" ) CHAIN=yes ;;
    "-m" | "--maxiter" ) shift ; MAXITER="$1" ;;
    "-w" | "--whitelist") shift 
                          [ -f $1 ] && WHITELIST=$(realpath $1) ;;
    "-T" | "--tmux" ) WITH_TMUX=yes ;;
    "-k" | "--tmux-keep-open" ) KEEP_OPEN=yes ;;
    *) notamock_vars="$notamock_vars $1";;
    esac
shift
done

# animation helper func
slash() {
case $1 in
    '===' ) echo '+==' ;;
    '+==' ) echo '=+=' ;;
    '=+=' ) echo '==+' ;;
    '==+' ) echo '===' ;;
esac
}

setup_tmux_session() {
    if [ -n "$TMUX" ]; then
        echo "Run notamocks using current tmux session: $TMUX"
        return 0
    else
        if ! tmux has-session -t notamocks 2>/dev/null; then
            tmux new-session -d -s notamocks -n "main"
            echo 'Tmux session "notamocks" created'
        else
            echo "Run notamocks using current tmux session: notamocks"
        fi
    fi
}

create_tmux_window() {
    local window_name=$1
    local command=$2
    local session='' 
    mkdir -p /tmp/notamocks/tmux
    local status_pipe="/tmp/notamocks/tmux/pipe_$window_name"
    mkfifo "$status_pipe"
    
    local wrapped_cmd="
        ($command)
        echo \$? > '$status_pipe'
        rm -f '$status_pipe'
    "
    [ "$3"_ == 'yes_' ] && wrapped_cmd="
        ($command)
        echo \$? > '$status_pipe'
        rm -f '$status_pipe'
        /bin/bash
    "
    [ -z "$TMUX" ] && session="-t notamocks"
    
    tmux new-window -d $session -n "$window_name" "$wrapped_cmd"
    
    echo "$status_pipe:$window_name"
}

wait_for_tmux_window() {
    local tracking_id=$1
    local status_pipe="${tracking_id%%:*}"
    local exit_code=1  
    if [ ! -p "$status_pipe" ]; then
        echo "Pipe file $status_pipe not found or not a pipe" >&2
        echo 1
        return 1
    fi
    if ! exit_code=$(cat "$status_pipe" 2>/dev/null); then
        echo "Failed to read from pipe $status_pipe" >&2
        echo 1
        return 1
    fi
    if ! expr "$exit_code" + 1 &>/dev/null; then
        echo "Invalid exit code: $exit_code" >&2
        echo 1
        return 1
    fi
    rm -f "$status_pipe" 2>/dev/null
    echo "$exit_code"
    return 0
}

# run notamock for project
run_notamock() {
    local tracking_id ecode
    [ "$BREAK" ] && return 1
    pushd "$1" >/dev/null
        if [ "$SKIP_BUILT" ] ; then
            if find ./RPMS/ -maxdepth 3  -name '*.rpm' 2>/dev/null |grep -Eq '\.rpm$' ; then
                echo "+++> skip: $(basename $PROJECT) - already built" |tee -a ${LOGDIR}/$NOTAMOCKSLOG
                exit
            fi
        fi
        if [ "$SKIP_FALLEN" ] ; then
            if find ./ -maxdepth 1  -name '*.buildlog'  |grep -q $(basename $PROJECT) ; then
                echo "---> skip: $(basename $PROJECT) - has bin fallen" |tee -a ${LOGDIR}/$NOTAMOCKSLOG
                exit
            fi
        fi
        date > $LOGDIR/$(basename $PROJECT).log
        [ "$CHAIN" == 'yes' ] && \
        ADDREP="PM_adds=--nogpgcheck ${PM_addrep_opt} notamocks,file://$CHAINREP"
        echo "notamock -v $ADDREP $notamock_vars" >> $LOGDIR/$(basename $PROJECT).log
        if [ "$WITH_TMUX" ] ; then
            echo "run using tmux, see log inside project dir: $1" >> $LOGDIR/$(basename $PROJECT).log
            tracking_id=$(create_tmux_window "$(basename $1)" "notamock -v \"$ADDREP\" $notamock_vars" "$KEEP_OPEN")
            ecode=$(wait_for_tmux_window "$tracking_id")
        else
            notamock "$ADDREP" $notamock_vars >> $LOGDIR/$(basename $PROJECT).log 2>&1
            ecode="$?"
        fi
        if [ -n "$ecode" ] && [ "$ecode" -eq 0 ] ; then
            echo $(basename $1) >> ${LOGDIR}/$SUCCESLOG
        else
            echo "$(basename $1): $ecode" >> ${LOGDIR}/$FAILLOG
        fi 
    popd >/dev/null
}

# check that one of pids is alive
check_pids(){
    for a in $@ ; do
        [ -e "$a" ] && checked="$checked $a"
    done
    echo "$checked"
}

# wait pids animation
waitall() {
    while [ "$(echo $RPIDS |wc -w)" -ne 0 ] ; do
    RPIDS=$(check_pids $RPIDS)
    aaa=$(slash $aaa)
    echo -e "${REWRITE}${aaa}> waiting:  $(for fproc in $RPIDS ; do
        echo -n "$(basename ${procs[$fproc]}):$(basename $fproc) "
    done)"
    sleep "$TIMEOUT"
    done
}

# update repository for chain build
mkrep() {
    while read rpm ; do
        [ "${rpm##*.}" = "rpm" ] && cp $rpm ${CHAINREP}/
    done
    $CREATE_REPO ${CHAINREP} >> ${LOGDIR}/$REPOLOG 2>&1
}

# create repo for chain build
initrep(){
    mkdir -p $CHAINREP
    $CREATE_REPO $CHAINREP >> ${LOGDIR}/$REPOLOG 2>&1
    sleep 5
}

[ "$WITH_TMUX" ] && setup_tmux_session

mkdir -p $LOGDIR
LOGDIR=$(realpath $LOGDIR)
:> ${LOGDIR}/$SUCCESLOG
:> ${LOGDIR}/$FAILLOG
:> ${LOGDIR}/$NOTAMOCKSLOG
:> ${LOGDIR}/$CHAINLOG
:> ${LOGDIR}/$REPOLOG

ftrap() {
    echo "waiting bg processes: $RPIDS"
    waitall
    BREAK=yes
    echo -e "${REWRITE}Warning: aborted!"
    rm -rf  "$CHAINREP"
    if [ -z "$KEEP_OPEN" ] && tmux has-session -t notamocks 2>/dev/null; then
        tmux kill-session -t notamocks
    fi
    rm -rf /tmp/notamocks/tmux
}

echo $notamock_vars >> ${LOGDIR}/$NOTAMOCKSLOG
trap 'ftrap' EXIT

[ "$CHAIN" == 'yes' ] && initrep
for a in $(seq $MAXITER) ; do
    for PROJECT in $DIRS; do
        echo "$PROJECT" |grep -Eq $REGEXP || continue
        if [ "$WHITELIST" ] ; then
	    grep -qw $(basename $PROJECT) "$WHITELIST" || continue
        fi
        PROJECT=$(realpath $PROJECT)
        [ "$PROJECT" == "$LOGDIR" ] && continue
        while [ "$(echo $RPIDS |wc -w)" -gt "$PARALLEL" ] ; do
            sleep $TIMEOUT
            RPIDS=$(check_pids $RPIDS)
        done
        [ "$BREAK" ] && break
        ( run_notamock "$PROJECT" ) &
        proc="/proc/$!"
        echo ${PROJECT}:$proc >> ${LOGDIR}/$NOTAMOCKSLOG
        RPIDS="$RPIDS $proc"
        procs[$proc]="$(basename $PROJECT)"
        echo -e "===> processing: ${procs[$proc]}:$(basename $proc)"
    done

    # line to be rewrited
    echo ''
    waitall
    echo -e "${REWRITE}done."

    [ "$CHAIN" != 'yes' ] && break
    for PROJECT in $DIRS; do
        if  find ${PROJECT}/RPMS/ -type f -name '*.rpm' 2>/dev/null |grep -Eq '\.rpm$' ; then 
            sed  's/ /\n/g' ${LOGDIR}/$CHAINLOG  |grep -qx "$(basename $PROJECT)" || \
            echo -n "$(basename $PROJECT) " >> ${LOGDIR}/$CHAINLOG
        fi
    done
    echo -e ${SEPARATOR}\n
    echo >> ${LOGDIR}/$CHAINLOG
    [ -z "$(cat ${LOGDIR}/$CHAINLOG |tail -n1)" ] && break
    find $DIRS -type f -name '*.rpm' | mkrep
    SKIP_BUILT='yes'
    echo $SEPARATOR >> ${LOGDIR}/$SUCCESLOG
    echo $SEPARATOR >> ${LOGDIR}/$FAILLOG
    echo $SEPARATOR >> ${LOGDIR}/$NOTAMOCKSLOG
    echo $SEPARATOR >> ${LOGDIR}/$REPOLOG
done
 
[ -d "$CHAINREP" ] && rm -rf  "$CHAINREP"
[ "$WITH_TMUX" ] && [ -z "$KEEP_OPEN" ] && tmux kill-session -t 'notamocks' 2>/dev/null
