#!/usr/bin/perl
#
# Script for converting between 
#   Matlab->Scilab
#   Scilab->Matlab 
# McStas data
#
# Cyclic conversion, e.g. Matlab->Scilab->Matlab is NOT supported!
#
# Peter Willendrup, Risoe, 20031205
#
#   This file is part of the McStas neutron ray-trace simulation package
#   Copyright (C) 1997-2004, All rights reserved
#   Risoe National Laborartory, Roskilde, Denmark
#   Institut Laue Langevin, Grenoble, France
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#   Based on / inspired from m2sci.pl:
#     (m2sci.pl by Torbjorn Pettersen <Torbjorn.Pettersen@broadpark.no>
#      available from http://home.broadpark.no/~tpette-4/)
#
use File::Basename;
use File::Find;
use Cwd;
use Config;

# Declaration of the global scope vars,
my ($i,$j);
my ($outdir,$indir);
my ($format,$randstr,$randdir,$quiet);
my $cwd = getcwd();
my @junk;

# Assume no random output dir...
$randdir = 0;
# Assume we want status written to output
$quiet = 0;

# Echo something meaningful to stdout
print "mcconvert.pl, part of the McStas simulation package\n";
print "This is free software, licensed under the GPL\n";
print "Copyright (C) 1997-2004, All rights reserved\n";
print "Risoe National Laborartory, Roskilde, Denmark\n";
print "Institut Laue Langevin, Grenoble, France\n\n";

# Parse the commandline args
for ($i = 0; $i< @ARGV; $i++) {
  if ($ARGV[$i] =~ /^--format=([a-zA-Z0-9_\"]+)$/) {
    $format = $1;
  } elsif ($ARGV[$i] =~ /^--outdir=([a-zA-Z0-9_\\\/\"\:\.~]+)$/) {
    $outdir = $1;
  } elsif ($ARGV[$i] =~ /^--indir=([a-zA-Z0-9_\\\/\"\:\.~]+)$/) {
    $indir = $1;
  } elsif ($ARGV[$i] eq "--quiet") {
    $quiet = 1;
  } elsif ($ARGV[$i] eq "--help") {
    # Print some help info, next exit
    print "\nmcconvert.pl script from McStas\n";
    print "\nConverts between Matlab and Scilab version of\nMcStas output data.\n";
    print "\nusage:\n";
    print "\nNo arguments:\n\tWill try to guess wanted output format\n\tWill place files in random subdir\n";
    print "\tWill try to convert files in current dir\n";
    print "\n--format=FORMAT\n\tSpecifies wanted output format, i.e. Matlab or Scilab\n";
    print "\n--outdir=DIR\n\tSpecifies wanted output directory\n";
    print "\n--indir=DIR\n\tSpecifies wanted input directory\n";
    print "\n--quiet\n\tSuppress status info (e.g. mcstas.m -> mcstas.sci)\n";
    print "\nIf no --indir  is specified, --indir=\".\" is assumed\n";
    print "\nWarning: mcconvert.pl searches the --indir RECURSIVELY\n\n";
    exit();
  } else {
    push @junk, $ARGV[$i];
  }
}

if (@junk>0) {
  print "Warning: Options @junk on commandline not understood...\n\n";
}

# Handle the indir first...
if (!$indir) {
    print "You did not give me any files to convert, assuming that you ment everything in .\n";
    $indir = ".";
}

if (! -e $indir) {
    die "mcconvert.pl: Input directory $indir does not exist...\n\n";
}

if (-e "$indir/.converted") {
    print STDERR "\nmcconvert.pl:You are seeminly trying to convert a set of converted data!";
    die "\nCyclic conversion not supported!\n\n";
}

if (!$format) {
    print "You did not specify a conversion format (Scilab/Matlab)\n";
    print "Trying to guess one... ";
    if ((-e "$indir/mcstas.sci") && (! -e "$indir/mcstas.m")) {
	$format = "Matlab";
    } elsif ((! -e "$indir/mcstas.sci") && (-e "$indir/mcstas.m")) {
	$format = "Scilab";
    } elsif ((-e "$indir/mcstas.sci") && (-e "$indir/mcstas.m")) {
	die "mcconvert.pl: You have mcstas.sci AND mcstas.m - don't know what to do then...\n\n";
    } else {
	die "mcconvert.pl: Could not find a Matlab/Scilab mcstas.* to convert from...\n\n";
    }
    print "Converting to $format format...\n";
}


if (!$outdir) {
    # Create a random directory for the converted data
    $randstr = generate_random_string(5);
    $outdir = "converted_${format}_${randstr}";
    $randdir = 1;
}
if (! -e $outdir) {
    mkdir($outdir) or die "mcconvert.pl: Could not create $outdir\n\n";
    open(TMP,">$outdir/.stop");
    close(TMP);
}
if ($Config{'osname'} eq 'MSWin32') {
    if (!($outdir =~ /^.:/)) {
	$outdir = "$cwd/$outdir";	
    }
} else {
    if (!($outdir =~ /^\//)) {
	$outdir = "$cwd/$outdir";
    }
}
if ($randdir == 1) {
  print "\nYour files, converted to $format format will be placed in $outdir\n\n";
}

# If this is Win32, let us simply make any \ in the filenames a /
if ($Config{'osname'} eq 'MSWin32') {
    @junk = split(/\\/,$outdir);
    $outdir = join('/',@junk);
    @junk = split(/\\/,$indir);
    $indir = join('/',@junk);
}

# We are now ready to do the work:
chdir($indir);
find(\&findit,".");

open(TMP,">$outdir/.converted");
close(TMP);
chdir($cwd);
print "\n\nmcconvert.pl done...\n\n";

sub findit {
    my $object = $File::Find::name;
    $object =~ s!^\.!$outdir!g;
    if (-e ".stop") {
	$File::Find::prune = 1;
    } else {
	if (-d $_) {
	    if (!($object eq $outdir)) {
		if ((! -e $object) && (! -e "$_/.stop")) {
		    mkdir($object) || die "mcconvert.pl: Could not create $object!\n\n";
		}
	    }
	} else {
	    convert($_,$object);
	}
    }
}


sub convert {
    my ($infile,$outfile) = @_;
    my ($tmp, $base, $dirname, $suffix, $NEXTFUNC);
    $tmp = $File::Find::dir;
    $tmp =~ s!^\.\/!$indir/!;

    # Split the filename in pieces:
    ($base, $dirname, $suffix) = fileparse($outfile,".m",".sci");
    if ($suffix eq ".m") {
	$outfile = "$dirname$base.sci";
    } elsif ($suffix eq ".sci") {
	$outfile = "$dirname$base.m";
    } else {
	$outfile = "$dirname$base";
    }

    # Adjust the $base, remove .'s
    $base =~ s!\.!_!g;

    if ($quiet == 0) {
      print "Reading from $tmp/$infile -> ";
    }
    open(IN,"<$infile");
    open(OUT,">$outfile") || die "mcconvert.pl: Unable to create $outfile: $!\n\n";
    if ($quiet == 0) {
      print "\t $outfile\n";
    }
    $NEXTFUNC = 0;
    while (<IN>) {
	if ($format eq "Scilab") {
	    if ((/^\w*function/) & $NEXTFUNC) {
		print OUT "endfunction\n"; # add endfunction 
	    } else { $NEXTFUNC = 1;};
	
	    unless (/\w*fprintf\w*/) {
		s/%/\/\//g;			# Comments are replaced
	    }
	
	    s/nargout/argn(1)/g;	# Outputarguments to function
	    s/nargin/argn(2)/g;		# Inputarguments to function
	
	    s/isempty\((\w*)\)/($1==[])/g;
	    s/\r$//g;
	
	    # Replacing matlab functions with corresponding scilab commands.
	    s/\bchar\b/ascii/;
	    s/\bcompan\b/companion/g;
	    s/\bfclose\b/mclose/g;
	    s/\bfigure\b/xbasc/g;
	    s/\bfopen\b/mopen/g;
	    s/\breshape\b/matrix/g;
	    s/\bfindstr\b/strindex/g;
	    s/\bif\ single_file\b/if\ single_file==1/g;
	    s/\.func=\'/\.func=\'get_/g;
	} elsif ($format eq "Matlab") {
	    if (/^\w*endfunction/) {
		print OUT "%" ; # comment out endfunction 
	    }
	    unless (/\w*fprintf\w*/) {
		s/\/\//%/g;			# Comments are replaced
	    }
	
	    s/argn\(1\)/nargout/g;	# Outputarguments to function
	    s/argn\(2\)/nargin/g;		# Inputarguments to function
	
	    s/(\w*)==\[\]/isempty\(($1)\)/g;
	    s/struct\(\)/\[\]/g;
	
	    # Replacing matlab functions with corresponding scilab commands.
	    s/\bascii\b/char/;
	    s/\bcompanion\b/compan/g;
	    s/\bmclose\b/fclose/g;
	    s/\bxbasc\b/figure/g;
	    s/\bmopen\b/fopen/g;
	    s/\bmatrix\b/reshape/g;
	    s/\bstrindex\b/findstr/g;
	    s/\bif\ single_file==1/if\ single_file/g;
	    s/\bmode\b/\%mode/g;
	}
	
	# Check if we are at one of the inline functions - print new according 
	# to format, then exit (closing the open files...).
	if (/^\w*function d=mc\w*_inline/) {
	    inline_replace(OUT,$format,$base);
	    close(IN);
	    close(OUT);
	}
	print OUT;
    }
    close(OUT);
}

# Function for replacing the inline_ Matlab/Scilab functions
sub inline_replace {
    my ($OUTFILE,$format,$base) = @_;
    if ($format eq "Scilab") {
	print $OUTFILE
"function d=mcload_inline(d)
// local inline func to load data
execstr(['S=['+part(d.type,10:(length(d.type)-1))+'];']);
if ~length(d.data)
 if ~length(strindex(d.format, 'binary'))
  exec(d.filename,-1);p=d.parent;
  if ~execstr('d2='+d.func+'();','errcatch'),d=d2; d.parent=p;end
 else
  if length(strindex(d.format, 'float')), t='f';
  elseif length(strindex(d.format, 'double')), t='d';
  else return; end
  fid=mopen(d.filename, 'rb');
  pS = prod(S);
  x = mget(3*pS, t, fid);
  d.data  =matrix(x(1:pS), S);
  if length(x) >= 3*pS,
  d.errors=matrix(x((pS+1):(2*pS)), S);
  d.events=matrix(x((2*pS+1):(3*pS)), S);end
  mclose(fid);
  return
 end
end
endfunction
function d=mcplot_inline(d,p)
// local inline func to plot data
if ~length(strindex(d.type,'0d')), d=mcload_inline(d); end
if ~p, return; end;
execstr(['l=[',d.xylimits,'];']); S=size(d.data);
t1=['['+d.parent+'] '+d.filename+': '+d.title];t = [t1;['  '+d.variables+'=['+d.values+']'];['  '+d.signal];['  '+d.statistics]];
mprintf('%s\\n',t(:));
if length(strindex(d.type,'0d')),return;
else
w=winsid();if length(w),w=w(\$)+1; else w=0; end
xbasr(w); xset('window',w);
if length(strindex(d.type,'2d'))
 d.x=linspace(l(1),l(2),S(2)); d.y=linspace(l(3),l(4),S(1)); z=d.data;
 xlab=d.xlabel; ylab=d.ylabel; x=d.x; y=d.y;
 fz=max(abs(z));fx=max(abs(d.x));fy=max(abs(d.y));
 if fx>0,fx=round(log10(fx)); x=x/10^fx; xlab=xlab+' [*10^'+string(fx)+']'; end
 if fy>0,fy=round(log10(fy)); y=y/10^fy; ylab=ylab+' [*10^'+string(fy)+']'; end
 if fz>0,fz=round(log10(fz)); z=z/10^fz; t1=t1+' [*10^'+string(fz)+']'; end
 xset('colormap',hotcolormap(64));
 plot3d1(x,y,z',90,0,xlab+'@'+ylab+'@'+d.zlabel); xtitle(t);
else
d.x=linspace(l(1),l(2),max(S));
 plot2d(d.x,d.data);xtitle(t,d.xlabel,d.ylabel);end
end
xname(t1);
endfunction
mc_$base=get_$base();\n"
} elsif ($format eq "Matlab") {
    print $OUTFILE
"function d=mcload_inline(d)
%% local inline function to load data
S=d.type; eval(['S=[ ' S(10:(length(S)-1)) ' ];']);
if isempty(d.data)
 if ~length(findstr(d.format, 'binary'))
  copyfile(d.filename,[d.func,'.m']);p=d.parent;path(path);
  eval(['d=',d.func,';']);d.parent=p;delete([d.func,'.m']);
 else
  if length(findstr(d.format, 'float')), t='single';
  elseif length(findstr(d.format, 'double')), t='double';
  else return; end
  if length(S) == 1, S=[S 1]; end
  fid=fopen(d.filename, 'r');
  pS = prod(S);
  x = fread(fid, 3*pS, t);
  d.data  =reshape(x(1:pS), S);
  if prod(size(x)) >= 3*pS,
  d.errors=reshape(x((pS+1):(2*pS)), S);
  d.events=reshape(x((2*pS+1):(3*pS)), S);end
  fclose(fid);
  return
 end
end
return;
function d=mcplot_inline(d,p)
%% local inline function to plot data
if isempty(findstr(d.type,'0d')), d=mcload_inline(d); end\nif ~p, return; end;
eval(['l=[',d.xylimits,'];']); S=size(d.data);
t1=['[',d.parent,'] ',d.filename,': ',d.title];t = strvcat(t1,['  ',d.variables,'=[',d.values,']'],['  ',d.signal],['  ',d.statistics]);
disp(t);
if ~isempty(findstr(d.type,'0d')), return; end
figure; if ~isempty(findstr(d.type,'2d'))
d.x=linspace(l(1),l(2),S(2)); d.y=linspace(l(3),l(4),S(1)); 
surface(d.x,d.y,d.data);
else\nd.x=linspace(l(1),l(2),max(S));\nplot(d.x,d.data);end
xlabel(d.xlabel); ylabel(d.ylabel); title(t); axis tight;
set(gca,'position',[.18,.18,.7,.65]); set(gcf,'name',t1);grid on;
if ~isempty(findstr(d.type,'2d')), colorbar; end"
}
}

# This function generates random strings of a given length
sub generate_random_string
{
	my $length_of_randomstring=shift;#the length of the random string to generate

	my @chars=('a'..'z','A'..'Z','0'..'9','_');
	my $random_string;
	foreach (1..$length_of_randomstring) 
	{
		#rand @chars will generate a random number between 0 and scalar @chars
		$random_string.=$chars[rand @chars];
	}
	return $random_string;
}

