package CFX5::Simulation::OptionHandling;

use CFX5::Config qw(%Config) ;
use CFX5::Output qw(_error);
use CFX5::PathManip qw(fullpath);

use strict;

my %handlers =  (

  #=============================================================================
  # General and Run Specification
  '-bak-elapsed-time' => sub {
      my $job = shift;
      my $value = shift;
      $job->bak_elapsed_time($value) ;
  },
  '-continue-from-configuration' => sub {
      my $job=shift;
      my $value=shift;
      $job->AddIniValsObj($value,"Configuration Results",1,"Solver Input File");
  },
  '-continue-from-file' => sub {
      my $job=shift;
      my $value=shift;
      $job->AddIniValsObj($value,"Results File",1,"Solver Input File");
  },
  '-debug-prompt' => sub {
      my $job=shift;
      $job->solver_debug_prompt(1);
  },
  '-debug-prompt-user' => sub {
      my $job=shift;
      $job->solver_debug_prompt_user(1);
  },
  '-output-summary-option' => sub {
    my $job=shift;
    my $value=shift;
    $job->output_summary_option($value);
  },
  '-debug-dummy-solver' => sub {
      my $job=shift;
      $job->lets_pretend(1);
  },
  '-fullname' => sub {
    my $job=shift;
    my $value=shift;
    $job->fullname(fullpath($value));
    # if there's already an output file associated with this job
    # name, let's open it, so that any further messages can be copied
    # to it.  This is used by cfx5extsolve, to make sure we get
    # errors back.
    if (-f $job->outfile()) {
      $job->StartOutFile();
    }
  }, 
  '-initial' => sub {
      my $job=shift;
      my $value=shift;
      # Do not continue history if previous CCL has:
      # - Mesh from solver input file
      # - 'Continue History From' not set
      my $cont_from = 1;
      if (defined $job->use_mesh_from()) {
        $cont_from = 0 if ($job->use_mesh_from() eq 'Solver Input File' and
                           not defined $job->cont_hist_from());
      }
      $job->DelIniValsObjs(); 
      $job->AddIniValsObj($value,"Results File",$cont_from,"Initial Values");
  },
  '-initial-configuration' => sub {
      my $job=shift;
      my $values=shift;
      foreach my $value (@{$values}) {
        $job->AddIniValsObj($value,"Configuration Results",0,"Solver Input File");
      }
  },
  '-initial-file' => sub {
      my $job=shift;
      my $values=shift;
      foreach my $value (@{$values}) {
        $job->AddIniValsObj($value,"Results File",0,"Solver Input File");
      }
  },
  '-interpolate-iv' => sub {
      my $job=shift;
      $job->use_iv_mesh('');
      $job->dont_continue_history() ; 
  },
  '-interrupt-after' => sub {
     my $job=shift;
     my $value=shift; 
     $job->interrupt_after($value);
     $job->save_workdir(1);
  },
  '-job' => sub {
      my $job=shift;
      $job->save_jobfile(1);
  },
  '-job-part' => sub {
      my $job=shift;
      $job->save_pjobfile(1);
  },
  '-max-elapsed-time' => sub {
      my $job = shift;
      my $value = shift;
      $job->max_elapsed_time($value) ;
  },
  '-name' => sub {
      my $job=shift;
      my $value=shift;
      $job->name(fullpath($value));
  },
  '-norun' => sub {
     my $job=shift;
     $job->interrupt_after("preparedef");
     $job->save_workdir(1);
     $job->{parent}->norun(1);
  },
  '-save' => sub {
    my $job=shift;
    $job->save_workdir(1);
  },
  '-preferred-license' => sub { 
    my $job=shift;
    my $value=shift;
    $job->preferred_license($value);
  },
  '-respect-suffix-history' => sub {
    my $job=shift;
    $job->respect_suffixhist(1);
  },
  '-use-mesh-from-iv' => sub {
    my $job=shift;
    $job->use_iv_mesh(1);
  },
  
  #===========================================================================
  # Execution options
  '-first-half' => sub {
    my $job=shift;
    if ($job->analysis_type() eq 'Transient') {
      # Transient. Try some duration settings..
      if (defined $job->time_duration()) {
        my $newduration;
        if (defined $job->timesteps()) {
          my $tstep = $job->timesteps();
          $newduration=$tstep.'*int(0.5*'.$job->time_duration().'/'.$tstep.')';
        } else {
          $newduration='0.5*'.$job->time_duration();
        }
        $job->time_duration($newduration);
      } elsif (defined $job->num_timesteps_per_run()) {
        my $maxsteps=$job->num_timesteps_per_run();
        my $newsteps=int(0.5*($maxsteps+1));
        if ($newsteps == 0) {
          _error("Not enough timesteps for first half of transient run.");
        }
        $job->num_timesteps_per_run($newsteps);
      } elsif (defined $job->time_per_run()) {
        my $newduration='0.5*'.$job->time_per_run();
        $job->time_per_run($newduration);
      } else {
        _error("Cannot start first half of transient run.");
      }
    } elsif ($job->analysis_type() eq 'Steady State') {
      # Steady-State
      my $maxnumits=$job->max_num_its();
      my $numits=int(0.5*($maxnumits+1));
      if ($numits == 0) {
        _error("Cannot start first half of run.");
      }
      $job->max_num_its($numits);
    } else {
      _error("Restart regression for analysis type currently not supported!");
    }
  },
  '-priority' => sub {
    my $job=shift;
    my $value=shift;
    if ($value =~ m!^[01234]$!) {
      # 'Idle' is also accepted, but has no numeric equivalent, as
      # I'm trying to deprecate the numerics!
      my %map=(0 => 'Idle',
               1 => 'Low',
               2 => 'Standard',
               3 => 'High',
               4 => 'High',
              );
      $value=$map{$value};
    }
    $job->solver_priority($value);
  },
  '-second-half' => sub {
    my $job=shift;
    if ($job->analysis_type() eq 'Transient') {
      # Transient
      if (defined $job->time_duration()) {
        # do nothing. Original end time is correct!
      } elsif (defined $job->num_timesteps_per_run()) {
        my $maxsteps=$job->num_timesteps_per_run();
        my $newsteps=$maxsteps - int(0.5*($maxsteps+1));
        if ($newsteps == 0) {
          _error("Not enough timesteps for second half of transient run.");
        }
       $job->num_timesteps_per_run($newsteps);
      } elsif (defined $job->time_per_run()) {
        my $newduration='0.5*'.$job->time_per_run();
        $job->time_per_run($newduration);
      } else {
        _error("Cannot start second half of transient run.");
      }
    } elsif ($job->analysis_type() eq 'Steady State') {
      # Steady-State
      my $maxnumits=$job->max_num_its();
      my $numits=$maxnumits - int(0.5*($maxnumits+1));
      if ($numits == 0) {
        _error("Cannot start second half of run.");
      }
      $job->max_num_its($numits);
    } else {
      _error("Restart regression for analysis type currently not supported!");
    }
    $job->interpolate_iv_int('no');
  },
  '-size' => sub {
    my $job=shift;
    my $value=shift;
    $job->memory_factor($value);
  },
  '-size-nc' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_nc($value);
  },
  '-size-nd' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_nd($value);
  },
  '-size-ni' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_ni($value);
  },
  '-size-nl' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_nl($value);
  },
  '-size-nr' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_nr($value);
  },
  '-size-cat' => sub {
    my $job=shift;
    my $value=shift;
    $job->cat_factor($value);
  },
  '-size-mms' => sub {
    my $job=shift;
    my $value=shift;
    $value=~s/x//g;
    $job->cat_factor($value."x");
  },
  '-size-cclsetup' => sub {
    my $job=shift;
    my $value=shift;
    $job->memory_factor_cclsetup($value);
  },
  '-size-cclsetup-nc' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_cclsetup_nc($value);
  },
  '-size-cclsetup-nd' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_cclsetup_nd($value);
  },
  '-size-cclsetup-ni' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_cclsetup_ni($value);
  },
  '-size-cclsetup-nl' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_cclsetup_nl($value);
  },
  '-size-cclsetup-nr' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_cclsetup_nr($value);
  },
  '-size-cclsetup-cat' => sub {
    my $job=shift;
    my $value=shift;
    $job->cat_factor_cclsetup($value);
  },
  '-size-interp' => sub {
    my $job=shift;
    my $value=shift;
    $job->memory_factor_interp($value);
  },
  '-size-interp-nc' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_interp_nc($value);
  },
  '-size-interp-nd' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_interp_nd($value);
  },
  '-size-interp-ni' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_interp_ni($value);
  },
  '-size-interp-nl' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_interp_nl($value);
  },
  '-size-interp-nr' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_interp_nr($value);
  },
  '-size-interp-cat' => sub {
    my $job=shift;
    my $value=shift;
    $job->cat_factor_interp($value);
  },
  '-size-part' => sub {
    my $job=shift;
    my $value=shift;
    $job->memory_factor_part($value);
  },
  '-size-part-nc' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_part_nc($value);
  },
  '-size-part-nd' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_part_nd($value);
  },
  '-size-part-ni' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_part_ni($value);
  },
  '-size-part-nl' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_part_nl($value);
  },
  '-size-part-nr' => sub {
    my $job=shift;
    my $value=shift;
    $job->size_part_nr($value);
  },
  '-size-part-cat' => sub {
    my $job=shift;
    my $value=shift;
    $job->cat_factor_part($value);
  },
  '-size-part-mms' => sub {
    my $job=shift;
    my $value=shift;
    $value=~s/x//g;
    $job->cat_factor_part($value."x");
  },
  '-numa' => sub {
    my $job=shift;
    my $value=shift;
    my %map = ('none' => 'None',
               'auto' => 'Automatic');
    my $option = $map{lc($value)};
    if (defined $option) {
      $job->numa($option);
    } else {
      _error("Invalid option $value passed to -numa");
    }
  },
  '-affinity' => sub {
    my $job=shift;
    my $value=shift;
    my %map = ('implicit' => 'Implicit',
               'explicit' => 'Explicit');
    my $option = $map{lc($value)};
    if (defined $option) {
      $job->affinity($option);
    } else {
      _error("Invalid option $value passed to -affinity");
    }
  },

  #=============================================================================
  # Partitioning
  '-part' => sub {
    my $job=shift; 
    my $value=shift;

    unless ($value =~ m!^\d+$!) {
      _error("Argument \"$value\" to -part must be an integer.");
    }
    elsif ($value >= 2) {
      $job->partitions($value);
      # if we're still set to "serial" run, it means we might be
      # doing a partition-only run.  If any of the -par* flags have
      # been passed this will be set to something else.
      $job->run_mode('Partitioner Only') if $job->start_method() eq 'Serial';
    } else {
      _error("You cannot do a parallel run with less than two partitions")
    }
    # Remove initial partition file if defined in CCL
    $job->delete_ccl_parm('/SIMULATION CONTROL/EXECUTION CONTROL/RUN DEFINITION/Initial Partition File') if $job->have_ccl_parm('/SIMULATION CONTROL/EXECUTION CONTROL/RUN DEFINITION/Initial Partition File');
  },
  '-part-only' => sub {
    my $job=shift; 
    my $value=shift;

    unless ($value =~ m!^\d+$!) {
      _error("Argument \"$value\" to -part-only must be an integer.");
    }
    elsif ($value >= 2) {
      $job->partitions($value);
      $job->run_mode('Partitioner Only') ;
      $job->ensure_ccl_rules_loaded() unless $Config{CFX5_SOLVE_LAX_CCL};
      $job->ppe_obj()->start_method('Serial');
    }
  },
  '-parfile-read' => sub {
    my $job=shift;
    my $value=shift;
    $job->parfile_in($value);
  },
  '-parfile-write' => sub {
    my $job=shift;
    my $value=shift;
    $job->parfile_out($value);
    $job->save_parfile(1);
  },
  '-parfile-save' => sub {
    my $job=shift;
    $job->save_parfile(1);
  },
  "-part-coupled" => sub { 
    my $job=shift;
    $job->multidomain_partitioning_option('Coupled Partitioning');
  },
  "-part-independent" => sub { 
    my $job=shift;
    $job->multidomain_partitioning_option('Independent Partitioning');
  },
  '-part-mode' => sub {
    my $job=shift;
    my $value=shift;
    my %map=(
             'metis-kway'                => 'MeTiS/k-way',
             'metis-rec'                 => 'MeTiS/recursive bisection',
              simple                     => 'Simple Assignment',
              rcb                        => 'Recursive Coordinate Bisection',
              drcb                       => 'Directional Recursive Coordinate Bisection',
              orcb                       => 'Optimized Recursive Coordinate Bisection',
              usd                        => 'User Specified Direction',
             'metis/k-way'               => 'MeTiS/k-way',
             'metis/recursive bisection' => 'MeTiS/recursive bisection',
             'recursive coordinate bisection','Recursive Coordinate Bisection',
             'directional recursive coordinate bisection','Directional Recursive Coordinate Bisection',
             'optimized recursive coordinate bisection','Optimized Recursive Coordinate Bisection',
             'user specified direction','User Specified Direction',
             'user partitioning routine','User Partitioning Routine',
            );
    my $mode=$map{lc($value)};
    if (defined $mode) {
      my ($m,$t)=split('/',$mode);
      $job->part_mode($m);
      if ($t) {
        $job->part_metis_type($t);
      }
      my $cclPath = '/SIMULATION CONTROL/EXECUTION CONTROL/PARTITIONER STEP CONTROL/PARTITIONING TYPE';
      $job->delete_ccl_obj("$cclPath/AXIS DEFINITION");
      $job->delete_invalid_params($cclPath);
    } else {
      _error("Invalid partitioning mode $value passed to -part-mode");
    }
  },

  #=============================================================================
  # Parallel
  '-par' => sub {
     my $job=shift;
     $job->parallel_mode('Distributed Parallel');
     $job->run_mode('Full');
  },
  '-par-local' => sub {
     my $job=shift;
     $job->parallel_mode('Local Parallel');
     $job->run_mode('Full');
  },
  '-par-dist' => sub {
     my $job=shift;
     my $value=shift;
     $job->parallel_mode('Distributed Parallel');
     $job->host_list($value);
     $job->run_mode('Full');
  },
  "-par-host-list" => sub {
     my $job=shift;
     my $value=shift;
     $job->hstlist($value);
  },
  "-serial" => sub {
     my $job=shift;
     # Note: No parallel compatibility checking required for serial
     # hence start_method() is called instead of parallel_mode()
     $job->ensure_ccl_rules_loaded() unless $Config{CFX5_SOLVE_LAX_CCL};
     $job->ppe_obj()->start_method('Serial');
     $job->partitions(1);
     $job->run_mode('Full');
  },
  "-start-method" => sub {
     my $job=shift;
     my $value=shift;
     # before creating the start method object, ensure the RULES are
     # loaded.  It will probably fail later anyway, as if they are
     # still not loaded that probably means the user forgot to give a
     # .def file.  But this way, the errors are clearer!
     $job->ensure_ccl_rules_loaded()
       unless $Config{CFX5_SOLVE_LAX_CCL};
     $job->ppe_obj()->start_method($value);
     # now we will do a full run, even if we thought we might only be
     # partitioning
     $job->run_mode('Full');
  },

  #=============================================================================
  # Executable selection
  '-single' => sub {
     my $job=shift;
     $job->global_exec_set_attr('single'); 
  },
  '-ccl2flow-single' => sub {
     my $job=shift;
     $job->ccl2flow_exec_set_attr('single'); 
  },
  '-cclsetup-single' => sub {
     my $job=shift;
     $job->cclsetup_exec_set_attr('single'); 
  },
  '-part-single' => sub {
     my $job=shift;
     $job->partitioner_exec_set_attr('single'); 
  },
  '-interp-single' => sub {
     my $job=shift;
     $job->interpolator_exec_set_attr('single'); 
  },
  '-solver-single' => sub {
     my $job=shift;
     $job->solver_exec_set_attr('single'); 
  },
  '-large' => sub {
     my $job=shift;
     $job->global_exec_set_attr('int64'); 
  },
  '-part-large' => sub {
     my $job=shift;
     $job->partitioner_exec_set_attr('int64');
  },
  '-interp-large' => sub {
     my $job=shift;
     $job->interpolator_exec_set_attr('int64');
  },
  '-solver-large' => sub {
     my $job=shift;
     $job->solver_exec_set_attr('int64'); 
  },
  '-double' => sub {
     my $job=shift;
     $job->global_exec_set_attr('double');
  },
  '-ccl2flow-double' => sub {
     my $job=shift;
     $job->ccl2flow_exec_set_attr('double'); 
  },
  '-cclsetup-double' => sub {
     my $job=shift;
     $job->cclsetup_exec_set_attr('double'); 
  },
  '-part-double' => sub {
     my $job=shift;
     $job->partitioner_exec_set_attr('double');
  },
  '-interp-double' => sub {
     my $job=shift;
     $job->interpolator_exec_set_attr('double');
  },
  '-solver-double' => sub {
     my $job=shift;
     $job->solver_exec_set_attr('double');
  },
  '-ccl2flow' => sub {
    my $job=shift;
    my $value=shift;
    $job->ccl2flow_exec(fullpath($value));
  },
  '-cclsetup' => sub {
    my $job=shift;
    my $value=shift;
    $job->cclsetup_exec(fullpath($value));
  },
  '-partitioner' => sub {
    my $job=shift;
    my $value=shift;
    $job->partitioner_exec(fullpath($value));
  },
  '-interpolator' => sub {
    my $job=shift;
    my $value=shift;
    $job->interpolator_exec(fullpath($value));
  },
  '-solver' => sub {
    my $job=shift;
    my $value=shift;
    my @entries=split(/,/, $value);
    foreach my $entry (@entries){
      my @vals=split(/=/, $entry);
      if(scalar(@vals)==1){
        unshift @vals, $Config{CFX5OS};
      }
      $job->solver_exec($vals[0], fullpath($vals[1]));
    }
  },
  #=============================================================================
  # ANSYS System Coupling
  '-scport' => sub { 
    my $job=shift;
    my $value=shift;
    $job->scport($value);
  },
  '-schost' => sub { 
    my $job=shift;
    my $value=shift;
    $job->schost($value);
  },
  '-scname' => sub { 
    my $job=shift;
    my $value=shift;
    $job->scname($value);
  },
  '-sclic' => sub { 
    my $job=shift;
    my $value=shift;
    $job->sclic($value);
  },
  '-scid' => sub { 
    my $job=shift;
    my $value=shift;
    $job->scid($value);
  },

  #=============================================================================
  # ANSYS Multifield and process coupling
  "-ansys-arguments" => sub {
    my $job=shift;
    my $value=shift;
    if( not $job->is_mfx_run() ) {
      _error("Multifield case not detected, -ansys-arguments not valid");
    }
    $job->ansys_args($value);
  },
  "-ansys-input" => sub {
    my $job=shift;
    my $value=shift;
    if( not $job->is_mfx_run() ) {
    	_error("Multifield case not detected, -ansys-input not valid");
    }
    $job->ansys_input(fullpath($value));
  },
  "-ansys-installation" => sub {
    my $job=shift;
    my $value=shift;
    if( not $job->is_mfx_run() ) {
      _error("Multifield case not detected, -ansys-installation not valid");
    }
    $job->ansys_install_root($value);
  },
  "-ansys-jobname" => sub {
    my $job=shift;
    my $value=shift;
    if( not $job->is_mfx_run() ) {
      _error("Multifield case not detected, -ansys-jobname not valid");
    }
    $job->ansys_jobname($value);
  },
  "-ansys-license" => sub {
    my $job=shift;
    my $value=shift;
    if( not $job->is_mfx_run() ) {
      _error("Multifield case not detected, -ansys-license not valid");
    }
    $job->ansys_license($value);
  },
  "-ansys-restart" => sub {
    my $job=shift;
    my $value=shift;
    if( not $job->is_mfx_run() ) {
      _error("Multifield case not detected, -ansys-restart not valid");
    }
    $job->mfx_restart_db(fullpath($value));
    $job->mfx_restart('On');
  },
  '-ansys-input-is-complete' => sub {
    my $job=shift;
    if( not $job->is_mfx_run() ) {
      _error("Multifield case not detected, -ansys-input-is-complete not valid");
    }
    $job->mfx_process_input('Off');
  },
  '-cplg-host' => sub {
    my $job=shift;
    my $value=shift;
    if ($value =~ m!^([0-9]+)@(.+)!) {
      my $port=$1;
      my $host=$2;
      $job->ansys_port($port);
      $job->ansys_host($host);
    } else {
      _error("Bad option \"$value\" for argument -cplg-host: the option must be of the form port\@host");
    }
    # This implies that ANSYS is already running, so don't try to start it
    $job->mfx_run_mode('Start CFX only');
  },
  "-mfx-run-mode" => sub {
    my $job=shift;
    my $value=shift;
    $job->mfx_run_mode($value);
  },
  '-cplg-master' => sub { 
    my $job=shift;
    my $value=shift;
    $job->cplg_name($value);
  },
);

sub handle {
  my $job=shift;
  my $option=shift;
  my $value=shift;
  
  my $ref=$handlers{$option};
  die "Option handler not found for $option" if not $ref;
  return &$ref($job, $value);
}
