#!/bin/bash

## Vars ##
pullcmd='wget --timeout=5 -q -O -'

## Visual Functions ##
# These function creates a visual indicator that a step has happened.
function header () {
    if [[ "$TERM" =~ xterm* ]]; then
        tput setaf 1; tput bold; echo $@; tput sgr0; return
    else
        echo $@;
    fi
}
function errorheader () {
    if [[ "$TERM" =~ xterm* ]]; then
        tput setaf 1 1>&2; tput bold 1>&2; echo "ERROR:" $@ 1>&2; tput sgr0 1>&2; return
    else
        echo $@;
    fi
}
function infoheader() {
    if [[ "$TERM" =~ xterm* ]]; then
        tput setaf 3; tput bold; echo $@; tput sgr0; return
    else
        echo $@;
    fi
}
function colorstrip() {
    perl -e 'use strict; use warnings; while(<>) { s/\e\[[\d;]*m//g; print; }'
}

## Configuration reading ##
function param() { 
    if [ -z "$1" ] || [ ! -f "$1" ] || [ -z "$2" ]; then
        errorheader "Configuration file not specified or doesn't exist."
        exit 1;
    fi
    export "$2"="$(grep '^'"$2"'=' "$1" | sed 's#^'"$2"'='"##")"
    return
}

## Help ##
# We expect each script to declare its own helptext functions.

## Logging ##
function logstatement() {
    if [ -z "$logfile" ]; then
        errorheader "Need the logfile environment variable exported."
        exit 1
    fi
    if [ -z "$1" ]; then
        errorheader "Need a statement to log."
        exit 1
    fi
    echo "$1" | tee -a "$logfile"
    return
}

## Storage functions ##
function addfs() {
    if [ -z "$1" ] || [ -z "$2" ] || (echo "$3" | grep -v -E '^[0-9]{1,4}'); then
        echo "Need a filesystem(1), volume group path(2), and size (3)"
        return
    fi
    lvcreate -L $3"G" -n "$(echo $2 | cut -f 4 -d '/')" "$(echo $2 | cut -f 3 -d '/')"
    mkfs.ext4 -m 0 -E nodiscard "$2"
    tune2fs -e remount-ro "$2"
    tune2fs -c 1 "$2"
    mkdir "$1"
    echo "$2 $1 ext4 defaults 1 2" >> /etc/fstab
    mount "$1" &>/dev/null
}
#This function will forcibly remove a filesystem 1 and lv 2 from a VG
function forciblyremove() {
    if [ -z "$1" ] || [ -z "$2" ]; then
        echo "Need a filesystem(1) and volume group path(2)"
    elif [ -z "$(mount | grep " ""$1"" ")" ]; then
        errorheader Filesystem is not mounted -- cannot safely remove mount point.
    else
        fuser -km "$1"
        umount -l "$1"
        rm -Rf "$1"
    fi
    lvremove -f "$2"
    sed -i "/\/$(echo $2 | rev | cut -f 1 -d '/' | rev) /d" /etc/fstab
}

## Date Math Functions ##
# These move dates to be used by AT jobs. They accept an date-style argument and return the new date in format YYMMDDHHMM.
#This function returns a new date incremented by (1)seconds
function incrementdate() {
    if [ -z "$2" ]; then
        currDateS=$(date +%s)
    else
        currDateS=$(date -d "$2" +%s)
    fi
    targetDateS=$(($currDateS + $1))
    echo $(date -d '@'$targetDateS +%y%m%d%H%M)
}
#This function returns a new date decremented by (1)seconds
function decrementdate() {
    if [ -z "$2" ]; then
        currDateS=$(date +%s)
    else
        currDateS=$(date -d "$2" +%s)
    fi
    targetDateS=$(($currDateS - $1))
    echo $(date -d '@'$targetDateS +%y%m%d%H%M)
}

## QA Functions ##
# This is for QA to test sanitizing input
if [ -z "$qapadding" ]; then qapadding=40; fi
function qaunittest {
    unset actualreturn;
    if [ -z "$1" ]; then
        echo Usage: qaunittest title function expectedreturn input inputarguments
        exit 1
    fi
    header="$1"
    function="$2"
    expectedreturn="$3"
    userinput="$4"
    fnargs="$5"
    cmd="$(printf "source /opt/aninix/Uniglot/Bash/header;%s %s" "$function" "$fnargs")"
    actualreturn="`bash -c "$cmd" 2>&1 <<EOM
$(echo -ne $userinput)
EOM
`"
printf "$header";
printf ":%*s" $(($qapadding - $(echo $header | wc -c) - 1)) '['
if [ "$actualreturn" == "$expectedreturn" ]; then
    tput setaf 2; printf "PASS"; tput sgr0; printf ']\n';
else
    tput setaf 1; printf "FAIL"; tput sgr0; printf '] -- found %s (expected %s) running '\''%s %s < "%s"'\''\n' "$actualreturn" "$expectedreturn" "$function" "$fnargs" "$userinput";
fi
return
}

## Prompt functions ##
## All input should pass through these to ensure it's appropriately sanitized.
## Do not use read statements directly!
## We do not unset the variable ${!1} so that if the script is accepting command-line arguments that are invalid the user is prompted.
export maxattempts=5
# This function will prompt the user for variable 1 with string 2 until it gets a nonempty answer.
function prompt-var() {
if [ -z "$1" ] || [ -z "$2" ]; then
    echo "Need a variable name (1) and a prompt string (2)"
    return 1;
fi
inputtime="0"
#echo "$0 is checking for $1 -- currently set to <${!1}>"
while [ -z "${!1}" ] || (echo "${!1}" | grep -v -E '^[-0-9a-zA-Z ,.@]{1,50}$' &>/dev/null); do
    printf "%s: " "$2";
    read $1;
    if [ "${!1}" == "OPT" ] || [ $inputtime -gt $maxattempts ]; then exit 0; fi
    inputtime=$(($inputtime + 1))
done
return 0;
}

# This function will prompt the user for variable 1 with string 2 until it gets a nonempty answer.
function prompt-var-len() {
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
    echo "Need a variable name (1), prompt string (2), and a length (3)"
    return 1;
fi
inputtime="0"
#echo "$0 is checking for $1 -- currently set to <${!1}>"
while [ -z "${!1}" ] || (echo "${!1}" | grep -v -E "^[0-9a-zA-Z]{1,$3}$" &>/dev/null); do
    printf "%s (Max length: %s): " "$2" "$3";
    read $1;
    if [ "${!1}" == "OPT" ] || [ $inputtime -gt $maxattempts ]; then exit 0; fi
    inputtime=$(($inputtime + 1))
done
return 0;
}
# This function will prompt the user for variable 1 with string 2 until it gets a nonempty answer.
function prompt-password() {
if [ -z "$1" ] || [ -z "$2" ]; then
    echo "Need a variable name (1) and a prompt string (2)"
    return 1;
fi
inputtime="0"
#echo "$0 is checking for $1 -- currently set to <${!1}>"
while [ -z "${!1}" ] || [ $(echo ${!1} | wc -c) -gt 50 ]; do
    printf "%s: " "$2";
    read -s $1;
    if [ "${!1}" == "OPT" ] || [ $inputtime -gt $maxattempts ]; then exit 0; fi
    echo
    inputtime=$(($inputtime + 1))
done
return 0;
}
#This function will prompt the user for a variable 1 with string 2 until it gets an answer between min 3 and max 4.
function prompt-num-var() {
if [ -z "$1" ] || [ -z "$2" ] || [ "$3" -ne "$3" ] || [ "$4" -le "$3" ]; then
    echo "Need a variable name (1), prompt string (2), min (3), and max (4)";
    echo 1: $1;
    echo 2: $2;
    echo 3: $3;
    echo 4: $4;
    return 1;
fi
inputtime="0"
#echo "$0 is checking for $1 -- currently set to <${!1}>"
while [ -z "${!1}" ] || (echo "${!1}" | grep -v -E "^[0-9]{1,8}$" &>/dev/null) || [ "${!1}" -lt "$3" ] || [ "${!1}" -gt "$4" ]; do
    printf "%s (between %s and %s): " "$2" "$3" "$4";
    read $1;
    if [ "${!1}" == "OPT" ] || [ $inputtime -gt $maxattempts ]; then exit 0; fi
    inputtime=$(($inputtime + 1))
done
return 0;
}

# Confirm a user wants to do the thing.
function confirmUser() {
    infoheader WARNING: "$1"
    read -p 'Are you sure? [yes/NO]' answer
    if [ "${answer,,}" != 'yes' ]; then
        exit 1
    fi
}

## Torrenting functions ##
# Configure the torrenting engine.
function configuretorrent {
    if [ ! -f "$1" ]; then
        errorheader "Torrent configuration file not found."
        exit 1;
    fi
    param "$1" torrentengine
    param "$1" torrentterms
    param "$1" magnetposition
    param "$1" torrentdebugging
    param "$1" searchunifier
    if [ "$torrentdebugging" == "true" ]; then
        echo '<'$torrentengine'>'
        echo '<'$torrentterms'>'
        echo '<'$magnetposition'>'
        echo '<'$torrentdebugging'>'
        echo '<'$searchunifier'>'
    fi
    export torrentconfigured="true"
}
# Get a magnet link for downloads.
function getmagnetlink {
    if [ -z "$1" ]; then
        errorheader "Need search terms";
        exit 1;
    fi
    if [ "$torrentconfigured" != "true" ]; then
        errorheader Torrent not configured.
        exit 1;
    fi 
    searchstring="$(echo "$1" | sed "s/ /$searchunifier/g")"
    searchlink="$(printf "$torrentengine" "$searchstring")"
    /bin/bash -c "$pullcmd "$searchlink" &>/dev/null"
    if [ $? -ne 0 ]; then
        errorheader Could not pull search results.
        exit 1
    fi
    searchresult="$(/bin/bash -c "$pullcmd $searchlink" | bash -c "$torrentterms")"
    magnetlink="$(/bin/bash -c "$pullcmd $searchresult" | grep 'magnet:?' | cut -f "$magnetposition" -d \"| head -n 1)"
    if [ $torrentdebugging == "true" ]; then
        echo '<'$searchstring'>'
        echo '<'$searchlink'>'
        echo '<'$searchresult'>'
        echo '<'$magnetlink'>'
    fi
}

### AniNIX/Foundation help ###

function findaninixcheckouts {
    ### Find git checkouts in $pwd that are from AniNIX/Foundation
    find . -type f -name config -exec grep -l -E 'url[[:space:]]=[[:space:]]/srv/foundation|url[[:space:]]=[[:space:]]https://(foundation.)aninix.net|url[[:space:]]=[[:space:]]([a-zA-Z0-9])+@(foundation.)aninix.net' {} \; 2>/dev/null | sed 's#.git/config$##'
}

function aninixgitstatus {
    for i in `findaninixcheckouts`; do
        status="$(git -C "$i" status)"
        if [ -n "$status" ]; then
            echo "$i" has work in flight.
        fi
    done
}
