#!/usr/bin/env bash

# tovid-init
# Part of the tovid suite
# =======================
# Define global (suite-wide) functions and variables
# for the tovid suite. 
#
# Project homepage: http://www.tovid.org
#
#
# Copyright (C) 2005 tovid.org <http://www.tovid.org>
# 
# This program is free software; you can redistribute it and/or 
# modify it under the terms of the GNU General Public License 
# as published by the Free Software Foundation; either 
# version 2 of the License, or (at your option) any later 
# version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Or see:
#
#           http://www.gnu.org/licenses/gpl.txt


# ******************************************************************************
# ******************************************************************************
#
#
# VARIABLES
#
#
# ******************************************************************************
# ******************************************************************************

# Reset input field separator to default
#IFS=

# Exit with error on undeclared variables
#set -u

# Suite version
TOVID_VERSION="0.30"

# String used to separate blocks of output
SEPARATOR="========================================================="

TOVID_HOME="$HOME/.tovid"
TOVID_HOME_PAGE="http://www.tovid.org"
TOVID_FORUMS="http://www.createphpbb.com/tovid/"


# ******************************************************************************
# ******************************************************************************
#
#
# FUNCTIONS
#
#
# ******************************************************************************
# ******************************************************************************


# ******************************************************************************
# Return the full, absolute pathname of a given file or directory
# Echoes result to stdout, so in order to use the output as a "return value",
# call the function like this:
#     RETURN_VALUE=$(abspath "$FILENAME")
# ******************************************************************************
function abspath()
{
    DIR=$(dirname "$1")
    BASE=$(basename "$1")
    ABS_PATH="`cd \"$DIR\" && pwd || echo \"$DIR\"`/$BASE"
    #ABS_PATH=$(cd "$DIR" && pwd || echo "$DIR")/$BASE
    echo "$ABS_PATH"
}

# ******************************************************************************
# Create a unique temporary directory and return its name
# Input args: $1 = Base name (i.e., "work_dir")
# Return value is echoed to stdout (i.e., "work_dir.3")
# ******************************************************************************
function tempdir()
{
    NUM=0
    BASENAME="$1"
    TEMPDIR="$BASENAME.$NUM"
    while test -d "$BASENAME.$NUM"; do
        ((NUM++))
    done
    mkdir "$BASENAME.$NUM"
    echo "$BASENAME.$NUM"
}

# ******************************************************************************
# Verify that a variable meets certain conditions
# Usage: verify $VAR set|range "test limits"
# Input: $1 = the variable to check
#        $2 = the kind of test to perform (set|range)
#             set: test if $VAR is in the space-separated set "test limits"
#             range: test if $VAR is in the range given by "test limits"
#        $3 = the limits for the test
#
# ex: verify $CMD_LN_OPT set "y n Y N"
#     will return ":" (true) if $CMD_LN_OPT is one of "y n Y N"
#     or retern "false" if it isn't (so if $CMD_LN_OPT was "no", you'd get "false")
#
# ex: verify $CMD_LN_OPT range "0 10"
#     will return ":" (true) if 0 <= $CMD_LN_OPT <= 10
# ******************************************************************************
function verify()
{
    VERIFY_VAR="$1"
    VERIFY_TEST_TYPE="$2"
    case $VERIFY_TEST_TYPE in
    "range" )
        VERIFY_LOW=$(echo "$3" | awk '{ print $1 }')
        VERIFY_HIGH=$(echo "$3" | awk '{ print $2 }')
        if test $VERIFY_LOW -le $VERIFY_VAR && \
        test $VERIFY_HIGH -ge $VERIFY_VAR; then
            echo ":"
        else
            echo "false"
        fi 
        ;;

    "set" )
        VERIFY_SET="$3"

        if echo "$VERIFY_SET" | grep -q -w "$VERIFY_VAR"; then
            echo ":"
        else
            echo "false"
        fi
        ;;
  esac
}

# ******************************************************************************
# Print a pretty (wrapped) notice message.
# Args: $@ == text string containing the message 
# ******************************************************************************
function precho()
{
    echo -e "$@" | fold -s -w ${COLUMNS:-80}
}

# ******************************************************************************
# Print error message, then exit.
# Args: $@ == text string containing error message 
# ******************************************************************************
function exit_with_error()
{
    echo $@
    exit 1
}

# ******************************************************************************
# Take an integer number of seconds and format it as hours:minutes:seconds
# Echoes result to stdout, so in order to use the output as a "return value",
# call the function like this:
#     RETURN_VALUE=$(format_time $NUM_SECONDS)
# ******************************************************************************
function format_time()
{
    HMS_HOURS=$(expr $1 / 3600)
    HMS_MINS=$(expr \( $1 % 3600 \) / 60)
    HMS_SECS=$(expr $1 % 3600 % 60)

    test "$HMS_HOURS" -lt 10 && HMS_HOURS="0${HMS_HOURS}"
    test "$HMS_MINS" -lt 10 && HMS_MINS="0${HMS_MINS}"
    test "$HMS_SECS" -lt 10 && HMS_SECS="0${HMS_SECS}"

    echo "${HMS_HOURS}:${HMS_MINS}:${HMS_SECS}"
}

# ******************************************************************************
# Take a string containing a time (like "02:15:25.3") and
# format it as an integer number of seconds. Fractional seconds
# are truncated. Result is echoed to stdout, so to use the output
# as a "return value", call the function like this:
#     RETURN_VALUE=$(unformat_time $TIME_STRING)
# ******************************************************************************
function unformat_time()
{
    HMS_HOURS=$(awk -F ':' '{print $1}' <<< $1)
    HMS_MINS=$(awk -F ':' '{print $2}' <<< $1)
    HMS_SECS=$(awk -F ':' '{print $3}' <<< $1)
    TOT_SECONDS=$(bc <<< "($HMS_HOURS * 3600) + ($HMS_MINS * 60) + $HMS_SECS")
    echo $TOT_SECONDS
}


# ******************************************************************************
# Read a space-separated list of arguments, stopping before the next option
# beginning with '-'. After running, ARGS_ARRAY contains the resulting list.
# ******************************************************************************
function get_listargs()
{
    unset x ARGS_ARRAY
    # Hackish list-parsing
    while test $# -gt 0 && test x"${1:0:1}" != x"-"; do
        ARGS_ARRAY[x++]="$1"
        shift
    done
    # Do not skip past the next argument
    if test $# -gt 0 && test x"${1:0:1}" = x"-";then
        DO_SHIFT=false
    fi
}


# ******************************************************************************
# Display a progress meter showing MB written to a file
# Args: $1 = name of file to monitor
#       $2 = a short message to display, such as "Encoding video"
# ******************************************************************************
function file_output_progress()
{
    if $FAKE; then
        return
    fi
    FOP_OUTFILE="$1"
    FOP_BASENAME=$(basename "$FOP_OUTFILE")
    if test -n "$3"; then
        FOP_BASENAME_NAME="$3"
    else
        FOP_BASENAME_NAME="$FOP_BASENAME"
    fi
    FOP_MSG="$2"
    # A dumb little animation toggle
    FOP_FLIP=false

    printf "\n"

    # Wait for input file to appear
    # After a 30-second timeout, exit gracefully
    CUR_TIME=30
    while test $CUR_TIME -gt 0; do
        # If file exists, wait a few more seconds, then break out
        if test -e "$FOP_OUTFILE"; then
            printf "Processing started. Please wait...                                               "
            sleep 3s
            break
        fi
        printf "Waiting $CUR_TIME seconds for output file \"$FOP_BASENAME_NAME\" to appear...\r"
        sleep 1s
        CUR_TIME=$(expr $CUR_TIME - 1)
    done

    printf "\n"

    # If file does not exist, exit with a runtime error
    if test ! -e "$FOP_OUTFILE"; then
        runtime_error "Couldn't create file: \"$FOP_OUTFILE\""
    fi
    
    # File size in bytes
    FOP_LAST_SIZE=0
    FOP_CUR_SIZE=$(du -b "$FOP_OUTFILE" | awk '{print $1}')
    
    # Keep looping until outfile stops getting bigger
    while test "$FOP_CUR_SIZE" -gt "$FOP_LAST_SIZE"; do
        # Display a changing line
        if $FOP_FLIP; then
            FOP_FLIP=false
            ANIM_STR="||| "
        else
            FOP_FLIP=:
            ANIM_STR="--- "
        fi

        # Display completion status
        FOP_CUR_MB=$(expr 1 + $FOP_CUR_SIZE / 1048576)
        printf "    %s %s: %s MB written to %s        \r" \
            "$ANIM_STR" "$FOP_MSG" "$FOP_CUR_MB" "$FOP_BASENAME_NAME"

        # Doze a bit to let the file size increase
        # (SLEEP_TIME defaults to 1s if unset)
        sleep ${SLEEP_TIME-"1s"}

        FOP_LAST_SIZE=$FOP_CUR_SIZE 
        FOP_CUR_SIZE=$(du -b "$FOP_OUTFILE" | awk '{print $1}')
    done
    printf "\n\n"
}

# ******************************************************************************
# Check to see if a dependency group exists, quit if missing
# Input args: 
#    $1 = dependency group to check for; note that this can be a globally
#         defined group (below), or a space-separated list of executables
#    $2 = Descriptive message about why the user needs the dependencies
#
# Ex:   assert_dep "$dvd" "You cannot make DVDs without these!"
#       assert_dep "rice sake fish" "You cannot make dinner!"
#
# ******************************************************************************
function assert_dep()
{
    DEPS="$1"
    HELP="$2"

    # Determine if any group member is missing
    NO_GROUP=false
    for dep in $DEPS; do
        if ! type -a $dep >> /dev/null 2>&1; then
            printf "%-13s %s\n" "  $dep" "MISSING!"
            NO_GROUP=:
        fi
    done

    # Quit if any group member is missing
    if $NO_GROUP; then
        echo
        precho "$HELP $DEP_ERROR_MSG"
        exit 1
    fi
}


# ******************************************************************************
# See what filesystem a directory is in
# Input args:
#   $1 = directory to find filesystem type
# 
# Usage:
#   FS_TYPE=$(get_filesystem "$WORK_DIR")
# ******************************************************************************
function get_filesystem()
{
    DIRECTORY="$1"
    PARTITION=$(df . | tail -n 1 | awk '{print $1}')
    FILESYSTEM=$(mount | grep "$PARTITION" | awk '{print $5}')
    echo "$FILESYSTEM"
}


# ******************************************************************************
# Countdown 5 seconds, then return
# TODO: Take argument # of seconds
# ******************************************************************************
function countdown()
{
    for _CNTR in 5 4 3 2 1; do
        echo -n -e " in $_CNTR seconds...\r"
        sleep 1s
    done
}

# ******************************************************************************
# ******************************************************************************
#
#
# EXECUTED INITIALIZATION
#
#
# ******************************************************************************
# ******************************************************************************

# ******************************************************************************
# Platform-specific initialization
# Determines host platform and configures things accordingly
# ******************************************************************************
KERNEL=$(uname)
if test "$KERNEL" = "Linux"; then
    # Linux should have /proc/cpuinfo
    CPU_MODEL=$(awk -F ":" '/model name/ {print $2}' /proc/cpuinfo)
    CPU_SPEED=$(awk 'BEGIN { IGNORECASE = 1 } /MHz/ { print $4 }' /proc/cpuinfo)
    # Test for multiple CPUs. If they are available, try to use them.
    if test $(grep "^processor" /proc/cpuinfo | wc -l) -ge "2"; then
        MULTIPLE_CPUS=:
    else
        MULTIPLE_CPUS=false
    fi
elif test "$KERNEL" = "Darwin"; then
    :
fi


# ******************************************************************************
# Find multiple version kludge
# ******************************************************************************
INSTALLED_TOVIDS=$(type -a tovid 2>>/dev/null | awk '{print $NF}' | tr '\n' ' ')
NUM_INSTALLED=0

# Only count non-links
for tovid in $INSTALLED_TOVIDS; do
    if ! test -L $tovid; then
        let "NUM_INSTALLED=NUM_INSTALLED+1"
    fi
done

# Exit when there is more than one tovid installed
if test $NUM_INSTALLED -ne 1; then
    echo "Found $NUM_INSTALLED installations of tovid on your system!"
    echo "I won't run until there is only one of me :)"
    echo "Installed versions:"
    for version in $INSTALLED_TOVIDS; do
        echo "   $version"
    done
    echo "Exiting..."
    exit 1
fi


# ******************************************************************************
# tovid home setup
# ******************************************************************************

# Make home!
if ! test -d "$TOVID_HOME"; then
    mkdir "$TOVID_HOME"
fi

# Config file configuration and creation
CONFIG_FILE=$TOVID_HOME/tovid.config

if ! test -f $CONFIG_FILE; then
    CONFIG_CONTENTS=`cat << EOF
tovid
# Sample tovid configuration file
# Each line may have one or more tovid options
# This file is read EVERY time tovid runs
# DO NOT COMMENT IN LINE

# See 'man tovid' for a complete list of options

# Disc type
#-dvd
#-half-dvd
#-dvd-vcd
#-vcd
#-svcd
#-kvcd
#-ksvcd
#-kdvd
    
# TV system standard
#-pal
#-ntsc
#-ntscfilm
EOF`
    printf "$CONFIG_CONTENTS\n" > "$CONFIG_FILE"
fi

# Working directory configuration
USER_PREFS=$TOVID_HOME/preferences

# Default working/output directories
WORKING_DIR=$PWD
OUTPUT_DIR=$PWD

# If prefs file exists, read it
if test -f $USER_PREFS; then
    eval $(grep -v ^# $USER_PREFS)
# Otherwise, create a default prefs file
else
    PREFS_CONTENTS=`cat << EOF
# tovid preferences
# Configures working/output directories for tovid
#WORKING_DIR=/tmp
#OUTPUT_DIR=/video/outfiles
EOF`
    printf "$PREFS_CONTENTS\n" > "$USER_PREFS"
fi

# ******************************************************************************
# Check for run-time dependencies
# ******************************************************************************

    DEP_ERROR_MSG="Please install the above MISSING dependencies and try again. See tovid.wikia.com/wiki/Tovid_dependencies or tovid.org for help."

    # debian based distros install normalize as
    # "normalize-audio" rather than "normalize"
    NORMALIZE="normalize"
    if ! type -a normalize > /dev/null 2>&1; then
        if type -a normalize-audio > /dev/null 2>&1; then
            NORMALIZE="normalize-audio"
        fi
    fi

    # Adding (or removing) dependencies:
    # Does the dependency belong to an existing depdency group below?
    #   Yes: add the dependency to the list.
    #   No:  add another group and fill it with members.

    # *************************************************************************
    # Required Dependencies 
    # *************************************************************************
    core="grep sed md5sum mplayer mencoder mplex mpeg2enc yuvfps yuvdenoise ppmtoy4m mp2enc jpeg2yuv ffmpeg"

    # *************************************************************************
    # Optional Dependencies
    # *************************************************************************
    # Optional dependencies are grouped according to the functionality they 
    # bring to tovid: menu creation, DVD creation, (S)VCD creation, and 
    # post-processing.

    # -------------------------------------------------------------------------
    # ImageMagick components
    magick="composite convert"

    # -------------------------------------------------------------------------
    # dvdauthor compononets
    # (note: growisofs is NOT distributed with dvdauthor, but for tovid's
    # purposes, it fits in the same catagory, as it burns DVDs!)
    dvd="spumux dvdauthor growisofs"

    # -------------------------------------------------------------------------
    # vcdimager components
    # (note: cdrdao is NOT distributed with vcdimager, but for tovid's
    # purposes, it fits in the same catagory, as it burns (S)VCDs!)
    vcd="vcdxbuild cdrdao"

    # -------------------------------------------------------------------------
    # transcode components
    transcode="tcprobe tcrequant"

    # -------------------------------------------------------------------------
    # Plugin tools
    plugins="sox $NORMALIZE"

    # Quit and complain if ANY core dependency is missing.
    assert_dep "$core" "You are missing CORE tovid dependencies!"

# End tovid-init
