#!/usr/bin/perl
# Author: Jacob Laas
# Last updated: 2016-04-13
#
#* USAGE:
#* if Gnuplot is not found automatically, you must manually edit $gnuplot_path to point to the executable
#* <Return> invokes the "Plot" button
#* <Shift>+<Return> sends whatever is in the "To GP" box to the underlying gnuplot program
#* <Up> increases the temperature by 10 and re-plots
#* <Down> decreases the temperature by 10 and re-plots
#* <CTRL>+<KP_Up> expands the frequency window by 10%
#* <CTRL>+<KP_Down> shrinks the frequency window by 10%
#* <CTRL>+<KP_Left> shifts the frequency window to the left by 25%
#* <CTRL>+<KP_Right> shifts the frequency window to the right by 25%
#* 
#* REQUIREMENTS:
#* - a gnuplot executable that is callable from the command-line
#*    - this script searches the most common locations in linux, windows, and mac (via MacPorts)
#* - perl
#* - the perl/tk package
#*    - in Windows via ActivePerl, use Perl Package Manager
#*    - in Debian/Ubuntu based Linux distros, just do "sudo apt-get install perk-tk"
#*    - in MacPorts, just do "sudo port install p5_tk"
#* - JPL/CDMS spectral catalogs
#*    - this script will actually copy/convert the catalog to a *.tsv form and use that
#* - (optional) any spectral data that's also in space-delimited form (.igor, .tsv, etc..)
#* - (optional) the perl package "Tie::File" if the program determines that you are
#*   trying to use a CSV file of any kind
#*    - named "Tie-File" in ActivePerl
#*    - typically a built-in module in Linux/MacPorts
#* 
#* NOTES:
#* - blue lines are observed (negative tag ID); red lines are unobserved
#* - temperature scaling does not work properly above 300 K (relative intensities are
#*   still correct, but you cannot compare them to lower temperatures)
#* - the partition function coefficients come from fits to:
#*        Q(T) = 10^(a0 + a1*(log10(T))^1 + a2*(log10(T))^2 + a3*(log10(T))^3)
#*    - see "Sauval & Tatum, 1984, ApJ Supp Series, v56, pp:193-209" for more info..
#*    - there exists a script "gnuplotfitpart.sh" for fitting coefficients using gnuplot.
#*      In principle, you can use any program that provides fitting to custom function.
#*    - without partition function coefficients, the scaling (T0/T)^(3/2+1) from 300K to
#*      9.375K is 5,7926. MOST partition functions for organic molecules do not scale by this
#*      (more like 60-500); thus relative intensities may be off by a factor of 10-100.. which
#*      is a LOT. If this is important to you, you would be wise to manually fit the catalog's
#*      partition function to a curve and then submit coefficients to this program.
#*    - you can pass the list of coefficients as command-line arguments (commas are ignored)
#*    - the following coefficients have been fit by Jake:
#*        mol\coeffs           a0, a1, a2, a3                 rel err of Q(300)/Q(9.375)
#*        CH3OH:   -1.42196, 3.75111, -1.24064, 0.244687      3.3%
#*        CH3O (o):2.50959, -2.21054, 2.16057, -0.394663      7.2%
#*        CH3O (n): 2.31648, -1.9851, 2.06608, -0.380373      ?
#*        CH2OH:   -0.615257, 2.20386, -0.136575, -0.053957   17.4%
#*        N2H+:    -0.058526, 2.37427, -0.86094, 0.169503     ?
#* - intensities are all relative; use the catalog scaling factor to scale up or down if
#*   need be (the format is somewhat flexible, such as 1000 or 1e3)
#* - output is only capable of PNG files (unless you know how to change the code to suit
#*   your needs...)
#* - for reference, the general call to gnuplot (once the pipe is set) is:
#*       $gnuplot->print( <INSERTYOURGNUPLOTCOMMANDHERE> );
#* - the "To GP" button sends the contents of the test box directly to gnuplot, and then replots
#* - if the program throws an error like
#*       |gnuplot> plot '...
#*       |                  ^
#*       |         line 0: all points y value undefined!
#*       |
#*   then either:
#*    a) there are no lines in that spectral window (check the frequencies &/or catalog)
#*             or
#*    b) your catalog file is messed up and not being read correctly
#* - if the program throws an error like
#*       |gnuplot> set term wxt
#*       |                  ^
#*       |         unknown or ambiguous terminal type; type just 'set terminal' for a list
#*   then send "To GP" something like: set term x11
#*
#* LICENSE:
#* The MIT License (MIT)
#* 
#* Copyright (c) 2015 Jacob Laas
#* 
#* Permission is hereby granted, free of charge, to any person obtaining a copy
#* of this software and associated documentation files (the "Software"), to deal
#* in the Software without restriction, including without limitation the rights
#* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#* copies of the Software, and to permit persons to whom the Software is
#* furnished to do so, subject to the following conditions:
#* 
#* The above copyright notice and this permission notice shall be included in
#* all copies or substantial portions of the Software.
#* 
#* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#* THE SOFTWARE.
#


#use warnings;
use strict; # to minimize bugs
use Tk; # for the GUI
use Tk::LabEntry; # for the GUI's textboxes
use FileHandle; # for easily forwarding to the gnuplot pipe
use File::stat; # for checking file modification timestamps

# INITIAL PARAMETERS (edit this if you're doing repeat work and/or are lazy)
my $catfile		= "";		# blank
my $specfile		= "";		# blank   
my $freq1			= "50000";	# 50000
my $freq2			= "500000";	# 500000
my ($ticmaj, $ticmin); 		# blank
my $temperature	= "300";	# 300
my $usepartfun		= "0";		# 0
my ($partConst0,$partConst1,$partConst2,$partConst3);	# blank
my $catscale		= "1";		# 1
my $ymax			= "";		# blank
my $ymin			= "";		# blank
my ($xdecimals,$mdecimals);	# blank
my $GPinput		= "";		# blank
my $linewidth		= "1.5";	# 1.5

# parses the commandline argument for help
if (! defined $ARGV[0]) {
	print "try --help if you have questions\n";
}
if (defined $ARGV[0] && $ARGV[0] =~ /-*help/) {
	print usage();
	exit;
}
# parses the commandline argument for an input file
if (defined $ARGV[0] && -e $ARGV[0]) {
	$catfile = shift @ARGV;
}
# uses command line arguments as partition function coefficients if possible
foreach (@ARGV) {
	s/,//;
}
if (isFloat($ARGV[0]) && isFloat($ARGV[1]) && isFloat($ARGV[2]) && isFloat($ARGV[3])) {
	print "found 4 floating-point values as command-line arguments and will use them for the partition function...\n";
	$partConst0 = $ARGV[0];
	$partConst1 = $ARGV[1];
	$partConst2 = $ARGV[2];
	$partConst3 = $ARGV[3];
	$usepartfun = "1";
}

# ignore this.. used for the lg(VAR) subroutine below
my $log10 = 2.30258509299405;

# sets default gnuplot terminal (wxt for linux, x11 for mac) and keybindings
my $terminal = "wxt";
my ($KPLEFT,$KPRIGHT,$KPUP,$KPDOWN);
print "the OS is: $^O\n";
if ($^O eq "darwin") {
	$terminal = "x11";
} elsif ($^O eq "MSWin32") {
	$terminal = "windows";
	$KPLEFT = '<Control-Left>';
	$KPRIGHT = '<Control-Right>';
	$KPUP = '<Control-Up>';
	$KPDOWN = '<Control-Down>';
} else {
	$KPLEFT = '<Control-KP_Left>';
	$KPRIGHT = '<Control-KP_Right>';
	$KPUP = '<Control-KP_Up>';
	$KPDOWN = '<Control-KP_Down>';
}

# this section looks for the path to your gnuplot executable
### IF THIS THROWS AN ERROR EVEN AFTER INSTALLING GNUPLOT, ADD YOUR UNUSUAL PATH ####
my $gnuplot_path = "";
if (-e "/usr/local/bin/gnuplot") {
	$gnuplot_path = "/usr/local/bin/gnuplot";
	print "found gnuplot executable at \"/usr/local/bin/gnuplot\"\n";
} elsif (-e "/opt/local/bin/gnuplot") {
	$gnuplot_path = "/opt/local/bin/gnuplot";
	print "found gnuplot executable at \"/opt/local/bin/gnuplot\"\n";
} elsif (-e "/usr/bin/gnuplot") {
	$gnuplot_path = "/usr/bin/gnuplot";
	print "found gnuplot executable at \"/usr/bin/gnuplot\"\n";
} elsif (-e "C:\\Program Files (x86)\\gnuplot\\bin\\gnuplot.exe") {
	$gnuplot_path = "C:\\Program Files (x86)\\gnuplot\\bin\\gnuplot.exe";
	print "found gnuplot executable at \"C:\\Program Files (x86)\\gnuplot\\bin\\gnuplot.exe\"\n";
} elsif (-e "C:\\Program Files\\gnuplot\\bin\\gnuplot.exe") {
	$gnuplot_path = "C:\\Program Files\\gnuplot\\bin\\gnuplot.exe";
	print "found gnuplot executable at \"C:\\Program Files\\gnuplot\\bin\\gnuplot.exe\"\n";
} else {
	print "WARNING: COULD NOT LOCATE THE GNUPLOT EXECUTABLE.\n";
	print "PLEASE DOWNLOAD & INSTALL IT FROM http://sourceforge.net/projects/gnuplot/files/\n";
	exit 1;
}

my $winMain = new MainWindow;
$winMain->title( 'Tk-plotlinecat' );

# for keeping track of the help window later...
my $winHelp;

# textboxes
my $catframe = $winMain->Frame()->pack();
$catframe->LabEntry(
	-label			=> 'Cat File:',
	-textvariable	=> \$catfile,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 35,
)->pack( -side => 'left' );
$catframe->Button(
	-text			=> 'browse',
	-command		=> \&catfile,
)->pack();

my $specframe = $winMain->Frame()->pack();
$specframe->LabEntry(
	-label			=> 'Spec File:',
	-textvariable	=> \$specfile,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 34,
)->pack( -side => 'left' );
$specframe->Button( # $specbutton
	-text			=> 'browse',
	-command		=> \&specfile,
)->pack();

my $freqframe = $winMain->Frame()->pack();
$freqframe->LabEntry(
	-label			=> 'Freq Range (MHz):',
	-textvariable	=> \$freq1,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 10,
)->pack( -side => 'left' );
$freqframe->LabEntry(
	-textvariable	=> \$freq2,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 10,
)->pack();

my $ticframe = $winMain->Frame()->pack();
$ticframe->LabEntry(
	-label			=> 'X-tics (major/minor spacing):',
	-textvariable	=> \$ticmaj,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 8,
)->pack( -side => 'left' );
$ticframe->LabEntry(
	-textvariable	=> \$ticmin,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 8,
)->pack();

my $decframe = $winMain->Frame()->pack();
$decframe->LabEntry(
	-label			=> '# of decimals (axis, mouse):',
	-textvariable	=> \$xdecimals,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 3,
)->pack( -side => 'left' );
$decframe->LabEntry(
	-textvariable	=> \$mdecimals,
	-labelPack		=> [ -side => 'right' ],
	-width			=> 3,
)->pack();

my $tempframe = $winMain->Frame()->pack();
$tempframe->LabEntry(
	-label			=> 'Temperature (K):',
	-textvariable	=> \$temperature,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 5,
)->pack( -side => 'left' );
$tempframe->Label(
	-text			=> 'or <Up>/<Down>',
)->pack();

my $partframe = $winMain->Frame()->pack();
$partframe->Checkbutton(
	-text			=> 'Use partition function?',
	-variable		=> \$usepartfun,
)->pack(-side => 'right');
$partframe->LabEntry(
	-label			=> 'a0:',
	-textvariable	=> \$partConst0,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 8,
)->pack();
$partframe->LabEntry(
	-label			=> 'a1:',
	-textvariable	=> \$partConst1,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 8,
)->pack();
$partframe->LabEntry(
	-label			=> 'a2:',
	-textvariable	=> \$partConst2,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 8,
)->pack();
$partframe->LabEntry(
	-label			=> 'a3:',
	-textvariable	=> \$partConst3,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 8,
)->pack();

$winMain->LabEntry(
	-label			=> 'Catalog Scale:',
	-textvariable	=> \$catscale,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 5,
)->pack();

my $autoscaleframe = $winMain->Frame()->pack();
$autoscaleframe->LabEntry(
	-label			=> 'Y-axis min/max:',
	-textvariable	=> \$ymin,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 10,
)->pack(-side => 'left');
$autoscaleframe->LabEntry(
	-textvariable	=> \$ymax,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 10,
)->pack();

my $toGPframe = $winMain->Frame()->pack();
my $GPbutton = $toGPframe->Button( # $toGPbutton
	-text			=> 'To GP',
	-command		=> \&toGP,
)->pack( -side => 'left' );
$toGPframe->LabEntry(
	-label			=> '',
	-textvariable	=> \$GPinput,
	-labelPack		=> [ -side => 'left' ],
	-width			=> 30,
)->pack();

# buttons
my $butframe = $winMain->Frame->pack( -anchor => 's' );
my $plotbutton = $butframe->Button(
	-text			=> 'Plot <Return>',
	-command		=> \&plot,
)->pack( -side => 'left' );
$butframe->Button( # $savebutton
	-text			=> 'Export to Image',
	-command		=> \&save,
)->pack( -side => 'left' );
$butframe->Button( # $helpbutton
	-text			=> 'Help',
	-command		=> \&doHelpWindow,
)->pack( -side => 'left' );
$butframe->Button( # $quitbutton
	-text			=> 'Quit',
	-command		=> \&quit,
)->pack( -side => 'left' );
#$butframe->Button( # $testbutton
#	-text			=> 'Test',
#	-command		=> \&pipetest,
#)->pack( -side => 'left' );

$winMain->protocol( 'WM_DELETE_WINDOW', \&quit );

# sets the pipe to gnuplot
my $gnuplot = new FileHandle( "| $gnuplot_path" );
$gnuplot->autoflush( 1 );

# sets terminal type dynamically as per the identification of gnuplot executable
print "setting terminal to $terminal\n";
$gnuplot->print( "set terminal $terminal\n" );

# sets up the color scheme for observed/unobserved lines (runs once per session)
$gnuplot->print( "isobserved(x) = (x > 0) ? 0 : 1\n" );
$gnuplot->print( "set palette defined (0 1 0 0, 1 0 0 1) # (red, blue)\n" );
$gnuplot->print( "set cbrange [0:1]\n" );
$gnuplot->print( "unset colorbox\n" );

# sets the impulse style & thickness/width
$gnuplot->print( "set style line 10\n" );

# spectra are plotted with line style 2.. set specific attributes here
$gnuplot->print( "set style line 2 lt -1\n" ); # black

# sets axis labels
$gnuplot->print( "set xlabel \"Frequency (MHz)\"\n" );
$gnuplot->print( "set ylabel \"Intensity (arb.)\"\n" );

# sets keybindings
$winMain->bind( "<Escape>", sub { exit 1 } ); # quick exit
$winMain->bind( "<Return>", sub { $plotbutton->invoke if $temperature > 0 } ); # re-plots
$winMain->bind( "<Shift-Return>", sub { $GPbutton->invoke} ); # sends the command box to the underlying gnuplot system
$winMain->bind('<Down>', # decreases the temperature by 10 K
	sub {
		if ($temperature > 9) { # to make sure you don't drop below zero!!
			$temperature = $temperature - 10;
			$plotbutton->invoke if $temperature > 0;
		} else {
			$winMain->bell; # I believe this doesn't actually do anything...
		}
	});
$winMain->bind('<Up>', # increases the temp by 10 K
	sub {
		$temperature = $temperature + 10;
		$plotbutton->invoke;
	} );
$winMain->bind($KPLEFT, # shifts the frequency window by 25% to the left
	sub {
		my $range = $freq2 - $freq1;
		my $shift = $range * 0.25;
		$freq1 = int($freq1 - $shift);
		$freq2 = int($freq2 - $shift);
		$plotbutton->invoke;
	} );
$winMain->bind($KPRIGHT, # shifts the frequency window by 25% to the right
	sub {
		my $range = $freq2 - $freq1;
		my $shift = $range * 0.25;
		$freq1 = int($freq1 + $shift);
		$freq2 = int($freq2 + $shift);
		$plotbutton->invoke;
	} );
$winMain->bind($KPDOWN, # shrinks the frequency window by 10%
	sub {
		my $range = $freq2 - $freq1;
		my $shift = $range * 0.05;
		$freq1 = int($freq1 + $shift);
		$freq2 = int($freq2 - $shift);
		$plotbutton->invoke;
	} );
$winMain->bind($KPUP, # enlarges the frequency window by 10%
	sub {
		my $range = $freq2 - $freq1;
		my $shift = $range * 0.05;
		$freq1 = int($freq1 - $shift);
		$freq2 = int($freq2 + $shift);
		$plotbutton->invoke;
	} );
#$winMain->bind('<Any-KeyPress>', # enlarges the frequency window by 10% to the right
#	sub {
#	 print "Any Key Press \n";
#	    my($keyc) = @_;
#	    my $e = $keyc->XEvent;
#	    my( $x, $y, $W, $K, $A ) = ( $e->x, $e->y, $e->K, $e->W, $e->A );
#	
#	    print "  x coor  = $x\n";
#	    print "  y coor  = $y\n";
#	    print "  KeyCode = $W\n";
#	    print "  ASCII   = $A\n";
#	    print "  Window  = $K\n";
#	} );

# runs GUI
MainLoop();

# subroutines for helper functions
sub usage {
	my $scriptname = $0;
	my @usage;
	push (@usage,"you seem lost...here are the notes from the header of this file:\n");
	open (SCRIPT,$scriptname) or die;
	while (<SCRIPT>) {
		if (/^\#\*/) {
			#s/\#\*//g;		# for cleaning up the #* characters.. looks worse without them
#			print;
			push (@usage,$_);
		}
	}
	return @usage;
}

sub loadText($){
	my( $txtWid ) = @_;
	my @text = &usage();
	$txtWid->insert( 'end', join( '', @text ) );
}

sub doHelpWindow {
	my $hFont = '-*-Helvetica-Medium-R-Normal--*-140-*-*-*-*-*-*';
	
	if( Exists($winHelp) ) {
		$winHelp->deiconify();
	} else {
		$winHelp = $winMain->Toplevel();
		$winHelp->title( 'Help for Application' );
		my $txtHelp = $winHelp->Scrolled(
			'ROText',
			-width  => 80,
			-height => 30,
			-font   => $hFont,
			-wrap   => 'word',
			-scrollbars  => 're',
		);
		$txtHelp->pack( -expand => 'yes', -fill => 'both' );
		$winHelp->Button(
		  -text    => 'Dismiss',
		  -command => sub { $winHelp->destroy() },
		)->pack();
		
		$txtHelp->insert( 'end', join( '', &usage() ) );
	}
}

sub lg {
	log($_[0])/$log10; # returns log_10(VAR) (only ln(VAR) exists as perl built-in)
}

sub isInt {
	defined $_[0] && $_[0] =~ /^[+-]?\d+$/; # checks if value is signed(optional) integer
}

sub isFloat {
	defined $_[0] && $_[0] =~ /^[+-]?\d+(\.\d+)?$/; # checks if value is signed(optional) floating point
}

sub catfile {
	$catfile = $winMain->getOpenFile(-title=>'Please Choose a Spectral Catalog');
}

sub specfile {
	$specfile = $winMain->getOpenFile(-title=>'Please Choose a Spectral File (which is hopefully whitespace-delimited)');
}

sub plot {
	# first ensure that the catalog is tab-delimited (original form sometimes works, sometimes doesn't)
	my $needsCAT2TSV;
	if ($catfile ne '') {
		# this whole group does in-house conversion of csv2tsv if it finds a SINGLE comma in the spectral data file
		$needsCAT2TSV = 0;
		unless ($catfile =~ m/.*\.(tsv)/) {
			print "will parse this catalog to determine whether it is a csv\n";
			open(CATFILE,"$catfile");
			while (my $line = <CATFILE>) {
				my @linearray = split("\t",$line);
				if (! $linearray[1] ne '') {
					$needsCAT2TSV = 1;
					print "this catalog needs converted to tab-delimited\n";
					last;
				}
			}
			close(CATFILE);
			
			if ($needsCAT2TSV and -e "$catfile.tsv") {
				print "it seems a tab-delimited form already exists...";
				
				# check the file timestamps
				my $catstat = stat($catfile);
				my $catmtime = $catstat->mtime;
				my $tsvfile = "$catfile.tsv";
				my $tsvstat = stat($tsvfile);
				my $tsvmtime = $tsvstat->mtime;
				
				if ($catmtime > $tsvmtime) { # checks to see if the catalog has been edited since the tsv was created
					print " however, it was found that the catalog has been recently modified.. will update the tsv file\n";
					unlink($tsvfile); # if the tsv file is older than the cat file, removes the tsv file
				} else { # else, uses the tsv file as normal
					print " and all seems in order.. will use the tsv file\n";
					$catfile = $tsvfile;
					$needsCAT2TSV = 0;
				}
			}
			if ($needsCAT2TSV) {
				print "converting to tab-delimited form now...\n";
				
				# copy catalog file
				use File::Copy;
				copy("$catfile","$catfile.tsv");
				
				# point to new file
				$catfile = "$catfile.tsv";
				
				# opens new file and substitutes all commas for tabs
				use Tie::File;
				tie my @fullfile, 'Tie::File', "$catfile";
				foreach (@fullfile) {					# loops through the file, line-by-line
					# note: the following "s/*/*/g;" sections are regular expression substitutions
					s/(.{13})(.{8})(.{8})(.{2})(.{10})(.{3})(.{7})(.{2})(.{1})(.{1})(.+)/$1\t$2\t$3\t$4\t$5\t$6\t$7\t$8\t$9\t$10\t$11/g;
				}
				untie @fullfile;
			}
		}
	} else {
		print "you did not select a catalog to plot... please try again\n";
		return 0;
	}
	
	# also checks the spectra data for whitespace-delimited values
	# note that I use a string to contain the additional plot argument for optional spectral data; if none exist, then the string is empty and the plot command does not plot anything besides the catalog file
	my $otherplot;
	my $needsCSV2TSV;
	if ($specfile ne '') {
		# this whole group does internal conversion to tab-delimited data if it finds a SINGLE comma in the spectral data file
		$needsCSV2TSV = 0;
		unless ($specfile =~ m/.*\.(tsv|igor)/) {
			print "will parse the spectral data to determine whether it is a csv\n";
			open(SPECFILE,"$specfile");
			my $counter = 0;
			LINE: while (my $line = <SPECFILE>) {
				$counter++;
				next LINE if ($line =~ m/^#/);
				my @linearray = split(",",$line);
				if ($linearray[1] ne '') {
					$needsCSV2TSV = 1;
					print "this file needs converted from csv to whitespace-delimited\n";
					last LINE;
				}
			}
			close(SPECFILE);
			
			if ($needsCSV2TSV) {
				use File::Copy;
				copy("$specfile","$specfile.tsv") if (! -e "$specfile.tsv");
				
				# point to new file
				$specfile = "$specfile.tsv";
				
				# opens new file and substitutes all commas for tabs
				use Tie::File;
				tie my @fullfile, 'Tie::File', "$specfile";
				#foreach (@fullfile) {					# loops through the file, line-by-line
				#	# note: the following "s/*/*/g;" sections are regular expression substitutions
				#	s/,/\t/g;
				#}
				my $lineidx = 0;
				LINE: while ($lineidx < $#fullfile) {
					my $line = $fullfile[$lineidx];
					if ($line =~ m/^#/) {
						splice(@fullfile, $lineidx, 1);
						next LINE;
					} else {
						$fullfile[$lineidx] =~ s/,/\t/g;
						$lineidx++;
					}
				}
				untie @fullfile;
			}
		}
		
		# sets new specfile to be the tab-delimited version
		$otherplot = ", '$specfile' using 1:2 with lines ls 2";
	} else {
		$otherplot = '';
	}
	
	# sets the y-axis scale
	if ($ymax) {
		# sets the y-axis range to autoscale only the maximum
#		if (!$ymin) {$ymin=0;}
		$gnuplot->print( "set yrange [$ymin:$ymax]\n" );
	} else {
		# sets the y-axis range to autoscale only the maximum
		$gnuplot->print( "set yrange [0:1000]\n" );
		$gnuplot->print( "set autoscale ymax\n" );
	}
	
	# sets the x-axis range, tics, and formatting
	if ($freq1 ne '' and $freq2 ne '') {
		if ($freq1 > $freq2) {
			print "WARNING: you tried to reverse the x-axis.. this is a irreversible issue with the gnuplot window! please try again...\n";
			return 0;
		}
		$gnuplot->print( "set xrange [$freq1:$freq2]\n" );
	}
	if ($ticmaj) {
		$gnuplot->print( "set xtics $ticmaj\n" );
	} else {
		$gnuplot->print( "set xtics autofreq\n" );
	}
	if ($ticmin) {
		$gnuplot->print( "set mxtics $ticmin\n" );
	} else {
		$gnuplot->print( "set mxtics default\n" );
	}
	if ($xdecimals) {
		my $format = "%6.".$xdecimals."f";
		$gnuplot->print( "set format x \"$format\"\n" );
	} else {
		$gnuplot->print( "set format x\n" );
	}
	if ($mdecimals) {
		my $format = "%6.".$mdecimals."f";
		$gnuplot->print( "set mouse mouseformat \"$format\"\n" );
	} else {
		$gnuplot->print( "set mouse mouseformat \"%6.0f\"\n" );
	}
	
#	# sets legend parameters
#	$gnuplot->print( "set multiplot\n" );
#	$gnuplot->print( "unset key\n" );
	
	# Here I use a string to contain the effect of temperature-scaling due to the partition function. If you always include the (T_0 / T)^(n+1) (see Equation 4 of "catintro.pdf"), then the plot does bad things. I think it's better to have incorrect scaling above 300 K than no plot at all...
	my $Qscale = 1;
	if ($temperature > 300) {
		#print "This program does not work properly for temperatures above 300 K.\n";
		#print "All relative line intensities are accurate, but the absolute scale no longer is.\n";
		#print "You have been warned...\n";
		$Qscale = "1";
	} else {
		if ($usepartfun) {
			if (!$partConst0 == 0 && !$partConst1 == 0 && !$partConst2 == 0 && !$partConst3 == 0) {
				my $Q300 = 10**($partConst0 + $partConst1*(lg(300))**1 + $partConst2*(lg(300))**2 + $partConst3*(lg(300))**3);
				my $Qtemp =10**($partConst0 + $partConst1*(lg($temperature))**1 + $partConst2*(lg($temperature))**2 + $partConst3*(lg($temperature))**3); 
				$Qscale = $Q300/$Qtemp;
			} else {
				print "WARNING: You opted to use explicit partition function scaling, yet one of the coefficients\n";
				print "was found to be zero... please fix this now. Meanwhile, will ignore temperature scaling.\n";
			}
		} else {
			$Qscale = "(300/$temperature)**(3/2+1)";
		}
	}
	
	### sets the scaling factor according to the lower/upper energies (Equations 3 in catintro.pdf) ###
	# NOTE: assumes Q(T) scales via T^(n+1) though!!! see "gnuplotfitpart.sh" for ways to get around this...
	my $energyscale = "(exp(-\$5/0.695/$temperature)-exp(-(\$5+(\$1*0.0000333564))/0.695/$temperature))/(exp(-\$5/0.695/300)-exp(-(\$5+(\$1*0.0000333564))/0.695/300))";
	
	# performs the actual scaling.. this is a complicated command so EDIT AT YOUR OWN RISK!!!
	$gnuplot->print( "plot '$catfile' using 1:(10**\$3 *$catscale *$Qscale *$energyscale):(isobserved(\$7)) with impulses lt 1 lw $linewidth palette notitle $otherplot\n" ) if ($temperature > 0 and $catfile ne '');
	
#	# adds legend
#	$gnuplot->print( "set key; unset tics; unset border; unset xlabel; unset ylabel\n" );
#	$gnuplot->print( "plot [][0:1] 2 title 'Observed' lt 1 lw 4 linecolor rgb \"red\", 2 title 'Unobserved' lt 1 lw 4 linecolor rgb \"blue\"\n" );
}

# this performs the saving routine.. EDIT AT YOUR OWN RISK!!
sub save {
	my $savefile = '';
	
	# choose file to save to...
#	my $types = [	# this doesn't work on Window...fine
#		['PNG Files',	'.png', '.PNG'],
#	];
	$savefile = $winMain->getSaveFile(
		-title				=>	'Please Choose an Output File to Save as PNG',
		-defaultextension	=>	'png',
	);
	
	# sets output options and then saves
	if ($savefile ne '') {
		unless ($savefile =~ m/^.*png$/g) {	# fixes the case that one saves to a file that does not contain a png file extension
			$savefile = "$savefile.png";
			print "please note that I appended a .png extension to the filename for you...\n";
		}
		$gnuplot->print( "set terminal png size 800,600 enhanced font \"Helvetica,12\" linewidth 1.5\n" );
		$gnuplot->print( "set output '$savefile'\n" );
		$gnuplot->print( "replot\n" );
	} else {
		print "If you did not choose a file, then ignore the error above about \"Use of uninitialized value \$savefile in string\" since I do not know how to prevent it.. DON'T PANIC\n\n";
	}
	
	# cleanly resets output and terminal so you may continue..
	$gnuplot->print( "set terminal $terminal\n" );
	$gnuplot->print( "set output\n" );
}

sub quit {
	$gnuplot->close; $winMain->destroy;
}

sub toGP {
	if ($GPinput) {
		print "you ran \"$GPinput\"\n";
		$gnuplot->print( "$GPinput\n" );
		#$gnuplot->print( "replot\n" );
		$GPinput = "";
	} else {
		print "nothing was in the input field...\n";
	}
}

sub pipetest {
#	print "\n\n\n========\nthis is from show var all:\n";
#	$gnuplot->print( "show variables all\n"); # prints ALL internal gnuplot variables to the STDOUT pipe
#	sleep(1);
#	print "will now try to use an internal variable...\n";

	$gnuplot->print( "set print \"GPVAL_X.OUT\"\n");
	$gnuplot->print( "print GPVAL_X_MIN\n");
	$gnuplot->print( "print GPVAL_X_MAX\n");
	$gnuplot->print( "set print\n");
	select(undef, undef, undef, 0.25);
	open (GPVAL_X, "<", "GPVAL_X.OUT");
	my @GPVAL_X = <GPVAL_X>;
	`del GPVAL_X.OUT`;
	chomp @GPVAL_X;
	$freq1 = $GPVAL_X[0];
	$freq2 = $GPVAL_X[1];
	
	#### this doesn't seem to work properly...
	# this redirects STDOUT and then outputs it...
#	my $TESTSTDOUT;
#	do {
#		local *STDOUT;
#		open (STDOUT, ">", \$TESTSTDOUT) or die "Can't open STDOUT: $!";
#		
#	#	$gnuplot->print( "save \'-\'\n"); # prints all internal things
#	#	$gnuplot->print( "show variables all\n");
#	
#		print "\nthis ls command will also be logged:\n";
#		print `ls -l`;
#	};
#	print "\n\n\n========\nHere is the internal variable, which should contain all STDOUT:\n";
#	print "$TESTSTDOUT\n";
#	print "=========\n";
}
