#!/usr/bin/perl

## reads ascii data separated by white space and writes netcdf files

use NetCDF ;
#no encoding;

package Ascii2nc ;

sub new { return bless {} ; }

# read CDL to get variables attributes
sub readCDL {

my $self = shift;
my( $cdlfile ) = @_ ;
my( @vars, %dim, $parts, $len ) ;

die "Missing cdlfile $cdlfile: $!\n" unless  -e $cdlfile ;
open( CDL, "$cdlfile" ) || die "could not open $cdlfile: $!\n" ;
# read variables dimensions
while( <CDL> ) {
	last if( /variables/ ) ;
	if( s#^\s*(\w{1,25})\s*=\s*UNLIMITED## ) {
		$unlimited = $1 ;
	} elsif( s#^\s*(\w{1,25})\s*=\s*(\d{1,6})## ) {
		$dim{ $1 } = $2 ;
	}
}
while( <CDL> ) {
	if( s#^\s*(char|int|long|double|float) (\w{1,25})## ) {
		$parts = "$1 $2" ;
		$len = 1 ;
		s#$unlimited\s*## ;
		while( s#,\s*(\w{1,25})\s*## ) {
			$len *= $dim{ $1 } ;
		}
		push( @vars, "$parts $len" ) ;
	}
}
close CDL ;
return( @vars ) ;
} # end readCDL

# Use ncgen to create NetCDF file
sub ncgen {

my $self = shift;
my( $cdlfile, $ncfile ) = @_ ;
my( $ncgen, $ncid, $recNum_id, $name_id, $recnum ) ;

# look for ncgen
if( -e "util/ncgen" ) {
	$ncgen = "util/ncgen" ;
} elsif( -e "/usr/local/ldm/util/ncgen" ) {
	$ncgen = "/usr/local/ldm/util/ncgen" ;
} elsif( -e "/upc/netcdf/bin/ncgen" ) {
	$ncgen = "/upc/netcdf/bin/ncgen" ;
} elsif( -e "./ncgen" ) {
	$ncgen = "./ncgen" ;
} else {
	open( NCGEN, "which ncgen |" ) ;
	$ncgen = <NCGEN> ;
	close( NCGEN ) ;

	if( $ncgen =~ /no ncgen/ ) {
		die "Can't find NetCDF utility 'ncgen' in PATH, util/ncgen
/usr/local/ldm/util/ncgen, /upc/netcdf/bin/ncgen, or ./ncgen : $!\n" ;
	} else {
		$ncgen = "ncgen" ;
	}
}
# open or create ncfiles
if( -e $ncfile ) {
	$ncid = NetCDF::open( "$ncfile", NetCDF::WRITE ) ;
	$recNum_id = NetCDF::dimid( $ncid, "recNum" ) ;
	$name_id =  "xxxxxxxx"  ;
	$recnum =  -1  ;
	# get current value of recnum
	NetCDF::diminq( $ncid, $recNum_id, $name_id, $recnum ) ;
} else {
	die "Wrong or missing cdlfile parameter: $!\n" unless  -e $cdlfile ;
	system( "$ncgen -o $ncfile $cdlfile" ) ;
	$ncid = NetCDF::open( "$ncfile", NetCDF::WRITE ) ;
	# NetCDF record counter
	$recnum = 0 ;
}
return $ncid, $recnum ;
} # end ncgen

# read ascii and write netcdf files.
package main ;

use File::Path ;

# set interrupt handler
$SIG{ 'INT' }  = 'atexit' ;
$SIG{ 'KILL' }  = 'atexit' ;
$SIG{ 'TERM' }  = 'atexit' ;
$SIG{ 'QUIT' }  = 'atexit' ;
$SIG{ 'HUP' }  = 'atexit' ;

# process command line switches
while ($_ = $ARGV[0], /^-/) {
	 shift;
       last if /^--$/;
	     /^(-d)/ && ( $delim = shift ) ; # input data delimiter
	     /^(-l)/ && $logging++ ; # turn logging on
	     /^(-r)/ && ( $recLen = shift ) ; # number lines of input/record
}
# process input parameters
if( $#ARGV == 1 ) {
	$cdlfile = $ARGV[ 0 ] ;
	$ncfile = $ARGV[ 1 ] ;
} elsif( $#ARGV == 1 ) {
	$cdlfile = $ARGV[ 0 ] ;
	$ncfile = $ARGV[ 1 ] ;
	$datadir = $ARGV[ 2 ] ;
} else {
	die "Usage with input on STDIN: ascii2nc [-d inputDelim][-l]",
		"[-r #] cdlfile ncfile [datadir]\n";
}
# the directory 
$datadir = "." unless( $datadir ) ;
mkpath( $datadir, 0, 0775 ) unless( -d $datadir ) ;
chdir( $datadir ) unless( $datadir eq "." ) ;

# set delimiter
$delim = "[ \t\n]+" unless( $delim ) ;

if( $logging ) {
	# redirect STDOUT and STDERR
	open( STDOUT, ">$datadir/asciiLog.$$.log" ) ||
		die "could not open $datadir/asciiLog.$$.log: $!\n" ;
	open( STDERR, ">&STDOUT" ) ||
		die "could not dup stdout: $!\n" ;
	select( STDERR ) ; $| = 1 ;
	select( STDOUT ) ; $| = 1 ;
}
# number lines of input/record
$recLen = 1 unless( $recLen ) ;

# set error handling to verbose only
$result = NetCDF::opts( VERBOSE ) ;

$a2nc = new Ascii2nc() ;
( @vars ) = $a2nc->readCDL( $cdlfile ) ;
$totalVars = 0 ;
for( $i = 0; $i <= $#vars; $i++ ) {
	( $type, $name, $len ) = split( / /, $vars[ $i ] ) ;
	push( @varLen, $len ) ;
	if( $type =~ /char|byte/ ) {
		$totalVars += 1 ;
	} else {
		$totalVars += $len ;
	}
}
$totalVars-- ;
( $ncid, $recnum ) = $a2nc->ncgen( $cdlfile, $ncfile ) ;

# main loop   set select processing here from STDIN
while( 1 ) {
	open( STDIN, '-' ) ;
	vec( $rin, fileno( STDIN ), 1 ) = 1 ;
	$timeout = 1200 ; # 20 minutes
	$nfound = select( $rout = $rin, undef, undef, $timeout ) ;
	# timed out
	if( ! $nfound ) {
		print STDOUT "Shut down, time out 20 minutes\n" 
			if( $logging ) ;
		atexit() ;
	}
	atexit( "eof" ) if( eof( STDIN ) ) ;

	# Process ascii data from STDIN
	undef( @values ) ;
	for( $i = 0; $i < $recLen; $i++ ) {
		chomp( $_ = <STDIN> ) ;
		s#^\s+## ; # strip leading white space
		( @temp ) = split( /$delim/, $_ ) ;
		push( @values, @temp ) ;
	} 
	if( $totalVars != $#values ) { # not enough data
		print "Number of data mismatched for NetCDF variables.\n" ;
		print "@values\n" ;
		next ;
	}
	# convert data to correct type and enter into dataref array
	$j = 0 ;
	for( $i = 0; $i <= $#vars; $i++ ) {
		if( $vars[ $i ] =~ /^(char|byte)/ ) {
			$values[ $j ] = 
				padstr( $values[ $j ], $varLen[ $i ]  ) ;
			$dataref[ $i ] = \$values[ $j ] ;
			$j++ ;
		} elsif( $varLen[ $i ] == 1 ) {
			if( $vars[ $i ] =~ /^(long|int)/ ) {
				$values[ $j ] *= 1 ;
			} else { #  /^(float|double)/
				$values[ $j ] *= 1.0 ;
			}
			$dataref[ $i ] = \$values[ $j ] ;
			$j++ ;
		} else {
			my( @array ) ;
			for( $k = 0; $k < $varLen[ $i ] ; $k++, $j++ ) {
				if( $vars[ $i ] =~ /^(long|int)/ ) {
					$values[ $j ] *= 1 ;
				} else { #  /^(float|double)/
					$values[ $j ] *= 1.0 ;
				}
				push( @array, $values[ $j ] ) ;
			}
			$dataref[ $i ] = \@array ;
		}
	}
	# enter data into NetCDF file
	$result = NetCDF::recput( $ncid, $recnum, [ @dataref ] );
	if( $result ) { # failure
		print "NetCDF::recput result = $result\n" ;
		print "@values\n" ;
	} else {
		$status = NetCDF::sync( $ncid ) ;
		#print "Syncing $ncfile with ncid $ncid\n" ;
		$recnum++ ;
	}
	atexit( "eof" ) if( eof( STDIN ) ) ;
} # end while( 1 )
atexit( "eof" ) ;
exit( 0 ) ; # should never get here

# pad str with nulls to correct length
sub padstr
{
( $str, $len ) = @_ ;

my( $size, $i ) ;

$size = length( $str ) ;
for( $i = $size; $i < $len; $i++ ) {
        $str .= "\0" ;
	#print "$str,\n" ;
}
if( $size > $len ) {
	print STDOUT "String length is over $len chars long:\n $str\n" ;
	$str = substr( $str, 0, $len ) ;
}
return $str ;
} # end padstr

# execute at exit
sub atexit
{
my( $sig ) = @_ ;

if( $sig eq "eof" ) {
	print STDOUT "eof on STDIN --shutting down\n" ;
} elsif( defined( $sig )) {
	print STDOUT "Caught SIG$sig --shutting down\n" ;
}
NetCDF::close( $ncid ) ;

exit( 0 ) ;
}
