#!/bin/bash

#########################################
# Name: qplot too
#
# 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
#
#########################################

QP_SESSION_ID=$$

#########################################
# Script configuration defaults
# casual users should not need to change
# anything below this section
#########################################
QP_PLOT_HOME="."
QP_ARCH_DIR_DFL="${QP_PLOT_HOME}/plot-data"
QP_OUTPUT_DIR_DFL="${QP_PLOT_HOME}/plot-output"

declare -A QP_GS_DEV=( [png]="png16m" [pdf]="pdfwrite" )

unset QP_CONF

QP_GHOSTSCRIPT_CMD=`which gs`
QP_GNUPLOT_CMD="/usr/local/bin/gnuplot"
QP_CONVERT_CMD=`which convert`
QP_MAKEPDF_CMD="$QP_GHOSTSCRIPT_CMD -q -dNOPAUSE -dNOPROMPT -dBATCH"
QP_COMBINE_CMD="$QP_GHOSTSCRIPT_CMD -q -dNOPAUSE -dNOPROMPT -dBATCH"

QP_OFILE_NAME_DFL="qplot-${QP_SESSION_ID}"
QP_DSEP_DFL=","
QP_TFMT_DFL="%s"
QP_XTFMT_DFL="%Y-%m-%d %H:%M:%S"
QP_POINT_TYPE_DFL=7
QP_POINT_SIZE_DFL=0.3
QP_TERMINAL_DFL="postscript color \"arial\" 10"
QP_YRANGE_MIN_DFL="*"
QP_YRANGE_MAX_DFL="*"
QP_XRANGE_MIN_DFL="*"
QP_XRANGE_MAX_DFL="*"
QP_POINT_TYPE_DFL=7
QP_POINT_SIZE_DFL=0.3
QP_KEY_SIZE_DFL=9
QP_KEY_FONT_DFL="arial"
QP_TERM_SIZE_DFL=10
QP_TERM_FONT_DFL="arial"
QP_TERM_OSIZE_DFL="800,600"

declare -a QP_JOB_LIST
declare -a QP_JOB_ORDER
declare -A QP_JOB_DEFS

declare -A COMBINE_SET
declare -A QP_OUTPUT_DIR
declare -A QP_TERMINAL
declare -A QP_ARCH_DIR

declare -A QP_OFILE_NAME
declare -A QP_PTITLE
declare -A QP_XTITLE
declare -A QP_YTITLE
declare -A QP_TFMT
declare -A QP_XTFMT
declare -A QP_DSEP
declare -A QP_PLOT_SPECS
declare -A QP_YRANGE_MIN
declare -A QP_YRANGE_MAX
declare -A QP_XRANGE_MIN
declare -A QP_XRANGE_MAX
declare -A QP_KEY_FONT
declare -A QP_KEY_SIZE
declare -A QP_TERM_FONT
declare -A QP_TERM_SIZE
declare -A QP_TERM_OSIZE
declare -A QP_PLOT_STRINGS

#################################
# Script variable initialization
#################################
QP_VERBOSE="N"
QP_DEBUG="N"


#################################
# 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 "-f <path>   : config file path             [$QP_CONF]"
    echo "-j <job>    : job name (select/order jobs)"

    echo "-a <dir>    : archive directory            [$QP_ARCH_DIR_DFL]"
    echo "-o <ofile>  : output file                  [$QP_OFILE_NAME_DFL]"
    echo "-O <dir>    : output directory             [$QP_OUTPUT_DIR_DFL]"

    echo "-r <min Y>  : y-range min                  [$QP_YRANGE_MIN_DFL]"
    echo "-R <max Y>  : y-range max                  [$QP_YRANGE_MAX_DFL]"
    echo "-i <min X>  : x-range min                  [$QP_XRANGE_MIN_DFL]"
    echo "-I <max X>  : x-range max                  [$QP_XRANGE_MAX_DFL]"
    echo "-p <ptype>  : point type                   [$QP_POINT_TYPE_DFL]"
    echo "-P <psize>  : point size                   [$QP_POINT_SIZE_DFL]"
	echo "-t <fmt>    : time input format            [$QP_TFMT_DFL]"
	echo "-x <fmt>    : x axis (time) format         [$QP_XTFMT_DFL]"
	echo "-s <sep>    : data separator               [$QP_DSEP_DFL]"

    echo "-D          : debug (keep temporary files) [$QP_DEBUG]"
    echo "-V          : verbose output               [$QP_VERBOSE]"
    echo "-h          : print this help message"
    echo ""
    echo "Examples:"
	echo ""
    echo "    `basename $0` -DVf /path/to/my_plot_cfg"
    echo ""
    echo
}

########################################
# name: vout
# description: print verbose message to stderr
# args:
#     msg: message
########################################
vout(){
    if [ "$QP_VERBOSE" == "TRUE" ] || [ "$QP_VERBOSE" == "Y" ] || [ "$QP_VERBOSE" == "y" ]
    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: qp_make_plotstring
# description: generate plot string
# args: key
#################################
qp_make_plotstring(){
    key="${1}"
    arch_dir=${QP_ARCH_DIR[$key]:-$QP_ARCH_DIR_DFL}
    pt_type=${QP_POINT_TYPE[$key]:-$QP_POINT_TYPE_DFL}

    declare -a plotspecs
    OFS=$IFS
    IFS="+" read -a plotspecs <<< "${QP_PLOT_SPECS[$key]}"
    let "last_spec=${#plotspecs[@]}-1"
    let "nspec=0"

    for plotspec in ${plotspecs[@]}
    do
        declare -a spec
        IFS="," read -a spec <<< "${plotspec}"
        # plot specifier format (comma delimited):
        # 0: file
        # 1: xcol
        # 2: number of y fields
        # 3: y-col
        # 4: title...
        # n-1: y-col
        # n: title

        let "ptr=0"
        let "spec_fields=${spec[2]}"
        let "last_field=${spec_fields}-1"
        while [ "${ptr}" -lt $spec_fields ]
        do
            let "idx=2*${ptr}+3"
            QP_PLOT_STRINGS[$key]="${QP_PLOT_STRINGS[$key]}\"${arch_dir}/${spec[0]}\" using ${spec[1]}:${spec[$idx]} t \"${spec[$idx+1]}\" pt ${pt_type} with points"
            QP_PLOT_STRINGS[$key]+=$', \\\n'
            let "ptr+=1"
        done
        let "nspec+=1"
    done
    # prepend with plot command
    QP_PLOT_STRINGS[$key]="plot ${QP_PLOT_STRINGS[$key]}"
    # replace last continuation with newline
    let "x=${#QP_PLOT_STRINGS[$key]}-4"
    QP_PLOT_STRINGS[$key]="${QP_PLOT_STRINGS[$key]:0:$x}"
    QP_PLOT_STRINGS[$key]+=$'\n'

}

#################################
# name: qp_make_gpfile
# description: Write gnuplot command file
# args: key, odevice, gpfile, plot_file
#################################
qp_make_gpfile(){

    key="${1}"
    odevice="${2}"
    gpfile="${3}"
    plot_output="\"${4}\""

    vout "qp_make_gpfile - key    :[$key]"
    vout "qp_make_gpfile - odevice:[$odevice]"
    vout "qp_make_gpfile - gpfile :[$gpfile]"
    key_font=${QP_KEY_FONT[$key]:-"$QP_KEY_FONT_DFL"}
    key_size=${QP_KEY_SIZE[$key]:-"$QP_KEY_SIZE_DFL"}
    dsep_cmd="set datafile separator \"${QP_DSEP[$key]:-$QP_DSEP_DFL}\""

    case $odevice in
        png)
        qp_term="png font \"${QP_TERM_FONT[$key]:-$QP_TERM_FONT_DFL}\" size ${QP_TERM_OSIZE[$key]:-$QP_TERM_OSIZE_DFL}"
        ;;
        ps)
        qp_term="postscript color \"${QP_TERM_FONT[$key]:-$QP_TERM_FONT_DFL}\" ${QP_TERM_SIZE[$key]:-$QP_TERM_SIZE_DFL}"
        ;;
        pdf)
        qp_term="postscript color \"${QP_TERM_FONT[$key]:-$QP_TERM_FONT_DFL}\" ${QP_TERM_SIZE[$key]:-$QP_TERM_SIZE_DFL}"
        ;;
        *) qp_term="${QP_TERMINAL_DFL}"
        ;;
    esac
    yrange_min=${QP_YRANGE_MIN[$key]:-$QP_YRANGE_MIN_DFL}
    yrange_max=${QP_YRANGE_MAX[$key]:-$QP_YRANGE_MAX_DFL}
    xrange_min=${QP_XRANGE_MIN[$key]:-$QP_XRANGE_MIN_DFL}
    xrange_max=${QP_XRANGE_MAX[$key]:-$QP_XRANGE_MAX_DFL}
    point_size=${QP_POINT_SIZE[$key]:-$QP_POINT_SIZE_DFL}
    ptitle="\"${QP_PTITLE[$key]}\""
    xlabel="\"${QP_XTITLE[$key]}\""
    ylabel="\"${QP_YTITLE[$key]}\""
    tfmt="\"${QP_TFMT[$key]:-$QP_TFMT_DFL}\""
    xtfmt="\"${QP_XTFMT[$key]:-$QP_XTFMT_DFL}\""

##########################
# Do Not Indent (here-doc)
##########################

cat > $gpfile << EOF
# GNU Plot definition file
# [automatically generated]

# output terminal
set terminal ${qp_term}
# plot title
set title ${ptitle}
# plot file
set output ${plot_output}
# plot style (enable multiple plots)
set multiplot
# line point size
set pointsize $point_size
# key options
set key right tmargin horizontal
set key spacing 1 width 0 height 0 maxcols 2
set key noautotitle
set key box
set key samplen 2
# key font and textcolor in gnuplot ver > 4.4
set key font "$key_font,$key_size" textcolor variable

# input options
# data separator
${dsep_cmd}

# x-axis
set xlabel ${xlabel}
set timefmt ${tfmt}
set format x ${tfmt}
set xtics rotate

# y-axis
set yrange [$yrange_min:$yrange_max]
set xdata time
set xrange [$xrange_min:$xrange_max]
set ylabel ${ylabel}

# for debug
#show xrange
#show yrange

# plot command string
${QP_PLOT_STRINGS[$key]}
EOF
}

#################################
# name: qp_make_gpfile
# description: Write gnuplot command file
# args: none
#################################
qp_run_tasks(){

    vout "running tasks"

    if [ "${#QP_JOB_LIST[@]}" -gt 0 ]
    then
        # use job list, if specified on command line,
        job_keys=${QP_JOB_LIST[@]}
    else
        # other wise, use config file or default
        # NOTE: default order is not necessarily the same
        # as order specified in config file
        job_keys=${QP_JOB_ORDER[@]:-${!QP_JOB_DEFS[@]}}
    fi

    # iterate over job keys
    for key in ${job_keys[*]}
    do

    # lookup next job
    job=${QP_JOB_DEFS[$key]}

    # get job type
    type=`qp_parse_tok $job "," 0`

    if [ "$type" == "plot" ]
    then
        odev=`qp_parse_tok ${job} "," 1`
        plots=`qp_parse_tok $job "," 2`
        declare -a keys
        IFS="+" read -a keys <<< "${plots}"
        for key in "${keys[@]}"
        do
            odir="${QP_OUTPUT_DIR[$key]:-$QP_OUTPUT_DIR_DFL}"

            if [ ! -d odir ]
            then
                mkdir -p ${odir}
            fi

            gpfile="${odir}/${QP_OFILE_NAME[$key]}-$QP_SESSION_ID.gp"
            vout "running plot task: [$key/$odev -> $gpfile]"
            # do plot task...
            qp_make_plotstring $key
            plot_file="${odir}/${QP_OFILE_NAME[$key]}.$odev"

            qp_make_gpfile $key $odev $gpfile $plot_file

            # Plot data with gnuplot
            vout "Generating plot..."
            $QP_GNUPLOT_CMD $gpfile

            if [ "${odev}" == "pdf" ]
            then
                plot_pdf="${odir}/${QP_OFILE_NAME[$key]}.pdf}"
                # Convert PS to PDF with GhostScript
                vout "Creating PDF..."
                vout "${QP_MAKEPDF_CMD} -sDEVICE=${QP_GS_DEV[$odev]}  -sOutputFile=${plot_pdf} -dAutoRotatePages=/None -c \"<</Orientation 3>> setpagedevice\" -f $plot_file"
                $QP_MAKEPDF_CMD  -sDEVICE=${QP_GS_DEV[$odev]}  -sOutputFile=$plot_pdf -dAutoRotatePages=/None -c "<</Orientation 3>> setpagedevice" -f $plot_file
            fi

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

        done
    elif [ "${type}" == "combine" ]
    then
        odev=`qp_parse_tok ${job} "," 1`
        ofile="${odir}/`qp_parse_tok ${job} "," 2`"
        list=`qp_parse_tok ${job} "," 3`
        declare -a combine_set
        IFS=";" read -a combine_set <<< "${list}"
        cset=`echo ${combine_set[@]}`
        if [ "${odev}" == "pdf" ]
        then
            vout "cmd[$QP_COMBINE_CMD -sDEVICE=${QP_GS_DEV[$odev]} -sOutputFile=${ofile} -dAutoRotatePages=/None -c \"<</Orientation 0>> setpagedevice\"  -f ${cset}]"
           $QP_COMBINE_CMD -sDEVICE=${QP_GS_DEV[$odev]} -sOutputFile=${ofile} -dAutoRotatePages=/None -c "<</Orientation 0>> setpagedevice"  -f ${cset}
        elif [ "${odev}" == "png" ]
        then
            vout "cmd[${QP_CONVERT_CMD} ${cset} ${ofile}]"
            ${QP_CONVERT_CMD} ${cset} ${ofile}
        fi
    fi
    done
}

qp_parse_tok(){
    # $1: string to parse
    # $2: delimiter
    # $3: index
     declare -a tokens
    IFS="$2" read -a tokens <<< "${1}"
    echo "${tokens[$3]}"
}

qp_dump_config(){
    key=${1:-" "}
    vout ""
    vout "QP_YRANGE_MIN = [${QP_YRANGE_MIN[$key]:-$QP_YRANGE_MIN_DFL}]"
    vout "QP_YRANGE_MAX = [${QP_YRANGE_MAX[$key]:-$QP_YRANGE_MAX_DFL}]"
    vout "QP_XRANGE_MIN = [${QP_XRANGE_MIN[$key]:-$QP_XRANGE_MIN_DFL}]"
    vout "QP_XRANGE_MAX = [${QP_XRANGE_MAX[$key]:-$QP_XRANGE_MAX_DFL}]"
    vout "QP_TFMT       = [${QP_TFMT[$key]:-$QP_TFMT_DFL}]"
    vout "QP_XTFMT      = [${QP_XTFMT[$key]:-$QP_XTFMT_DFL}]"
    vout "QP_POINT_TYPE = [${QP_POINT_TYPE[$key]:-$QP_POINT_TYPE_DFL}]"
    vout "QP_POINT_SIZE = [${QP_POINT_SIZE[$key]:-$QP_POINT_SIZE_DFL}]"
    vout "QP_PTITLE     = [${QP_PTITLE[$key]:-'-'}]"
    vout "QP_XTITLE     = [${QP_XTITLE[$key]:-'-'}]"
    vout "QP_YTITLE     = [${QP_YTITLE[$key]:-'-'}]"
    vout "QP_PLOT_SPECS = [${QP_PLOT_SPECS[$key]}]"
    vout "QP_OFILE_NAME = [${QP_OFILE_NAME[$key]:-$QP_OFILE_NAME_DFL}]"

    vout "QP_ARCH_DIR   = [${QP_ARCH_DIR[$key]:-$QP_ARCH_DIR_DFL}]"
    vout "QP_OUTPUT_DIR = [${QP_OUTPUT_DIR[$key]:-$QP_OUTPUT_DIR_DFL}]"
    vout "QP_DEBUG      = [$QP_DEBUG]"
    vout "QP_VERBOSE    = [$QP_VERBOSE]"


    for job in "${QP_JOB_DEFS[@]}"
    do
        vout "job:[${job}]"
    done

#    vout "ARGS          = [$*]"
#    vout "OPTIND        = [$OPTIND]"
    vout ""

}

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

qp_check_missing(){
    # check for missing dependencies
    declare -a MISSING_X

    if [ ! "$QP_GHOSTSCRIPT_CMD" ]
    then
    MISSING_X[${#MISSING_X[@]}]="gs"
    fi

    if  [ ! "$QP_GNUPLOT_CMD" ]
    then
    MISSING_X[${#MISSING_X[@]}]="gnuplot"
    fi

    if  [ ! "$QP_CONVERT_CMD" ]
    then
    MISSING_X[${#MISSING_X[@]}]="convert"
    fi

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

processCmdLine(){
	OPTIND=1
    vout "`basename $0` all args[$*]"
while getopts a:f:hi:I:j:o:O:p:P:r:R:s:t:x:DV Option
	do
		vout "`basename $0` Processing option - $Option[$OPTARG]"
		case $Option in
            f ) QP_CONF=$OPTARG
            ;;
            j )
                if [ "${PROCESS_PASS}" -eq 0 ]
                then
                    declare -a jlist
                    IFS="," read -a jlist <<< "${OPTARG}"
                    for jspec in ${jlist[@]}
                    do
                        QP_JOB_LIST[${#QP_JOB_LIST[@]}]=${jspec}
                    done
                fi
            ;;
			a ) QP_ARCH_DIR_DFL="`dirname $OPTARG`/`basename $OPTARG`"
			;;
			O ) QP_OUTPUT_DIR_DFL=$OPTARG
			;;
			o ) QP_OFILE_NAME_DFL=$OPTARG
			;;
			p ) QP_POINT_TYPE_DFL=$OPTARG
			;;
			P ) QP_POINT_SIZE_DFL=$OPTARG
			;;
			r ) QP_YRANGE_MIN_DFL=$OPTARG
			;;
			R ) QP_YRANGE_MAX_DFL=$OPTARG
			;;
			s ) QP_DSEP_DFL=${OPTARG}
			;;
			i ) QP_XRANGE_MIN_DFL=$OPTARG
			;;
			I ) QP_XRANGE_MAX_DFL=$OPTARG
			;;
			t ) QP_TFMT_DFL=$OPTARG
			;;
			x ) QP_XTFMT_DFL=$OPTARG
			;;
            D ) QP_DEBUG="Y"
            ;;
			V ) QP_VERBOSE="Y"
			;;
			h) printUsage
			  exit 0
			;;
			*) let "argopt=$OPTIND-1"
			   exitError "unrecognized option [${@:$argopt:1} ${@:$OPTIND:1}]"
			;;
		esac
	done

    let "PROCESS_PASS+=1"
}

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

if [ "$#" -eq 0 ]
then
printUsage
exit -1
fi

# check for missing programs
qp_check_missing

# process args (read cmd line)
let "PROCESS_PASS=0"
processCmdLine $*

# if config file, source
if [ "${QP_CONF}" ] && [ -f "${QP_CONF}" ]
then
source ${QP_CONF}
fi

# set XFHOME (use config override, if any)
XFHOME=${XFHOME:-"${HOME}/xfoce"}
if [ -f ${XFHOME}/src/scripts/xfenv ]
then
. ${XFHOME}/src/scripts/xfenv
else
exitError "Can't find XFHOME[$XFHOME]" 1
fi

# apply cmd line settings (override config file)
processCmdLine $*

# check options
qp_validate_opts


# run the jobs
qp_run_tasks

exit 0
