###################################################################################################
# Typically overridden settings
#

my $clusterhost=$ENV{CCP_SCHEDULER};

# Specific account to use
# --> Defaults to current user
my $account=$ENV{USERDOMAIN}.'\\'.$ENV{USERNAME};

#
# Process placement options
#
# Set this to 2 on systems that are limited by memory bandwidth (most are).
my $processorspersolver=1;

# Mapping of local paths to network paths
my %pathmap;

# Create cfxccs_options.txt in %USERPROFILE%\.cfx to override these settings e.g:
# ACCOUNT=MYDOMAIN\MyUser
# PATHMAP=C:\Users\MyUser;\\myheadnode\Users\MyUser
# PATHMAP=C:\Program Files\Ansys Inc;\\myheadnode\Ansys Inc
# PROCESSORSPERSOLVER=2
# CLUSTERHOST=myheadnode

###################################################################################################

#
# Setup helper functions
#
use CFX5::Output qw(:all error_text);
use CFX5::PathManip qw(join_file basename dirname normalize_filename);
use CFX5::CCLI;
use CFX5::Config qw(getconf);

my %ucpathmap;
my %revpathmap;

# Check if a path is a UNC path 
sub check_unc_path {
  my $path=shift;
  if(substr($path,0,2) eq "\\\\" or substr($path,0,2) eq "//") {
    return 1;
  } else {
    return 0;
  }
}

# Map a local path to a UNC path
sub maplocalpath {
  my $value=shift;
  my $info=shift;
  
  if(not -e "$value"){
    # Probably not a path, return as is
    return $value;
  }

  $value=uc(normalize_filename($value));
  my $mappedpath=mappath_tail($value);
  if($mappedpath ne $value){
    print OUT "Mapping path for $info\n Original $value\n Mapped: $mappedpath\n";
  }
  return $mappedpath;
}
sub mappath_tail {
  my $path=shift;

  return undef unless $path;

  if($ucpathmap{$path}){
    return $ucpathmap{$path};
  }
  elsif(dirname($path) and dirname($path) ne '.') {
    return join_file(mappath_tail(dirname($path)),basename($path));
  }
  else{
    return $path;
  }
}


sub processccl {
  my $api=shift;
  my $objpath=shift;
  my $function=shift;
  my $fargs=\@_;

  my @list;
  my $rv;
  
  $rv=$api->getChildParamList($objpath, \@list);
  
  foreach my $param (@list) {
    my $value;
    $rv=$api->getStringValue("$objpath/$param", \$value);
    $value=$function->($value, "$objpath/$param");
    $rv=$api->setStringValue("$objpath/$param", $value);
  }

  $rv=$api->getChildObjectList($objpath, \@list);
  
  foreach my $obj (@list) {
    processccl($api, "$objpath/$obj", $function);
  }
}

#
# Process command line options
#

use Getopt::Long;
$Getopt::Long::passthrough = 1;
$Getopt::Long::ignorecase = 0;
$options = {};
GetOptions($options, "-part=i", "-indirect-startup-path=s");

my $numpartitions=$options->{"part"};
my $indirectpath=$options->{"indirect-startup-path"};
die "Invalid indirect path" unless defined $indirectpath;
my $outfile=$indirectpath.'.out';
my $indirectcclfile=join_file($indirectpath.'.dir', "startup.ccl");

#
# Open the output file
#
open(OUT, ">>$outfile") || die "Error opening outfile $outfile: $!";
use IO::Handle;
OUT->autoflush(1);

# Read cfxccs_options.txt file, if it exists
# Look first in documented location: %USERPROFILE%\.cfx
# then (for backwards compatibility) in: %CFX_USER_CONFIG_DIR%
my @dirs=( getconf('USERPROFILE'), getconf('CFX_USER_CONFIG_DIR') );
my $optionsfile;
foreach my $dir (@dirs) {
  $optionsfile = join_file($dir,'.cfx','cfxccs_options.txt');
  unless (-e $optionsfile) {
    $optionsfile = join_file($dir,'.cfx','cfxccs_options.txt.txt');
  }
  if (-e $optionsfile) {
    last;
  }
}
if(-e $optionsfile){
  my @lines;
  unless(open(IN, "<$optionsfile")){
    print OUT "ERROR opening user CCS options file $optionsfile: $!\n";
    close(OUT);
    exit(1);
  }
  print OUT "Processing user options from $optionsfile:\n";
  while(my $line=<IN>){
    chomp($line);
    $line=~s!^\s+!!; # Remove space at the begining of each line
    $line=~s!\s+$!!; # Remove space at the end of each line

    if ( $line=~m!^$! ) { # Ignore blank lines
      next;  
    }
    elsif($line=~m!^#!) { # Ignore comment lines
      print OUT "$line\n";
      next; 
    }
    $line=~s!\s*([=;])\s*!$1!g; # Remove spaces around ";" and "=" separators
    @fields=split(/=/, $line);
    $numfields=@fields;
    if($numfields>1){
      $fields[0]=~tr/a-z/A-Z/; # Upper case 
      $line=join("=",@fields);
    }
    if($line=~m!^PATHMAP=([^;]+)*;(.+)!){
      $pathmap{$1}=$2;
    }
    elsif($line=~m!^ACCOUNT=(.+)!){
      $account=$1;
    }
    elsif($line=~m!^CLUSTERHOST=(.+)!){
      $clusterhost=$1;
    }
    elsif($line=~m!^PROCESSORSPERSOLVER=([0-9]+)!){
      $processorspersolver=$1;
    }
    else {
      print OUT "ERROR Processing user CCS options file $optionsfile: $line\n";
      close(OUT);
      close(IN);
      exit(1);
    }
    print OUT "$line\n";
  }
  close(IN);
}
else {
  print OUT "No user CCS options file has been specified.\nThe following parameter settings are assumed:\n\n";
  my %h = (CLUSTERHOST         => $clusterhost,
           ACCOUNT             => $account,
           PROCESSORSPERSOLVER => $processorspersolver);

  while (my ($var,$value) = each %h)
  {
    printf OUT "%20s = %s\n", $var, defined $value ? $value : '<undefined>';
  }
}

foreach my $path (keys %pathmap){
  $ucpathmap{uc(normalize_filename($path))}=uc(normalize_filename($pathmap{$path}));
  $revpathmap{uc(normalize_filename($pathmap{$path}))}=uc(normalize_filename($path));
}

# Set num processors after reading user options.
my $numprocessors=$numpartitions * $processorspersolver;


# Shared CFX Installation
my $sharedroot = dirname(dirname(`cfx5info -whereis cfx5info`));
if ( exists $ENV{CFX_SHAREDINST_DIR}) {
  $sharedroot = $ENV{CFX_SHAREDINST_DIR} ;
}

#
# Map paths
#

print OUT "\nMAPPING PATHS\n";
$sharedroot=maplocalpath($sharedroot, "CFX installation directory");
$indirectpath=join_file(maplocalpath(dirname($indirectpath), "Working directory"), basename($indirectpath));

unless(check_unc_path($sharedroot)) {
  print OUT "ERROR: Shared CFX installation is not a UNC path: $sharedroot\n"; 
  close(OUT);
  exit 1; 
}

unless(check_unc_path(dirname($indirectpath))){
  print OUT "ERROR: Working directory is not a UNC path: $indirectpath\n"; 
  close(OUT);
  exit 1; 
}


my $api=CFX5::CCLI::new_api_object();
my @ccl;
my $rv;
$rv=$api->readTextFile(join_file($indirectpath.".dir","startup.ccl"), \@ccl);
$rv=$api->parseCCLData(\@ccl);
processccl($api, "", \&maplocalpath);
$api->deleteObj('/SIMULATION CONTROL/EXECUTION CONTROL/PARALLEL HOST LIBRARY');
$rv=$api->getFormattedCCL("", 78, \@ccl, );
unless(open(CCL, ">".join_file($indirectpath.".dir","startup.ccl"))){
  print OUT "ERROR opening indirect ccl file: $!\n";
  close(OUT);
  exit(1);
}
foreach my $line (@ccl){
  print CCL "$line\n";
}
close(CCL);

#
# Connect to cluster
#
use Win32::OLE;
my $cluster;
my $ishpc=0;


# Try HPC 2008 first
if(Win32::OLE->new("Microsoft.HPC.Scheduler.Scheduler")){
  $ishpc=1;
}

$cluster=Win32::OLE->new("Microsoft.ComputeCluster.Cluster");
unless($cluster) {
  print OUT "ERROR: Could no connect to a scheduler OLE object\n";
  close(OUT);
  exit 1;
}

$cluster->Connect($clusterhost);
unless(defined $cluster){
  print OUT "ERROR: Connection to cluster $clusterhost failed\n";
  close(OUT);
  exit 1;
}
print OUT "Connected to cluster $cluster->{Name}\n";

my $job = $cluster->CreateJob();
$job->{Name} = "ANSYS CFX - ". basename($indirectpath);

$job->{IsExclusive}=0;
$job->{MinimumNumberOfProcessors} = $numprocessors;
$job->{MaximumNumberOfProcessors} = $numprocessors;

# Stdout and stderr files, normally empty unless an error occurs
# Default to stdout and stderr in the working directory
my $stdoutfile=$indirectpath.'.stdout';
my $stderrfile=$indirectpath.'.stderr';

my $task = $cluster->CreateTask();
$task->{Name}="ANSYS CFX Solver";
$task->{IsRerunnable}=0;
$task->{RunTime}="Infinite";
if($ishpc){
  $task->SetEnvironmentVariable("MPIEXEC_OPTIONS", "-affinity");
}
$task->{MinimumNumberOfProcessors} = $numprocessors;
$task->{MaximumNumberOfProcessors} = $numprocessors;

$task->{Stdout} = $stdoutfile;
$task->{Stderr} = $stderrfile;
$task->{WorkDirectory} = dirname($indirectpath);

# Disable remote checks, we know all nodes are Windows Server x64
$task->SetEnvironmentVariable("CFX_DISABLE_REMOTE_CHECKS", 1);
$task->SetEnvironmentVariable("CFX5_HOSTS_CCL", "NUL");
$task->SetEnvironmentVariable("_CFX_PROC_PER_SOLVER", $processorspersolver);
$task->SetEnvironmentVariable("_CFX_ENABLE_MSMPI_SM", 1);

$task->{CommandLine}='"'.join_file($sharedroot,'bin','cfx5solve').'" -start-method MSMPI -part '.$numpartitions.' -indirect-startup-path "'.$indirectpath.'"'; 

print OUT "Command submitted to cluster =\n  $task->{CommandLine}\n";
close(OUT);


$job->AddTask($task);
unless( $cluster->QueueJob($job, $account, "", 0, 0) ) {
  open(OUT, ">>$outfile") || die "Error opening outfile $outfile: $!";
  use IO::Handle;
  OUT->autoflush(1);
  print OUT "ERROR: Could not submit job: " . $cluster->{ErrorMessage}."\n";
  close(OUT);
  exit 1;
}

my $jobid=$job->{Id};

while($job->{Status} < 3){
  $job=$cluster->getJob($jobid);
  sleep(1);
}

# Seem to get caching issue when running over network share 
# Sleep for 10 seconds, hopefully the caches will update by then
sleep(10);

exit 0 if $job->{Status} == 3;

#
# Open the output file
#
open(OUT, ">>$outfile") || die "Error opening outfile $outfile: $!";
use IO::Handle;
OUT->autoflush(1);

if(-e $stdoutfile){
  print OUT "STDOUT:\n";
  open(IN, "<$stdoutfile") || die "Error open stdout file $stdoutfile: $!";
  while(my $line=<IN>){
    chomp $line;
    print OUT "$line\n";
  }
  close(IN);
}
if(-e $stderrfile){
  print OUT "STDERR:\n";
  open(IN, "<$stderrfile") || die "Error open stderr file $stderrfile: $!";
  while(my $line=<IN>){
    chomp $line;
    print OUT "$line\n";
  }
  close(IN);
}
close(OUT);

exit 1;

