#!/bin/bash

#########################################
# Name: qplot
#
# Summary: quick plot sample data for csv files
#
# Description:  
# Generate plot of sample data from CSV files
#
# Creates and executes a gnuplot command file
# Input: list of CSV data files (.csv)
# record format: timestamp (epoch seconds), field...
#
# Author: k. headley
#
# Copyright MBARI 2009
#
#########################################

#########################################
# Script configuration defaults
# casual users should not need to change
# anything below this section
#########################################
PLOT_HOME="."
OUTPUT="$PLOT_HOME/plot-output"
PDF="$OUTPUT/pdf"
PS="$OUTPUT/ps"
PNG="$OUTPUT/png"
OFILE_NAME="qplot_out"
#GPFILE="$PLOT_HOME/gp-`date +%s`.tmp"
GPFILE="$PLOT_HOME/gp-$$.tmp"
#GPFILE="$PLOT_HOME/gp.tmp"

ODEVICE="png"
GSDEV_PNG="png16m"
GSDEV_PDF="pdfwrite"
GS_DEV="${GSDEV_PNG}"

PS_OUT="$PS/$OFILE_NAME.ps"
PDF_OUT="$PDF/$OFILE_NAME.pdf"
PNG_OUT="$PNG/$OFILE_NAME.png"



GS_X=`which gs`
GRAPH_X=`which graph`
GNUPLOT_X="/usr/local/bin/gnuplot"
#GNUPLOT_X=`which gnuplot`
CONVERT_X=`which convert`

# check for missing dependencies
declare -a MISSING_X

let "i=0"
if [ ! "$GS_X" ]
then
	MISSING_X[i]="gs"
	let "i+=1"
fi
if  [ ! "$GRAPH_X" ]
then
	MISSING_X[i]="graph"
	let "i+=1"
fi
if  [ ! "$GNUPLOT_X" ]
then
MISSING_X[i]="gnuplot"
let "i+=1"
fi
if  [ ! "$CONVERT_X" ]
then
MISSING_X[i]="convert"
let "i+=1"
fi

# if required package missing
# output error and exit
if [ "${#MISSING_X[*]}" -gt 0 ]
then
  echo
  echo "program(s) not found [${MISSING_X[@]}]"
  echo
  exit 1
fi


GRAPH="$GRAPH_X -T ps "
GNUPLOT="$GNUPLOT_X $GPFILE"
CONVERT="$CONVERT_X"

unset DO_COMBINE
unset COMBINE_SET

replot=0
PLOT_STRING_FILE="plotString.txt"
TITLE=""
RANGE_MIN="*"
RANGE_MAX="*"
XRANGE_MIN="*"
XRANGE_MAX="*"
YTITLE=""
XTITLE="\"Timestamp (epoch sec)\""
unset ARCH_DIR
COL=2
NCOL=2
TFMT="\"%s\""
XTFMT="\"%m/%d/%y %H:%M:%S\""
DSEP=","
#DSEP_CMD="set datafile separator ${DSEP}"
POINT_TYPE=7
POINT_SIZE=0.3
GP_TERMINAL="postscript color \"Arial\" 10"

#################################
# Script variable initialization
#################################
VERBOSE="FALSE"
unset QP_DEBUG
#QP_DEBUG=""

declare -a PLOT_STRINGS
declare -a PLOT_SPECS

# Plot string holds the value of the
# plot command 
PLOT_STRING=""

ARGX=""

#################################
# Function Definitions
#################################
#################################
# name: printUsage
# description: print use message
# args: none
#################################
printUsage(){
    echo
    echo "`basename $0`: Generate plots from one or more delimited text files"
    echo 
    echo "usage: `basename $0` [options] file... "
    echo ""
	echo "Options:"
    echo "-D          : debug (keep temporary files) [$QP_DEBUG]"
    echo "-C <files>  : combine pdf files (see -o)   []"
    echo "-a <path>   : archive directory            [$ARCH_DIR]"
    echo "-j <fmt>    : output type pdf,png          [$ODEVICE]"
    echo "-o <ofile>  : output file                  [$OFILE_NAME]"
    echo "-O <dir>    : output directory             [$OUTPUT]"
    echo "-r <min Y>  : y-range min                  [$RANGE_MIN]"
    echo "-R <max Y>  : y-range max                  [$RANGE_MAX]"
    echo "-i <min X>  : x-range min                  [$XRANGE_MIN]"
    echo "-I <max X>  : x-range max                  [$XRANGE_MAX]"
    echo "-p <ptype>  : point type                   [$POINT_TYPE]"
    echo "-P <psize>  : point size                   [$POINT_SIZE]"
    echo "-T <title>  : plot title                   [$TITLE]"
	echo "-Y <title>  : Y axis title                 [$YTITLE]"
	echo "-X <title>  : X axis title                 [$XTITLE]"
	echo "-t <fmt>    : time input format            [$TFMT]"
	echo "-x <fmt>    : x axis (time) format         [$XTFMT]"
	echo "-s <sep>    : data separator               [$DSEP]"
	echo "-y <pspec>  : file,xcol,N,col-1,title-1,...col-N,title-N "
    echo "-V          : verbose output               [$VERBOSE]"
    echo "-h          : print this help message"
    echo ""
    echo "Tip: store command line options in a file"
    echo "     and pipe/redirect input:"
	echo ""
    echo "    `basename $0` < my_plot_cfg"
    echo "    or"
    echo "    cat my_plot_cfg | `basename $0`"
    echo
}

########################################
# name: vout
# description: print verbose message to stderr
# args:
#     msg: message
########################################
vout(){
    if [ "$VERBOSE" == "TRUE" ]
    then
        echo "$1" >&2
    fi
}

########################################
# name: exitError
# description: print use message to stderr
# args:
#     msg:        error message
#     returnCode: exit status to return
########################################
exitError(){
    echo >&2
    echo "$1" >&2
    exit $2
}

#################################
# name: add_plot_string
# description: generate lines for 
# the plot string file
# args: none
#################################
add_plot_string(){
	# exects comma-delimited specifier:
	# 0: file
	# 1: xcol
	# 2: x-title
	# 3: number of y fields
	# 4: y-col
	# 5: title...
	# n-1: y-col
	# n: title

	declare -a parms
	IFS=',' read -a parms <<< "$1"
	let "i=0"
	let "parm_count=${#parms[@]}-1"
	let "field_count=${parms[2]}"
	let "j=${#PLOT_STRINGS[@]}"

	while [ "${i}" -lt $field_count ]
	do
	let "idx=2*${i}+3"
	if [ "${j}" -lt 1 ]
	then
	PLOT_STRINGS[$j]="plot \"${ARCH_DIR}${parms[0]}\" using ${parms[1]}:${parms[$idx]} t ${parms[$idx+1]} pt ${POINT_TYPE} with points"
	else
	PLOT_STRINGS[$j]="\"${ARCH_DIR}${parms[0]}\" using ${parms[1]}:${parms[$idx]} t ${parms[$idx+1]} pt ${POINT_TYPE} with points"
	fi
	vout "PLOT_STRINGS[$j]=${PLOT_STRINGS[$j]}"
	let "i+=1"
	let "j+=1"	
	done
}

#################################
# name: process_plot_specs
# description: process plot specifiers
# args: none
#################################
process_plot_specs(){
	if [ "${#PLOT_SPECS[@]}" -gt 0 ]
	then
	 let "k=0"
	 while [ "${k}" -lt "${#PLOT_SPECS[@]}" ]
	 do
		vout "adding plot string $k/${#PLOT_SPECS[@]}"
		add_plot_string ${PLOT_SPECS[$k]}
		let "k+=1" 
	 done
	fi
}

#################################
# name: gen_plot_string
# description: generate plot string
# args: none
#################################
gen_plot_string(){
	# truncate plot string file
	> $PLOT_STRING_FILE
	let "i=0"
	while [ "${i}" -lt "${#PLOT_STRINGS[@]}" ]
	do
	  echo -n ${PLOT_STRINGS[$i]} >> ${PLOT_STRING_FILE}
	  let "i+=1"
	  # if still lines to do,
	  # add line ends
	  if [ "${i}" -lt "${#PLOT_STRINGS[@]}" ]
	  then
	    echo ", \\" >> ${PLOT_STRING_FILE}
	  fi	  
	done
	
	PLOT_STRING=`cat $PLOT_STRING_FILE`
	
	vout "plot string: [$PLOT_STRING]"
	if [ ! "${QP_DEBUG}" ]
	then
		rm $PLOT_STRING_FILE
	fi
}

#################################
# name: makeGP
# description: Write gnuplot command file
# args: none
#################################
makeGP(){
vout "makeGP args $* [$#]"
vout $PLOT_STRING

# Do Not Indent (here-doc)
cat > $GPFILE << EOF
# GNU Plot definition file
# [automatically generated]

# general options
#set terminal postscript color "Arial" 10
#set terminal png font "arial" size 800,600
set terminal ${GP_TERMINAL}

set title $TITLE
set output ${GP_OUTPUT}

set multiplot
set pointsize ${POINT_SIZE}  
# key font and textcolor in gnupolt > 4.4
set key right tmargin
set key spacing 1 width 1 height 1
set key noautotitle 
set key box 
set key samplen 2 
set key font "Arial,10" textcolor variable

# input options
${DSEP_CMD}

# x-axis
set xlabel ${XTITLE}
set timefmt ${TFMT}
set format x ${XTFMT}
set xtics rotate

# y-axis
set yrange [$RANGE_MIN:$RANGE_MAX]
set xdata time
set xrange [$XRANGE_MIN:$XRANGE_MAX]
set ylabel ${YTITLE}

# for debug
#show xrange
#show yrange

# plot command string
$PLOT_STRING
EOF
}

processCmdLine(){
	OPTIND=1
    vout "`basename $0` all args[$*]"
    while getopts CDa:hi:I:j:o:O:p:P:r:R:s:T:t:Vy:x:X:Y: Option
	do
		vout "`basename $0` Processing option - $Option[$OPTARG]"
		case $Option in
			D ) QP_DEBUG="Y"
			;;
			C ) DO_COMBINE="Y"
			;;
			a ) ARCH_DIR="`dirname $OPTARG`/`basename $OPTARG`/"
			;;
            j ) ODEVICE=$OPTARG
            ;;
			O ) OUTPUT=$OPTARG
			;;
			o ) OFILE_NAME=$OPTARG
			;;
			p ) POINT_TYPE=$OPTARG
			;;
			p ) POINT_SIZE=$OPTARG
			;;
			r ) RANGE_MIN=$OPTARG
			;;
			R ) RANGE_MAX=$OPTARG
			;;
			s ) DSEP=${OPTARG}
				DSEP_CMD="set datafile separator ${DSEP}"
			;;
			i ) XRANGE_MIN=$OPTARG
			;;
			I ) XRANGE_MAX=$OPTARG
			;;
			T ) TITLE=$OPTARG
			;;
			Y ) YTITLE=$OPTARG
			;;
			X ) XTITLE=$OPTARG
			;;
			t ) TFMT=$OPTARG
			;;
			x ) XTFMT=$OPTARG
			;;
			y ) PLOT_SPECS[${#PLOT_SPECS[@]}]=$OPTARG
			;;
			V ) VERBOSE="TRUE"
			;;
			h) printUsage
			  exit 0
			;;
			*) let "argopt=$OPTIND-1"
			   exitError "unrecognized option [${@:$argopt:1} ${@:$OPTIND:1}]"
			;;
		esac
	done

    # update dependencies
	PDF="$OUTPUT/pdf"
	PS="$OUTPUT/ps"
	PNG="$OUTPUT/png"
	PS_OUT="$PS/$OFILE_NAME.ps"
	PDF_OUT="$PDF/$OFILE_NAME.pdf"
	PNG_OUT="$PNG/$OFILE_NAME.png"

    if [ "${ODEVICE}" == "pdf" ]
    then
        GS_DEV=${GSDEV_PDF}
        GP_TERMINAL="postscript color \"Arial\" 10"
        GP_OUTPUT="\"$PS_OUT\""
    elif [ "${ODEVICE}" == "png" ]
    then
        GS_DEV=${GSDEV_PNG}
        GP_TERMINAL="png font \"arial\" size 800,600"
        GP_OUTPUT="\"$PNG_OUT\""
    fi

    #MAKEPDF="$GS_X -q -dNOPAUSE -dNOPROMPT -dBATCH -sDEVICE=pdfwrite"
    #COMBINE="$GS_X -q -dNOPAUSE -dNOPROMPT -dBATCH -sDEVICE=pdfwrite"
    MAKEPDF="$GS_X -q -dNOPAUSE -dNOPROMPT -dBATCH -sDEVICE=${GS_DEV}"
    COMBINE="$GS_X -q -dNOPAUSE -dNOPROMPT -dBATCH -sDEVICE=${GS_DEV}"
}

##########################
# Script main entry point
##########################

# Argument processing
# Accepts arguments from command line 
# or pipe/file redirect
# Comand line settings override config
# file settings
if [ -t 0 ]
then
	if [ "$#" -eq 0 ];then
		printUsage
		exit -1
	else
		processCmdLine $*
		let "i=$OPTIND-1"
		while [ "${i}" -gt 0  ]
		do
			shift
			let "i-=1"
		done
	fi
else	
	# save command line args
	declare -a cline_args
	let "i=1"
	while [ "$i" -le "$#" ]
	do
		cline_args[$i]="${@:$i:1}"
		let "i+=1"
	done

	# process config file 
  	declare -a arg_array
	let "z=0"
	while read line ; do
		IFS=" " read opt val <<< $line
		if [ "${opt}" ] && [[ $opt != \#* ]]
		then
		    vout "opt $opt $z"
			arg_array[$z]=${opt}
			let "z+=1"
			if [ "${val}" ]
			then
				vout "val $val $z"
				arg_array[$z]=${val}
				let "z+=1"
			fi
		else 
			vout "skipping line $line"
		fi
	done
	
	# set default delimiter to 
	# newline for parsing/setting
	# positional parameters
	# [must restore after processing]
	IFSO=$IFS
	IFS=`echo` 
	set - ${arg_array[*]}
	
	# apply config file settings
	processCmdLine ${arg_array[*]}

	# apply command line settings
	# (override config file options)
	set - ${cline_args[*]}
	processCmdLine ${cline_args[*]}

	# restore default delimiter
	IFS=$IFSO
	
	#let "i=$OPTIND-1"
	#while [ "${i}" -gt 0  ]
	#do
	#shift
	#let "i-=1"
	#done
		
fi

vout ""
vout "OPTIND     = [$OPTIND]"
vout "ARCH_DIR   = [$ARCH_DIR]"
vout "RANGE_MIN  = [$RANGE_MIN]"
vout "RANGE_MAX  = [$RANGE_MAX]"
vout "XRANGE_MIN = [$XRANGE_MIN]"
vout "XRANGE_MAX = [$XRANGE_MAX]"
vout "XTFMT      = [$XTFMT]"
vout "POINT_TYPE = [$POINT_TYPE]"
vout "POINT_SIZE = [$POINT_SIZE]"
vout "TITLE      = [$TITLE]"
vout "XTITLE     = [$XTITLE]"
vout "YTITLE     = [$YTITLE]"
vout "TFMT       = [$TFMT]"
vout "OUTPUT     = [$OUTPUT]"
vout "OFILE_NAME = [$OFILE_NAME]"
vout "ODEVICE    = [$ODEVICE]"
vout "QP_DEBUG   = [$QP_DEBUG]"
vout "VERBOSE    = [$VERBOSE]"
vout "PS_OUT     = [$PS_OUT]"
vout "PDF_OUT    = [$PDF_OUT]"
vout "ARGS       = [$*]"
vout "PLOT_SPECS = [${#PLOT_SPECS[@]}]"
vout ""


# Go to working directory
cd $PLOT_HOME

# check for invalid directory
if [ "${ARCH_DIR}" ] && [ -e "${ARCH_DIR}" ] && [ ! -d "${ARCH_DIR}" ]
then
	exitError "Invalid directory [${ARCH_DIR}]" -1
fi

# create directories if they don't exist
if [ ! -d "$PS" ]
then
    mkdir -p "$PS"
fi
if [ ! -d "$PDF" ]
then
    mkdir -p "$PDF"
fi
if [ ! -d "$PNG" ]
then
    mkdir -p "$PNG"
fi

# process plot specifiers
process_plot_specs
gen_plot_string

#################
# exit here
#################
#exit 0

# combine and exit
if [ "${DO_COMBINE}" ]
then
	processCmdLine $*
	let "i=$OPTIND-1"
	while [ "${i}" -gt 0  ]
	do
		shift
		let "i-=1"
	done
	COMBINE_SET="$*"

    vout "Combining plot files..."

    if [ "${ODEVICE}" == "pdf" ]
    then
        vout "cmd[$COMBINE -sOutputFile=$OFILE_NAME -dAutoRotatePages=/None -c \"<</Orientation 0>> setpagedevice\"  -f $COMBINE_SET]"
        $COMBINE -sOutputFile=$OFILE_NAME -dAutoRotatePages=/None -c "<</Orientation 0>> setpagedevice"  -f $COMBINE_SET
    elif [ "${ODEVICE}" == "png" ]
    then
        vout "cmd[$CONVERT $COMBINE_SET $OFILE_NAME]"
        $CONVERT $COMBINE_SET $OFILE_NAME
    fi

    exit 0
fi

# Generate gnuplot plot definition file
# use configured file paths for output
makeGP $*

# Plot data with gnuplot
vout "Creating PS plot..."
vout "GNUPLOT"
$GNUPLOT

if [ "${ODEVICE}" == "pdf" ]
then
    # Convert PS to PDF with GhostScript
    vout "Creating PDF..."
    vout "$MAKEPDF -sOutputFile=$PDF_OUT $PS_OUT"
    $MAKEPDF  -sOutputFile=$PDF_OUT -dAutoRotatePages=/None -c "<</Orientation 3>> setpagedevice" -f $PS_OUT
fi

# remove temp gnuplot file
if [ "$QP_DEBUG" == "Y" ]
then
	echo "keeping gpfile [$GPFILE]"
else
	echo "removing gpfile [$GPFILE]"
	rm $GPFILE
fi

