#!/bin/bash
#
# Helper program used by Rosegarden to preview and print Lilypond output.
# Copyright 2004-2008 Chris Cannam and Fervent Software Ltd.
# Copyright 2006-2008 Heikki Junes.
# Distributed under the GNU General Public License.

tmpdir=/tmp/$$_lilypondview
mkdir "$tmpdir" || exit 1

trap "rm -rf \"$tmpdir\"" 0

# Requirements, actual or potential:
# 
# lilypond (that actually runs, of course)
# mktemp OR tempfile
# kpdf OR kghostview OR gpdf OR xpdf OR evince OR acroread
# kdialog [for graphical mode only]
# kprinter OR gtklp

prog_lilypond=""
prog_temp_file=""
prog_kdialog=""
prog_pdf_view=""
prog_printer=""

set eval -- `getopt -n$0 --longoptions="graphical,pdf,print,version,conftest" "gpPv" "$@"`

input_files=""
pdf_view=""
printing=""
graphical=""
while [ $# -gt 0 ]
do
    # Parse options
    case "$1" in
        -g|--graphical) graphical=true ;;
        -p|--print) printing=true ;;
        -P|--pdf) pdf_view=true ;;
        -v|--version) print_version=true ;;
           --conftest) conftest=true ;;
    esac

    # Check and list the listed LilyPond input files
    # getopt adds quotation marks ('): "'input.ly'"
    if [ "`expr match $1 '.*.ly'`" == "`expr length $1 - 1`" -o \
	 "`expr match $1 '.*.ly.gz'`" == "`expr length $1 - 1`" ]; then
        input="${1:1:`expr length "$1" - 2`}"
        if [ ! -f "$input" ]; then
	    echo "Error: Can't open \"$input\" for reading" 1>&2
	    exit 1
        fi
	input_files="$input_files
$input"
    fi
    shift
done

#echo "input files: $input_files"

if [ -n "$print_version" ]; then
    echo "rosegarden-lilypondview v1.6" 1>&2
    exit 0
fi

if [ -x "`type -path lilypond`" ]; then
    lilypond_version="`lilypond --version | grep LilyPond | head -1 | sed 's/^.* //'`"
    case "$lilypond_version" in
	1.*|2.[0-5].*)
	    echo "rosegarden-lilypondview: Unsupported LilyPond version ($lilypond_version)" 1>&2
	    echo "Required: LilyPond v2.6 or newer";;
	*)
	    prog_lilypond=lilypond;;
    esac
else
    echo "rosegarden-lilypondview: LilyPond unavailable" 1>&2
    echo "Required: LilyPond"
fi

reqd=

for x in mktemp tempfile; do
    if [ -x "`type -path $x`" ]; then
	prog_temp_file=$x
	break;
    fi
done

if [ -z "$prog_temp_file" ]; then
    reqd="mktemp OR tempfile, $reqd"
    echo "rosegarden-lilypondview: No temporary file utility found" 1>&2
fi

if [ -x "`type -path kdialog`" ]; then
    prog_kdialog=kdialog
fi

for x in acroread kpdf kghostview gpdf xpdf evince; do
    if [ -x "`type -path $x`" ]; then
	prog_pdf_view=$x
	break;
    fi
done

if [ -z "$prog_pdf_view" ]; then
    reqd="kpdf OR kghostview OR gpdf OR xpdf OR evince OR acroread, $reqd"
    echo "rosegarden-lilypondview: No PDF viewer found" 1>&2
fi

for x in kprinter gtklp; do
    if [ -x "`type -path $x`" ]; then
	case "$x" in kprinter) x="$x --stdin";; esac
	prog_printer=$x
	break;
    fi
done

if [ -z "$prog_printer" ]; then
    reqd="kprinter OR gtklp, $reqd"
    echo "rosegarden-lilypondview: No printing program found" 1>&2
fi

if [ -n "$conftest" ]; then
    if [ -n "$reqd" ]; then
	echo "Required: "${reqd%%, }
    fi
fi

[ -z "$prog_lilypond" ] && exit 1
[ -z "$prog_pdf_view" ] && exit 1
[ -z "$prog_temp_file" ] && exit 1

if [ -n "$conftest" ]; then
    echo "LilyPond version: $lilypond_version"
    exit 0
fi

if [ -z "$prog_kdialog" ]; then
    # can't do graphical mode
    echo "rosegarden-lilypondview: Graphical progress dialog requires kdialog utility" 1>&2
    graphical=""
fi

if [ -z "$input" ]; then
cat <<End-of-Usage-message
Process LilyPond files to PDF files and print/view them.

Usage: rosegarden-lilypondview [OPTION] file.ly ...

Options:
  -g, --graphical : show Lilypond progress in a graphical dialog
  -P, --pdf       : view created PDF files (DEFAULT)
  -p, --print     : print created PDF files; together with '--pdf', viewing starts first
  -v, --version   : print version of rosegarden-lilypondview
      --conftest  : check that all dependencies are present

example: rosegarden-lilypondview -g file.ly

End-of-Usage-message
    exit 2
fi

for x in $input_files; do
    case "$x" in
	*.ly|*.ly.gz)
        input="$x"
        cp "$input" "$tmpdir/" || exit 1

(
	dir=`dirname "$input"`
	base=`basename "$input"`
	cd "$tmpdir"

	if [ -n "$graphical" ]; then
	    dcopRef=`kdialog --title "Processing" --icon "rosegarden" --progressbar "Processing LilyPond file..." 100`
	fi

	send_dcop() {
	    if [ -n "$graphical" ]; then
		dcop $dcopRef "$@"
	    fi
	}

	trap "send_dcop close" 0

	send_dcop showCancelButton true

	if file "$base" | grep -q gzip; then
		gunzip "$base"
		base=`basename "$base" .gz`
	fi

	includes=`\
		grep '\\include ' "$base" | \
		sed -e 's/\\include//' \
		    -e 's/^.*[^a-zA-Z0-9-]\([a-zA-Z0-9-][a-zA-Z0-9-]*.ly\).*$/\1/'`

	for include in $includes; do
		if [ -r "$dir/$include" ]; then
			cp "$dir/$include" .
		elif [ -r "$dir/$include.gz" ]; then
			gunzip -c "$dir/$include" > ./"$include"
		fi
	done

	fileversion=`grep '\\version ' "$base" | head -1 | \
	    sed -e 's/\\version //' -e 's/[^0-9.]//g'`

	args=""
	convert=""
echo "LilyPond version is $lilypond_version, file version is $fileversion"
	case "$lilypond_version" in
	    2.6.*)
		args="--format=pdf"
		case "$fileversion" in
		    1.*|2.[0-5].*) convert=true;;
		esac;;
	    2.8.*)
		args="--format=pdf"
		case "$fileversion" in
		    1.*|2.[0-7].*) convert=true;;
		esac;;
	    2.10.*)
		args="--format=pdf"
		case "$fileversion" in
		    1.*|2.[0-9].*) convert=true;;
		esac;;
	esac

	logfile="lilypond-output.log"
	cat </dev/null >"$logfile"

	if [ -n "$convert" ]; then
	    echo "File version is $fileversion against LilyPond version $lilypond_version -- converting..." 1>&2
	    send_dcop setLabel "Updating LilyPond file..."
	    for srcfile in "$base" $includes; do
	        if [ ! -f "$srcfile" ]; then continue; fi
		case "$fileversion" in
		    1.*|2.[012345].*)
		    	grep -v override-auto-beam-setting "$srcfile" > "${srcfile}_tmp"
			mv "${srcfile}_tmp" "$srcfile"
			;;
		esac
		if [ -n "$graphical" ]; then
		    convert-ly "$srcfile" > "${srcfile}_converted" 2> "$logfile" && mv "${srcfile}_converted" "$srcfile"
		else
		    convert-ly "$srcfile" > "${srcfile}_converted" && mv "${srcfile}_converted" "$srcfile"
		fi
	    done
	    send_dcop setLabel "Processing LilyPond file..."
	fi

	if [ -n "$graphical" ] ; then
	    # special bar comment syntax RG exports -- hopefully benign if absent
	    bars=`grep '^%% [0-9][0-9]*$' "$base" | sort -k 1 -n | tail -1 | awk '{ print $2; }'`
	
	    if [ -z "$bars" ]; then
		staffs=`grep 'Staff *[=<]' "$base" | wc -l`
		[ "$staffs" -eq 0 ] && staffs=1
		bars=`grep -v '^ *%' "$base" | wc -l`
		bars=$(($bars / $staffs))
	    fi
		
	    bars=$(($bars + 5))
	    count=$(($bars * 7 / 3))
	
	    indev=/dev/pts/0
	    if [ ! -c "$indev" ]; then indev=/dev/ptya0; fi

	    PROGRESS=`$prog_temp_file`

#	    echo "Running $prog_lilypond $args \"$base\""
	    $prog_lilypond $args "$base" <$indev 2>&1 | tee -a "$logfile" | \
	      perl -e '
		$| = 1;
		print "0\n";
		$state = 0;
		$n = "";
		$base = 0;
		while (defined ($key = getc)) {
		    if ($key eq "[") {          # bar number start mark
			$state = 1;
	            } elsif ($key eq "]") {     # bar number end mark
			$state = 2;
			$val = int( ($base + $n) * 100 / '$count' );
			if ($val > 100) { $val = 100; }
			print " $val";
			print "\n";
			$n = "";
		    } elsif ($key eq "\n") {
			if ($state == 2) {
			    $base = $base + '$bars'; $state = 0;
			}
		    } elsif ($state == 1) {     # bar number
			$n = $n . $key;
	            }
		}
                print "end\n"
                ' >& $PROGRESS &

	    PROCESSINGSTAGE=0
            #
	    # Stages:
            #  0 -- Process LilyPond file
            #  1 -- Create PDF output
            #  2 -- Finished
            #
	    until [ "$PROCESSINGSTAGE" != "0" ]; do
		sleep 0.2
		PROGRESSVALUE=`tail -c 4 $PROGRESS`
		## Debugging code:
		# cat $PROGRESS
		# echo "= $PROGRESSVALUE =="
		if [ "$PROGRESSVALUE" == "end" ]; then
                    #
		    # Processing was terminated:
                    #  - either the number of bars was not known,
                    #  - or there appeared an error during processing.
                    #
                    send_dcop setProgress 100
                    PROCESSINGSTAGE=2
		elif [ "$PROGRESSVALUE" == "100" ]; then
                    PROCESSINGSTAGE=1
		    #
		    # Note: percentage is 100 only after PDF has been created.
		    #
                    send_dcop setProgress 99
	            send_dcop setLabel "Creating PDF output..."
	        else
                    send_dcop setProgress $PROGRESSVALUE
		fi
	        if [ "true" == `send_dcop wasCancelled` ]; then
	            send_dcop close
	            rm $PROGRESS
                    exit 1;
                fi
	    done
	    until [ "$PROCESSINGSTAGE" == "2" ]; do
	        sleep 0.2
	        if [ "true" == `send_dcop wasCancelled` ]; then
	            send_dcop close
	            rm $PROGRESS
                    exit 1;
                fi
		PROGRESSVALUE=`tail -c 4 $PROGRESS`
		if [ "$PROGRESSVALUE" == "end" ]; then
                    PROCESSINGSTAGE=2
                    send_dcop setProgress 100
                fi
            done
	    ( sleep 2 ; send_dcop close ) &
	    rm $PROGRESS
	else
#	    echo "running $prog_lilypond $args \"$base\"..."
	    $prog_lilypond $args "$base"
	fi

	target="${base%.*}.pdf"

	if [ -f "$target" ]; then
	    if [ -z "$printing" ]; then
	        $prog_pdf_view "$target"
            else
                if [ -n "$pdf_view" ]; then
	            $prog_pdf_view "$target"
                fi
	        if [ -n "$printing" ]; then
	            $prog_printer < "$target"
	        fi
            fi
	    exit 0
	elif [ -n "$graphical" ]; then
	    cat $logfile 1>&2
	    echo 1>&2
	    echo "LilyPond failed" 1>&2
	    LOGGINGFILE=`$prog_temp_file`
	    if [ -n "$graphical" ]; then
		( echo 
		  echo "  ERROR: LilyPond processing failed."
		  echo "  LilyPond output follows:"
		  echo 
		  cat "$logfile" ) > $LOGGINGFILE
               $prog_kdialog --textbox $LOGGINGFILE 600 200
	    fi
	    rm $LOGGINGFILE
	else
	    echo 1>&2
	    echo "LilyPond processing failed." 1>&2
	fi
	exit 1
)
    ;;
    *) ;;
    esac
done;
