#! perl -w

##
#H Name:
##
## cfx5cmds
##
#H Purpose:
##
## Edit the CFX-5 Command Language stored in a CFX-5 Solver Input File
## 
#H Synopsis:
##
#S cfx5cmds -read|-write|-edit -definition <file> -text <file> 
#S            [-help] [-verbose]
##
## []  denotes an optional argument. 
## |   separates mutually exclusive arguments. 
## ... following an argument indicates that it may be repeated.
## <>  denotes that substitution of a suitable value is required. All other
##     arguments are keywords, some of which have a short form.
##
## Arguments:
##
## -read
##     Read the command file from an ANSYS CFX Solver Input File and put it
##     into a text file, which must not already exist.
##
## -write
##     Write parameters contained in the text file to the ANSYS CFX
##     Solver Input File.
##
## -edit
##     Edit an ANSYS CFX Solver Input File using the CFX Solver File Editor
##
## -definition <file> 
## -def <file> 
##     Use <file> as the CFX-5 Solver Input File.
##
## -ccl <file> 
## -text <file> 
##     Use <file> as the command text file.
##     Changes that affect the way the mesh is defined, or that affect the
##     way the physics CCL relates to the topology of the mesh stored
##     in the solver input file, cannot be made using the -ccl option.
##     For example, locators for applying physics cannot be modified using 
##     the -ccl option. Such changes can, however, be made in CFX-Pre.
##
## -help
## -h
##     Print this message.
##
## -verbose
## -v
##     Specifying this option may result in additional output being sent to 
##     the UNIX standard output file (normally the screen).
##

# get the standard CFX-5 library directory
BEGIN {
 if ($0 =~ m!^([a-z]:)?[\\/]!i) {
    # it's an absolute path, use it as-is
    (my $nd=$0) =~ s!(perllib[\\/])?[^\\/]*$!!i;
    unshift @INC, "$nd/perllib";
  } else {
    # wrap this in an eval, and if it fails, pray ... for example,
    # this can fail on Windows if we're running off a share.
    eval {
      require FindBin;
      FindBin->import();
      unshift @INC, "$FindBin::Bin/perllib";
      my $dummy=$FindBin::Bin; # kill used only once warning
    };
    warn $@ if $@;
  }
  if (defined $ENV{CFX_LOCAL_PERLLIB}) {
     unshift @INC, $ENV{CFX_LOCAL_PERLLIB};
  }

}

#
# Generate an unique session ID for the ANSYS help system.
#
if(! $ENV{ANSHELP_SESSION_ID})
{
	srand (time ^ $$ ^ unpack "%L*", rand());
	my $ANSHELP_SESSION_ID = "";
	for ($i = 1; $i <= 64; $i++)
	{
		$ANSHELP_SESSION_ID = $ANSHELP_SESSION_ID . sprintf("%x", int(rand(16)));
	}
	$ENV{ANSHELP_SESSION_ID} = $ANSHELP_SESSION_ID;
}

use CFX5::Init;
CFX5::Init::init();

use vars qw($CVSVersion);
sub BEGIN {
  $CVSVersion='@(#) $Id$\n';
}

use strict;

use CFX5::Output qw(:all);
use CFX5::Config qw(%Config export_conf prepend_to_libpath);
use CFX5::PrintConfig;

use CFX5::CmdLine qw(:util parse_cmdline);

use CFX5::FileTests;
use CFX5::FileStat;

use CFX5::PathManip qw(join_file basename dirname fullpath);
use CFX5::Commands qw(do_cmd handle_rv do_cmd_quiet);

use CFX5::CCLI qw(getcclds setcclds writeccl export_parser_conf);
use CFX5::FrameServ;
use CFX5::System;

use CFX5::FileOps qw(move_file_to_backup append_to_file create_empty_file);

#
# read the configuration
#
CFX5::Config::read_config();

#
# Parse the command line
#
use vars qw($deffile $textfile $op @editor_args);

parse_cmdline
  (\@ARGV,
   'INCLUDE_REQ',
   '-def+'        => \$deffile,
   '-definition+' => '-def',
   '-text+'       => \$textfile,
   '-txt+'        => '-text',
   '-ccl+'	  => '-text',
   -read          => sub { $op='read'; },
   -write         => sub { $op='write'; },
   -edit          => sub { $op='edit'; },
   -safe          => \@editor_args,
  );

exit_if_error();

# 
# Check we got a read/write action and relevant files
# 
unless(defined $deffile or defined $textfile or defined $op) {
  # user didn't pass any arguments, don't give errors about all three!
  _error("No arguments specified. Type $CFX5::Output::Prog -help for help.");
} else {
  unless (defined $op) {
    _error("Please specify -read, -write or -edit")
  } else {
    _error("Please specify a text file")
      unless defined $textfile or $op eq 'edit';
    _error("Please specify a CFX-5 Solver Input File")
      unless defined $deffile;
  }
}

exit_if_error();

if ($op eq 'read' and test_f $textfile) {
  _error("The destination text file $textfile already exists, not overwriting.");
}

# we'll check later whether we can do what we want to, but datafile_w
# doesn't check for existance.  We might as well check here as
# anywhere.
unless (test_e $deffile) {
  _error("The specified CFX-5 Solver Input File $deffile does not exist.");
}

exit_if_error();

#
# go to!
#

if ($op eq 'edit') {
  prepend_to_libpath($Config{CFX_QT_LIBDIR});
  export_parser_conf();

  export_conf(qw(CFX5_PHYSICS_EXEC CFX5ROOT CFXBUILDID CFX_NO_FRAME_VIEWER));

  # this is needed for the physics validator on NT
  if ($Config{windows_nt}) {
    prepend_to_libpath($Config{CFX_STL_LIBDIR});
  }
}

CFX5::PrintConfig::show_vars_by_verbosity();

#
# define read-from-file and write-to-file functions for use below.
# Error handling here is quite limited; these are not generic
# functions, and will exit if they get errors.
#
sub read_ccl_from_file {
  my ($deffile,$textfile)=@_;

  CFX5::FileTests::datafile($deffile);
  CFX5::FileTests::datafile_w($textfile);

  exit_if_error();

  # read from the definition file into the text file
  my $ccl = getcclds($deffile);
  if (!defined $ccl) {
    # getcclds will have signalled an error
    exit(1);
  }
  unless (ref $ccl) {
    # it's an error message, eg "no CCL", which we can just report.
    _abort($ccl);
  }
  # OK, it must be good!
  if (test_f $textfile) {
    # back it up if possible ...
    _debug("Backing up $textfile to $textfile.bak");
    move_file_to_backup($textfile, "$textfile.bak");
    # ... and then unlink it anyway
    ! test_f $textfile 
      or unlink $textfile 
	or _abort "Error removing existing $textfile: $!";
  }
  my @ccl = $ccl->data();
  local *TFILE;
  unless (open TFILE, ">$textfile") {
    _abort("Cannot open $textfile for write: $!");
  }
  for my $l (@ccl) {
    #print TFILE $l . (' ' x (78-length($l))) . "\n";
    print TFILE "$l\n";
  }
  close TFILE or _abort "Cannot write to $textfile: $!";
  _info(scalar(@ccl), "lines of CFX Command Language written to $textfile.");
  1;
}

sub write_ccl_to_file {
  my ($deffile,$textfile)=@_;

  CFX5::FileTests::datafile_w($deffile);
  CFX5::FileTests::datafile($textfile);

  exit_if_error();

  # write the text file into the definition file, we can just use
  # writecmds
  writeccl($deffile, $textfile, 1)
    or exit(1);
  _info("CFX Command Language data written to $deffile.");
  1;
}

if ($op eq 'edit') {
  #TODO: we don't allow readonly mode ...
  CFX5::FileTests::datafile_w($deffile);
  exit_if_error();
  
  my $exec=$Config{CFX5_CCLEDITOR_EXEC};
  CFX5::FileTests::execfile($exec) or exit_if_error();

  #
  # choose a temporary filename for the editor
  #

  my $dir=dirname($deffile);
  # if it has a leading ., we don't want to write an empty filename
  # ...
  (my $base=basename($deffile)) =~ s!(.+)\.[^.]*$!$1!g;
  my $cclfile = join_file($dir, $base . ".ccl");
  if (test_f $cclfile) {
    my $count=1;
    my $pcount=sprintf("%03d",$count);
    while (test_f(join_file($dir, $base . "_$pcount.ccl"))) {
      $count++;
      _abort("Cannot work out temporary filename based on", 
	     join_file($dir,$base . "_999.ccl"))
	if $count > 999;
      $pcount=sprintf("%03d",$count);
    }
    $cclfile=join_file($dir, $base . "_$pcount.ccl");
  }
  #create_empty_file($cclfile)
  #  or _abort("Error creating temporary file for editor: $!");

  $cclfile=fullpath($cclfile);

  # first extract the CCL dataset
  read_ccl_from_file($deffile,$cclfile);

  my $time=(stat $cclfile)[9];

  CFX5::System::lang_fixup();

  export_conf(qw(CFX_DATA_DIR));

  my $rv = handle_rv(do_cmd_quiet($Config{CFX5_CCLEDITOR_EXEC}, @editor_args, $cclfile));
  if (defined $rv and !$rv) {
    # $rv == 0 -- success
    my $ntime=(stat $cclfile)[9];
    if (defined $time and defined $ntime and $time == $ntime) {
      # file has not changed
      _info("Command file unchanged in editor.");
    } else {
      write_ccl_to_file($deffile,$cclfile);
    }
    unlink($cclfile) or _warn("Error deleting $cclfile: $!");
  } elsif (defined $rv) {
    _error("The CFX Command Language editor exited with return code $rv.  Your\n",
	   "modifications to the problem setup have not been made in the\n",
	   "definition file; you can find them in the file\n",
	   "\t$cclfile");
  }

  exit_if_error();
  exit(0);

} elsif ($op eq 'read') {
  read_ccl_from_file($deffile,$textfile);
  exit(0);
} elsif ($op eq 'write') {
  write_ccl_to_file($deffile,$textfile);
  exit(0);
} else {
  internal_error("Unexpected operation \$op=$op");
}
