=head1 NAME

  CFX5::Simulation::Configuration::APIP - ANSYS Product Improvement Program control

=head1 SYNOPSIS

This object is used by L<CFX5::Simulation::Configuration>, to handle APIP data

=head1 DESCRIPTION

$Id$

=cut

package CFX5::Simulation::Configuration::APIP;

use base 'CFX5::Simulation::Subtask';

use strict;
use Carp;

use CFX5::Config qw(%Config query_conf setconf export_conf prepend_to_libpath);
use CFX5::Commands qw(do_cmd_quiet);
use CFX5::FileOps qw(append_to_file);
use CFX5::FileStat;
use CFX5::PathManip qw(join_file);
use CFX5::Simulation::Util qw (GetDataSet);

sub _Do {
  my $self = shift;

  # make sure the request is sensible
  my $do = shift;
  internal_error("First argument must be 0 or 1 or 2") 
    unless defined $do and ($do eq 0 or $do eq 1 or $do eq 2); 
  return unless ($do == 2);

  $self->run();
}


=head4 

  $self->run();

  Perform APIP tasks

=cut

sub run {  
  my $self = shift;

  # Skip if requested
  return 0 if (defined $Config{CFX_APIP_DISABLE});

  my $version = $Config{CFXBUILDID_NOSEP_MAJOR_MINOR};

  # APIP helper executable
  my $apip_helper_exe = $Config{CFX_APIPHELPER_EXEC};
  $apip_helper_exe = join_file($Config{CFX5EXECDIR},'apiphelper.exe') unless (defined $apip_helper_exe);
  return 0 unless (test_e $apip_helper_exe);
  CFX5::FileTests::execfile($apip_helper_exe) or return 0;

  # Set environment
  my $rootvs = 'AWP_ROOT'.$version;
  unless (query_conf($rootvs))
  {
    my $rootvs_value = join_file($Config{INSTALLROOT},'v'.$version);
    setconf($rootvs, $rootvs_value);
    export_conf($rootvs);
  }

  # Return unless APIP is enabled
  my $rv = do_cmd_quiet($apip_helper_exe,'-check','-version',$version);
  return 0 unless ($rv->is_success());

  # Preparation
  my $pe_path = '/SIMULATION CONTROL/EXECUTION CONTROL/SOLVER STEP CONTROL/PARALLEL ENVIRONMENT';

  my @hostlist = $self->read_ccl_default("$pe_path/Parallel Host List",1);

  my $buildinfo = $self->get_resfile_data('S/BUILD_INFO', 'EVERY', 0);
  $buildinfo =~ s/,/\//g if defined ($buildinfo);

  # Specify the required data (hash value can take a variety of forms)
  my %data_items =
    ('Session.StartMethod'             => sub { $self->read_ccl_parm("$pe_path/Start Method") },
     'Session.NumberOfProcesses'       => sub { $self->read_ccl_default("$pe_path/Number of Processes",1) },
     'Session.HPCNumberOfComputeNodes' => scalar(@hostlist),
     'Session.WorkbenchSession'        => $self->is_wbrun() ? 'T' : 'F',
     'Session.ExecutionMode'           => $self->parent()->batch() ? 'batch' : 'interactive',
     'Session.TotalCpuTime'            => sub { $self->get_cpu_time() },
     'Application.Buildinfo'           => $buildinfo,
     'Mesh.Zones'                      => sub { $self->get_resfile_data('G/NZN', 'EVERY', 0) },
     'Mesh.Nodes'                      => sub { $self->get_domain_sum('G/NVX', 0) },
     'Mesh.Elements'                   => sub { $self->get_domain_sum('G/NEL', 0) },
     'Mesh.Faces'                      => sub { $self->get_domain_sum('G/NFC', 0) });

  # Data acquisition
  my %data;
  for my $key (keys %data_items)
  {
    my $ref_type = ref($data_items{$key});
    my $value;

    # Non-reference: Assume raw value to be used 'as is'
    if (! $ref_type) {
      $value = $data_items{$key};
    # Code reference: Assume a subroutine has been provided
    } elsif ($ref_type eq 'CODE') {
      my $func = $data_items{$key};
      $value = $func->();
    }

    $data{$key} = $value if (defined $value);
  }

  # Generate file
  my @lines;
  for my $key (sort keys %data)
  {
    push @lines, sprintf("%s,%s\n",$key,$data{$key});
  }

  my $workdir = $self->rundir_obj();
  return 0 unless (defined $workdir);

  my $apip_data_file = 'apip_data';
  my $file = $workdir->creatable_file($apip_data_file);
  return 0 unless (append_to_file($file, @lines));

  # Send data
  $workdir->enter();
  my @args = ('-version',$version,
              '-send', $apip_data_file);
  do_cmd_quiet($apip_helper_exe,@args);
  $workdir->leave();
  $workdir->remove_file($apip_data_file);
 
  return 1;
}

=head4 

  $self->get_resfile_data('G/NZN', 'EVERY', 0);

  Return the value of a dataset.

=cut

sub get_resfile_data {
  my $self = shift;
  my $res = join_file($self->rundir(), 'res');
  return undef unless (test_r $res);
  my @data = GetDataSet($res,@_);
  return undef unless (defined $data[0]);
  return wantarray ? @data : join(',', @data);
}

=head4 

  $self->get_domain_sum('G/NVX', 0);

  For single valued datasets specified by WHAT and WHEN, sum their values
  for all values of WHERE in the range ZN1:ZNn (i.e. all domains)

=cut

sub get_domain_sum {
  my $self = shift;
  my $what = shift;
  my $when = shift;
  my $ret = 0;
  my $nzn = $self->get_resfile_data('G/NZN', 'EVERY', 0);
  return undef unless (defined $nzn);
  for my $izn (1..$nzn) {
    my $where = sprintf "ZN%d", $izn;
    $ret += $self->get_resfile_data($what,$where,$when);
  }
  return $ret;
}

=head4 

  $self->read_ccl_default('/FLOW/ANALYSIS TYPE/Option', 'Steady State');

  Read a CCL parameter. If it isn't defined, return a specified default value.

=cut

sub read_ccl_default {
  my $self = shift;
  my $param = shift;
  my $default = shift;
  my $value = $self->read_ccl_parm($param);
  $value = $default unless (defined $value);
  return $value;
}

=head4 

  $self->get_cpu_time();

  Return the solver CPU time.

=cut

sub get_cpu_time {
  my $self = shift;
  my @transient = $self->get_resfile_data('G/TRANSIENT', 'ZN1', -2);
  return $transient[0][5];
}

1;
