#!/bin/bash

# This is the terminal front end program. It communicates with the autopackage tools through a named pipe, and
# is responsible for displaying output, and getting input. The reason for this system is so you can 
# have multiple front ends, for instance not just a tty fe, but also a KDE/GNOME frontend, and a "bot"
# fe, ie automatic

###
#
# This code is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Copyright 2002 Mike Hearn (mike@theoretic.com)
#
###


# for protocol details see main/doc/protocol

# supported protocol versions
ttyfe_protocol_versions="1"

# keeps track of when it's time to quit
session_depth=1;

# get the named pipe as $1
pipe="$1"

[ -e /etc/autopackage ] && source /etc/autopackage;
locked_global="$autopackage_deny_install"
locked_user="$autopackage_deny_user_install"
[ -e ~/.autopackage ] && source ~/.autopackage;

source "$autopackage_prefix/share/autopackage/apkg-funclib" # needed for formatting commands

############################################################################################

# prints the indents to indicate dependancy depth
function indent() {
    local i
    local j
    if [[ "$1" == "" ]]; then j="$session_depth"; else j="$1"; fi
    (( j-- ))
    if [[ "$j" == "0" ]]; then return 0; fi;
    for i in `seq 1 $j`; do echo -n "#"; done;
    echo -n " ";
}

# Execute a command, and kill it if it doesn't exit in time
function _timeout()
{
    set -m
    ( sleep $1 ) &
    local pid1=`jobs -l | tail -n 1 | awk '{print $2}'`

    ( set -m; trap "kill %1 2>/dev/null" EXIT; eval "$2"; kill "$pid1" > /dev/null ) &
    local pid2=`jobs -l | tail -n 1 | awk '/ Running / {print $2}'`

    set +m
    wait $pid1
    kill $pid2 2>/dev/null
}

function timeout()
{       
    local cmd="$2"
    if echo "$cmd" | grep '&$' >/dev/null; then
        cmd=`echo "$cmd" | sed 's/&$//'`
        ( trap "" SIGHUP; _timeout "$1" "$cmd" ) 2>/dev/null &
    else
        _timeout "$1" "$cmd" 2>/dev/null
    fi
}

function checkProtocolVersions() {
    if ! echo "$ttyfe_protocol_versions" | grep "`echo \"$1\" | head -1| sed 's/HELLO //'`" >/dev/null; then
	echo; red; outn "$intl_FAIL "; normal; out "$intl_PROTOCOL_MISMATCH" "$1"; normal;
	timeout 3 "echo \"UHOH VERSION_MISMATCH\" > \"$pipe\" &";
	exit 1;
    fi
}

function debug() {
    # uncomment the line below to switch on debug output
    # indent; echo $@
    return 0; # bash isn't happy about functions with no statements
}

function queueStr() {
    local binding=`echo "$data" | head -n 1`
    local tmp="queue_${current_group}_bindings";
    
    if [[ "${!tmp}" != "" ]]; then # avoid adding a trailing space when only one binding...
    	export queue_${current_group}_bindings="${!tmp} $binding"
    else
	export queue_${current_group}_bindings="$binding";
    fi;
    export queue_${current_group}_${binding}_type="$1";
    export queue_${current_group}_${binding}_default="`echo "$data" | tail +2 | head -n 1`";
    export queue_${current_group}_${binding}_message="`echo "$data" | tail +3`"
    
    if [[ "$bindings" != "" ]]; then
	export bindings="$bindings
$binding:$current_group"
    else
    	export bindings="$binding:$current_group";
    fi
    		 
    unset binding
}

function processQueue() {
    local cur_group="";
    for bl in $bindings; do
      local b;
      local g;
      b=`echo "$bl" | sed 's/:.*//'`
      g=`echo "$bl" | sed 's/.*://'`
      d=`echo "$groups" | grep "$g" | sed 's/.*://'`
      if [[ "$cur_group" != "$g" ]]; then
	cur_group="$g";
      fi

      local message="queue_${g}_${b}_message"
      local default="queue_${g}_${b}_default"
      local type="queue_${g}_${b}_type"

      if [[ "${!type}" == "string" ]]; then
	read -e -p "`indent`${!message} (${!default}): ";
	if [[ "$REPLY" != "" ]]; then
	    export bindings_${g}_${b}="$REPLY"
	else # use default
	    local tmp2="queue_${g}_${b}_default"
	    export bindings_${g}_${b}="${!tmp2}"
	fi
      fi
      
      if [[ "${!type}" == "path" ]]; then
	read -e -p "`indent`${!message} (${!default}): ";
	if [[ "$REPLY" != "" ]]; then
	    local tmp2="$REPLY"
		    
	    # this expands ~ into the home dir....
	    if echo "$tmp2" | grep '~' >/dev/null; then
		tmp2=`echo "$tmp2" | sed "s|~|$HOME|"`;
	    fi
	    
	    export bindings_${g}_${b}="$tmp2"
	else # use default
	    local tmp2="queue_${g}_${b}_default"
	    local tmp2="${!tmp2}"
	    # this expands ~ into the home dir....
	    if echo "$tmp2" | grep '~' >/dev/null; then
		tmp2=`echo "$tmp2" | sed "s|~|$HOME|"`;
	    fi
	    export bindings_${g}_${b}="$tmp2"
	fi
      fi
    done
    
    return 1;
}

# FIXME: this doesn't work for some reason. The backend never gets the echo :(
function abort() {
    echo "ABORT" > "$pipe"
    echo;
    red; out "$intl_ABORTED"; normal;
    exit 1;
}


############################################################################################

hello=`cat "$pipe"`
if ! echo "$hello" | grep "^HELLO " >/dev/null; then
    echo; red; outn "$intl_FAIL "; normal; out "$intl_PROTOCOL_FAILURE: $hello"; normal;
    exit 1;
fi

debug -n Checking protocol versions... 
checkProtocolVersions "$hello"
debug negotiation went OK

# get name from HELLO
cur_name=`echo "$hello" | head -n 2 | tail -n 1`
if ! echo "$cur_name" | grep "^HELLO" >/dev/null; then drawHeaderCyan "$cur_name"; fi

# check for global lock
if [[ "$lock_global" == "true" ]]; then
    echo; red; outn "$intl_FAIL "; normal; out "$intl_ACCESS_DENIED"; normal;
    echo "UHOH ACCESS_DENIED" > "$pipe";
    exit 1;
fi

# check for user lock
if [[ `id -u` != "0" ]] && [[ "$lock_user" == "true"  ]]; then
    echo; red; outn "$intl_FAIL "; normal; out "$intl_ACCESS_DENIED"; normal;
    echo "UHOH ACCESS_DENIED" > "$pipe";
    exit 1;
fi

# otherwise, everything seems ok - so LET'S GO!

echo "LETS GO" > "$pipe";

old_name=""
cur_name=""
current_group="default"
# a list of group:description strings
groups="$current_group:Options";
bindings="";


#trap abort SIGINT SIGTERM

while true; do # start a quick infinite loop ;)
    input=`cat "$pipe"`
    command=`echo "$input" | head -n 1`
    data=`echo "$input" | tail +2`
    debug "got command $command"
    if echo "$command" | grep '^HELLO' >/dev/null; then
	# a new package has joined us. welcome!
	debug "package joined session"
	checkProtocolVersions "$command"
	cur_name="$data"
	(( session_depth++ ))
	debug "session depth is now $session_depth"
	if [[ "$old_name" != "$cur_name" ]]; then
	    drawHeaderCyan "`indent`$cur_name";
	    old_name="$cur_name"
	fi
	echo "LETS GO" > "$pipe"
	continue;
    fi
    
    case "$command" in
	"STATUS")   normal; # make normal text
		    indent; out "$data";
		    ;;
        "TEST")     indent; normal; outn "$intl_APKG_TTYFE_TESTING" "$data";
		    ;;
	"FAILTEST") bold; red;
		    out "$intl_FAILED";
		    normal;
		    ;;
	"PASSTEST") green; out "$intl_PASSED"; normal;
		    ;;
	"RECOMMEND") blue;
		     out "$intl_APKG_TTYFE_RECOMMENDED";
		     normal;
		     ;;
        "TERMINATE") debug "package left session";
     		     (( session_depth-- ));
		     debug "session depth is now $session_depth"
		     if [[ "$session_depth" == "0" ]]; then
		       if [[ "$aborted" != "true" ]]; then
		           out "$intl_APKG_TTYFE_COMPLETED"
		       else
		           out "$intl_APKG_TTYFE_ABORTED"
		       fi
		       echo "OK" >"$pipe" # send confirmation
		       exit 0;
		     fi
		;;
	"FAIL") if (( `echo "$data" | wc -l | sed 's/^ *//'` > 1 )); then
	          indent; red; outn "$intl_FAIL "; normal; echo; echo "$data" | while read; do indent; out "$REPLY";  done;
		else
		  indent; red; outn "$intl_FAIL "; normal; out "$data";
		fi  
		aborted=true
		;;
	"INTERACT STRING") queueStr string;
			 ;;
	"INTERACT PATH")  queueStr path;		
			 ;;

	"SWITCH GROUP") new_group=`echo "$data" | head -n 2 | sed '2d'`;
			if ! echo "$groups" | grep "$new_group" >/dev/null; then
			    new_group_description=`echo "$data" | sed '1d'`;
			    groups="$groups
$new_group:$new_group_description"
			fi
		        current_group="$new_group"
		        ;;
			
        "WAIT") processQueue;;
	
	"GET")  #echo "getting $data in group $current_group";
		tmp="bindings_${current_group}_${data}"
		echo ${!tmp} > "$pipe";
		unset tmp; continue;
	       ;;

	*) echo "WTF? We didn't understand $command - abort!"; exit 1;
    esac
    echo "OK" > "$pipe" #&
done
