#!/usr/bin/env perl

# Copyright (C) 2022  Light and Molecules Group

# 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 3 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, see <https://www.gnu.org/licenses/>.

use strict;
use warnings;
use diagnostics;

use lib join('/', $ENV{NXHOME}, 'perllib');
use lib join('/', $ENV{NXHOME}, '/share/newtonx/perllib');
use NX::Configuration;
use NX::InputGenerator::Menu;
use NX::InputGenerator::Item;
use NX::InputGenerator::SetValues;
use NX::InputGenerator::CheckValues;
use NX::InputGenerator::Utils qw( nxinp_set_parameter ask_y_or_n generate_internal_coords parse_internal_coords );
use NX::Tools qw( prog_name units prog_hash_from_name );

use File::Copy::Recursive qw ( fcopy dircopy );

use FindBin;
use YAML::Tiny;

use Data::Dumper qw( Dumper );

my $license = <<"END_MESSAGE";
nxinp  Copyright (C) 2022  Light and Molecules Group
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions (see GPLv3).

END_MESSAGE

my $nx_title = <<"END_MESSAGE";
\t============================================================
\t                       NEWTON-X NS 3
\t         Newtonian dynamics close to the crossing seam
\t                       www.newtonx.org
\t============================================================

\t -----------------------------------------------------------
\t WARNING:
\t This input generator is to be deprecated !!
\t The way to reliably generate an input is the following:
\t\t 1. Set up the basic configuration here (option 1) ;
\t\t 2. Save 'user_config.nml' ;
\t\t 3. Generate the full input with a dry-run from 'nx_moldyn':
\t\t\t  nx_moldyn --dry-run
\t\t 4. Modify the file 'nx_exported_config.nml' as you want
\t -----------------------------------------------------------
END_MESSAGE

# Starting default configuration loaded from YAML file
# my $default_config_file = "$FindBin::Bin/../data/default.yml";
my $nxhome = $ENV{NXHOME};
my $default_config_file = "$nxhome/data/default.yml";
if (-d "$nxhome/share/newtonx/") {
    $default_config_file = "$nxhome/share/newtonx/data/default.yml";
}
my $config = NX::Configuration->new($default_config_file);

# Main menu of nxinp
my $main_title = $license.$nx_title."\t\t\tMAIN MENU";
my $main_menu = NX::InputGenerator::Menu->new($main_title);
$main_menu->exit_option(0);

# Set basic options
my $menu_basic_title = $nx_title."\t\t\tSET BASIC OPTIONS";
my $menu_basic = NX::InputGenerator::Menu->new($menu_basic_title);

# Set general options
my $menu_general_title = $nx_title."\t\t\tSET GENERAL OPTIONS";
my $menu_general = NX::InputGenerator::Menu->new($menu_general_title);
$menu_general->exit_option(0); # Not possible to exit program from this menu

# Set Nonadiabatic dynamics
my $menu_nad_title = $nx_title."\t\t\tSET NONADIABATIC DYNAMICS";
my $menu_nad = NX::InputGenerator::Menu->new($menu_nad_title);
$menu_nad->exit_option(0);

# Set adaptive time-step
my $menu_adt_title = $nx_title."\t\t\tSET ADAPTIVE TIME STEP";
my $menu_adt = NX::InputGenerator::Menu->new($menu_adt_title);
$menu_adt->exit_option(0);

# Set creation of trajectories
my $menu_traj_title = $nx_title."\t\t\tSET TRAJECTORIES CREATION";
my $menu_traj = NX::InputGenerator::Menu->new($menu_traj_title);
$menu_traj->exit_option(1);

# Set H5MD parameters
my $menu_h5md_title = $nx_title."\t\t\tSET H5MD OUTPUT FILE";
my $menu_h5md = NX::InputGenerator::Menu->new($menu_traj_title);
$menu_h5md->exit_option(0);

# This hash stores the sections that have been modified by the user, and is used for
# creating 'user_dynamics.ini'. This ALWAYS include the 'dynamics' section, as no computation
# can work without it, and because this section will be checked by all parts of the input
# generator.
my %modification_tracker = ( has_read_configuration => 0,
			     has_basic => [ 0 ],
			     has_nxconfig => [ 0 ],
			     has_zpe_correction => [ 0 ],
			     has_thermostat => [ 0 ],
			     has_analytical => [ 0 ],
			     has_sbh => [ 0 ],
			     has_recohmod => [ 0 ],
			     has_onedim => [ 0 ],
			     has_turbomole => [ 0 ],
			     has_columbus => [ 0 ],
			     has_g09 => [ 0 ],
			     has_gaussian => [ 0 ],
			     has_mopac => [ 0 ],
			     has_exc_gaussian => [ 0 ],
			     has_exc_mopac => [ 0 ],
			     has_exc_inp => [ 0 ],
			     has_tinker_mndo => [ 0 ],
			     has_tinker_g16 => [ 0 ],
			     has_dftb => [ 0 ],
			     has_sh => [ 0 ],
			     has_cioverlap => [ 0 ],
			     has_auxnac => [ 0 ],
			     has_nad_setup => [ 0 ],
			     has_adapt_dt => [ 0 ],
			     has_generate_traj => [ 0 ],
			     has_h5md => [ 0 ],
			   );

# Prepare main menu
# my $main_item_initcond = NX::InputGenerator::Item->new('Set initial conditions', sub {&set_initial_conditions()});
my $main_item_basic = NX::InputGenerator::Item->new('Set basic input',
					   sub {
					     $menu_basic->print();
					     &set_basic_input($config, \%modification_tracker)
					   });
my $main_item_general = NX::InputGenerator::Item->new('Set general options',
					     sub {
					       if (check_basic_configuration($config, \%modification_tracker)) {
						 $menu_general->print();
					      }
					     });
my $main_item_nad = NX::InputGenerator::Item->new('Set Nonadiabatic dynamics',
					 sub {
					   if (check_basic_configuration($config, \%modification_tracker)) {
					     $menu_nad->print();
					   }
					 });

my $main_item_adt = NX::InputGenerator::Item->new('Set adaptive time step options',
						  sub {
						    if (check_basic_configuration($config, \%modification_tracker)) {
						      set_adt($config, \%modification_tracker);
						    }
						  });

my $main_item_traj = NX::InputGenerator::Item->new('Generate trajectories',
						  sub {
						    if (check_basic_configuration($config, \%modification_tracker)) {
						      set_traj($config, \%modification_tracker);
						    }
						   });

my $main_item_h5md = NX::InputGenerator::Item->new('Set H5MD output parameters',
						  sub {
						    if (check_basic_configuration($config, \%modification_tracker)) {
						      set_h5md($config, \%modification_tracker);
						    }
						   });

# my $main_item_traj = NX::InputGenerator::Item->new('Set trajectory', sub {&set_traj()});
# my $main_item_stats = NX::InputGenerator::Item->new('Set statistical analysis', sub {&set_stats()});
my $main_item_exit = NX::InputGenerator::Item->new('Save current configuration and exit', sub {&save_config($config, \%modification_tracker); exit;});
my @main_menu_items = [
		       # $main_item_initcond,
		       $main_item_basic,
		       $main_item_general,
		       $main_item_nad,
		       $main_item_adt,
		       $main_item_traj,
		       # $main_item_stats,
		       $main_item_h5md,
		       $main_item_exit,
		      ];
$main_menu->add(@main_menu_items);

# Prepare menu for general options
my $general_item_dynamics = NX::InputGenerator::Item->new('Set general dynamics parameters',
						 sub {&set_general_dynamics($config)});
my $general_item_thermostat = NX::InputGenerator::Item->new('Set thermostat',
						   sub {&set_general_thermostat($config, \%modification_tracker)});
my $general_item_model_or_qm = NX::InputGenerator::Item->new('Set electronic structure computation parameters',
						   sub {&set_general_model_or_qm($config)});
my $general_item_return = NX::InputGenerator::Item->new('Return to main menu',
					       sub {$main_menu->print()});
my @menu_general_items = [$general_item_dynamics,
			  $general_item_thermostat,
			  $general_item_model_or_qm,
			  $general_item_return,
			 ];
$menu_general->add(@menu_general_items);

# Prepare menu for NAD options
my $nad_item_tdse_sh = NX::InputGenerator::Item->new('Set TDSE integration method and Surface Hopping algorithm',
					    sub {&set_nad_tdse_sh($config)});
my $nad_item_compute_nac = NX::InputGenerator::Item->new('Define which non-adiabatic couplings (NAC) will be computed',
						sub {&set_nad_compute_nac($config)});
my $nad_item_how_nac = NX::InputGenerator::Item->new('Define how to compute time-derivative couplings',
					    sub {&set_nad_how_nac($config)});
my $nad_item_return = NX::InputGenerator::Item->new('Return to main menu',
					  sub {$main_menu->print()});
my @menu_nad_items = [$nad_item_tdse_sh,
		      $nad_item_compute_nac,
		      $nad_item_how_nac,
		      $nad_item_return,
		     ];
$menu_nad->add(@menu_nad_items);

# Prepare menu for adaptive time step
# my $adt_item = NX::InputGenerator::Item->new('Set options for adaptive time-step algorithm',
#					     sub {&set_adt($config, \%modification_tracker)});
# $menu_adt->add([$adt_item]);

$main_menu->print();


sub check_basic_configuration {
  my ($configuration, $modification_tracker) = @_;

  # Check wether:
  # 1. If has_basic_dynamics is set to 1, i.e. the basic configuration has been done
  # 2. If a configuration file is found in the working directory with
  #    basic directives
  # If not, ask the user to set-up a basic input first.
  my $basic_has_been_done = 0;
  if ($modification_tracker{has_basic}[0] == 1) {
    $basic_has_been_done = 1;
  }

  elsif (-s 'user_config.nml' && $modification_tracker{has_read_configuration} == 0) {
    my $question = "An input file has been found, would you want to read it";
    my $answer = ask_y_or_n($question);
    if ($answer) {
      read_user_configuration($configuration, 'user_config.nml', $modification_tracker);
      $modification_tracker{has_read_configuration} = 1;
    }
    if (@{$modification_tracker{has_basic}}[0] == 1) {
      $basic_has_been_done = 1;
    }
  }

  if ($basic_has_been_done) {
    return 1;
  } else {
    print STDOUT " Please set up the basic options first !!\n\n";
    print STDOUT " Press any key to go back to Main Menu\n";
    $_ = <STDIN>;
    return 0;
  }
}


sub read_user_configuration {
  my ($configuration, $file_name, $modification_tracker) = @_;
  my $result = $configuration->import_nml($file_name);

  foreach my $sec (keys(%$result)) {
    if ("$sec" eq 'nxconfig') {
	@{$modification_tracker{has_basic}}[0] = 1;

	my @tmp = @{$result->{nxconfig}};
	if (grep /progname/, @tmp) {
	    my $progname = $configuration->value('nxconfig', 'progname');
	    my $methodname = $configuration->value('nxconfig', 'methodname');

	    my $prog = prog_hash_from_name( $progname, $methodname );
	    $configuration->value('nxconfig', 'prog', $prog);
	}
    }

    if ("$sec" eq 'sbh' or
	"$sec" eq 'recohmod' or
	"$sec" eq 'tully_mod') {
	@{$modification_tracker{has_analytical}}[0] = 1;
    }
    my $has_key = "has_".$sec;
    @{$modification_tracker{$has_key}}[0] = 1;
    push @{$modification_tracker{$has_key}}, @{%$result{$sec}};
  }

  generate_missing_defaults($configuration, 'nxconfig', $result);

  return 0;
}


sub set_basic_input {
  my ($configuration, $modification_tracker) = @_;

  if (-s 'user_config.nml' && $modification_tracker{has_read_configuration} == 0) {
    my $question = "Would you want to read old input file";
    my $answer = ask_y_or_n($question);
    if ($answer) {
      read_user_configuration($configuration, 'user_config.nml', $modification_tracker);
    }
  }

  my $section = 'nxconfig';
  my @parameters_to_modify = (
			      'nat',
			      'nstat',
			      'nstatdyn',
			      'dt',
			      'tmax',
			     );

  foreach my $parameter (@parameters_to_modify) {
      nxinp_set_parameter($configuration, $section, $parameter);
      push @{$modification_tracker{has_nxconfig}}, $parameter
	  if not grep /$parameter/, @{$modification_tracker{has_nxconfig}};
  }

  nxinp_set_parameter($configuration, $section, 'prog');
  my $prog = $configuration->value('nxconfig', 'prog');
  my ($progname, $methodname) = prog_name($prog);
  push @{$modification_tracker{has_nxconfig}}, 'progname'
      if not grep /progname/, @{$modification_tracker{has_nxconfig}};
  push @{$modification_tracker{has_nxconfig}}, 'methodname'
      if not grep /methodname/, @{$modification_tracker{has_nxconfig}};

  # Set the derivative couplings program
  nxinp_set_parameter($configuration, $section, 'dc_method');
  push @{$modification_tracker{has_nxconfig}}, 'dc_method'
      if not grep /dc_method/, @{$modification_tracker{has_nxconfig}};

  my $question = "Would you like to generate internal coordinate file";
  my $answer = ask_y_or_n($question);
  if ($answer) {
      generate_internal_coords($configuration);
  }

  # Ask for ZPE correction
  $question = "Do you want to use ZPE correction";
  my $new_answer = ask_y_or_n($question);
  if ($new_answer) {
      $section = 'zpe_correction';
      my @parameters = (
	  'kcheck',
	  'kmodel',
	  'tcheck',
	  'tcycle',
	  'zthresh',
	  );

      foreach my $parameter (@parameters) {
	  nxinp_set_parameter($configuration, $section, $parameter);
	  push @{$modification_tracker{has_zpe_correction}}, $parameter
	      if not grep /$parameter/, @{$modification_tracker{has_zpe_correction}};
      }
      @{$modification_tracker{has_zpe_correction}}[0] = 1;
  }
  

  @{$modification_tracker{has_basic}}[0] = 1;

  print STDOUT "\nThe basic configuration is now finished.\n";
  print STDOUT "Press any key to go back to the main menu.\n";
  $_ = <STDIN>;

  return 0;
}


sub set_general_dynamics {

  my ($configuration, $modification_tracker) = @_;
  my @parameters_to_modify = ( 'killstat', 'timekill', 'lvprt', 'save_cwd', 'kt',
			       'etot_jump', 'etot_drift', 'thrs_cos', 'thrs_norm',
			     );

  my $section = 'nxconfig';
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_nxconfig}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_nxconfig}};
  }

  @{$modification_tracker{has_nxconfig}}[0] = 1;

  print STDOUT "\nThe general configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}


sub set_adt {
  my ($configuration, $modification_tracker) = @_;

  my $section = 'adapt_dt';

  my @parameters_to_modify = ( 'model', 'ratio', 'max_subtraj');
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_adapt_dt}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_adapt_dt}};
  }

  @{$modification_tracker{has_adapt_dt}}[0] = 1;
  print STDOUT "\nThe thermostat configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}


sub set_general_thermostat {
  my ($configuration, $modification_tracker) = @_;

  my $question = "Do you want to set up a thermostat ?";
  my $answer = ask_y_or_n($question);
  if (! $answer) {
    return 0;
  }

  my $section = 'thermostat';

  # Set up immediately the ktherm value !!
  $configuration->value($section, 'ktherm', 1);

  my @parameters_to_modify = ( 'ktherm', 'kts', 'lts', 'nstherm', 'temp',
			       'gamma', 'iseed', 'lvp'
			     );
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_thermostat}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_thermostat}};
  }

  @{$modification_tracker{has_thermostat}}[0] = 1;

  print STDOUT "\nThe thermostat configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}


sub set_general_model_or_qm {
    my ($configuration, $modification_tracker) = @_;

    my $program = $configuration->value('nxconfig', 'prog');
    my ($name, $dum) = prog_name($program);

    if ($name eq 'analytical') {
	set_general_analytical($configuration, $modification_tracker);
    } elsif ($name eq 'columbus') {
	set_general_columbus($configuration, $modification_tracker);
    } elsif ($name eq 'turbomole') {
	set_general_turbomole($configuration, $modification_tracker);
    } elsif ($name eq 'gau') {
	set_general_gau($configuration, $modification_tracker);
    } elsif ($name eq 'g09') {
	set_general_g09($configuration, $modification_tracker);
    } elsif ($name eq 'gaussian') {
	set_general_gaussian($configuration, $modification_tracker);
    } elsif ($name eq 'tinker_mndo') {
	set_general_tinker_mndo($configuration, $modification_tracker);
    } elsif ($name eq 'tinker_g16mmp') {
	set_general_tinker_g16($configuration, $modification_tracker);
    } elsif ($name eq 'mopac') {
	set_general_mopac($configuration, $modification_tracker);
    } elsif ($name eq 'exc_mopac') {
	set_general_exc_mopac($configuration, $modification_tracker);
    } elsif ($name eq 'exc_gaussian') {
	set_general_exc_gaussian($configuration, $modification_tracker);
    }
}


sub set_general_analytical {
  my ($configuration, $modification_tracker) = @_;

  print STDOUT "\nSet-up parameters for Analytical Model\n\n";
  my $section = 'analytical';
  my @parameters_to_modify = ( 'anmod' );
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_analytical}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_analytical}};
  }

  my $model = $configuration->value($section, 'anmod');
  if ($model eq 'tully_models.pl') {
    nxinp_set_parameter($configuration, $section, 'tully_mod');
    push @parameters_to_modify, 'tully_mod';
    push @{$modification_tracker{has_analytical}}, 'tully_mod'
      if not grep /tully_mod/, @{$modification_tracker{has_analytical}};
  }

  if ($model == 2) {
    $section = 'sbh';
    @{$modification_tracker{has_sbh}}[0] = 1;
    @parameters_to_modify = ('e0', 'v0', 'n', 'jw');
    foreach my $parameter (@parameters_to_modify) {
      nxinp_set_parameter($configuration, $section, $parameter);
      push @{$modification_tracker{has_sbh}}, $parameter
	if not grep /$parameter/, @{$modification_tracker{has_sbh}};
    }
    if (($configuration->value('sbh', 'jw') eq 'ohmic') or
	($configuration->value('sbh', 'jw') eq 'debye')) {
      nxinp_set_parameter($configuration, $section, 'wc');
      push @{$modification_tracker{has_sbh}}, 'wc'
	if not grep /wc/, @{$modification_tracker{has_sbh}};
      nxinp_set_parameter($configuration, $section, 'wmax');
      push @{$modification_tracker{has_sbh}}, 'wmax'
	if not grep /wmax/, @{$modification_tracker{has_sbh}};
    }
    if ($configuration->value('sbh', 'jw') eq 'ohmic') {
      nxinp_set_parameter($configuration, $section, 'xi');
      push @{$modification_tracker{has_sbh}}, 'xi'
	if not grep /xi/, @{$modification_tracker{has_sbh}};
    }
    if ($configuration->value('sbh', 'jw') eq 'debye') {
      nxinp_set_parameter($configuration, $section, 'er');
      push @{$modification_tracker{has_sbh}}, 'er'
	if not grep /er/, @{$modification_tracker{has_sbh}};
    }
  }
  elsif ($model == 3) {
    $section = 'recohmodel';
    @{$modification_tracker{has_recohmod}}[0] = 1;
    @parameters_to_modify = ('modelnum', 'A', 'B', 'C');
    foreach my $parameter (@parameters_to_modify) {
      nxinp_set_parameter($configuration, $section, $parameter);
      push @{$modification_tracker{has_recohmod}}, $parameter
	if not grep /$parameter/, @{$modification_tracker{has_recohmod}};
    }
  }
  elsif ($model == 1) {
      $section = 'onedim_model';
      @{$modification_tracker{has_onedim}}[0] = 1;
      @parameters_to_modify = ('onedim_mod');
      foreach my $parameter (@parameters_to_modify) {
	  nxinp_set_parameter($configuration, $section, $parameter);
	  push @{$modification_tracker{has_onedim}}, $parameter
	      if not grep /$parameter/, @{$modification_tracker{has_onedim}};
      }
  }


  @{$modification_tracker{has_analytical}}[0] = 1;

  print STDOUT "\nThe model configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}


sub set_general_columbus {
  my ($configuration, $modification_tracker) = @_;
  print STDOUT "\nSet-up parameters for COLUMBUS\n\n";

  my @parameters_to_modify = ( 'all_grads', 'mem', 'cirestart', 'ivmode', 'mocoef', 'prt_mo',
			       'reduce_tol', 'quad_conv', 'mc_conv', 'ci_conv' );

  my $section = 'columbus';
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_columbus}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_columbus}};
  }

  @{$modification_tracker{has_columbus}}[0] = 1;

  print STDOUT "\nThe Columbus configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}

sub set_general_turbomole {
  my ($configuration, $modification_tracker) = @_;

  print STDOUT "\nSet-up parameters for TURBOMOLE\n\n";

  my $section = 'turbomole';
  my @parameters_to_modify = ( 'nnodes', 'multiplicity', 'npre', 'nstart' );
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_turbomole}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_turbomole}};
  }

  @{$modification_tracker{has_turbomole}}[0] = 1;

  print STDOUT "\nThe Turbomole configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}

sub set_general_gau {
  my ($configuration, $modification_tracker) = @_;

  print STDOUT "\nSet-up parameters for GAUSSIAN\n\n";
  my $section = 'gau';
  my @parameters_to_modify = ( 'g_vers', 'mocoef', 'prt_mo' );
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_gaussian}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_gaussian}};
  }

  @{$modification_tracker{has_gaussian}}[0] = 1;

  print STDOUT "\nThe Gaussian configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}


sub set_general_g09 {
  my ($configuration, $modification_tracker) = @_;
  print STDOUT "\nSet-up parameters for GAUSSIAN\n\n";

  my $section = 'g09';
  my @parameters_to_modify = ( 'mocoef', 'td_st', 'kind_g09', 'ld_thr', 'prt_mo' );
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_g09}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_g09}};
  }

  @{$modification_tracker{has_g09}}[0] = 1;

  print STDOUT "\nThe Gaussian configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}


sub set_general_gaussian {
  my ($configuration, $modification_tracker) = @_;
  print STDOUT "\nSet-up parameters for GAUSSIAN\n\n";

  my $section = 'gaussian';
  my @parameters_to_modify = ( 'mocoef', 'td_st', 'kind_g09', 'ld_thr', 'prt_mo' );
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_gaussian}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_gaussian}};
  }

  @{$modification_tracker{has_gaussian}}[0] = 1;

  print STDOUT "\nThe Gaussian configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}


sub set_general_tinker_mndo {
  my ($configuration, $modification_tracker) = @_;

  print STDOUT "\nSet-up parameters for TINKER / MNDO\n\n";

  my $section = 'tinker_mndo';
  my @parameters_to_modify = ( 'interface_path', 'nac_mm' );
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_tinker_mndo}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_tinker_mndo}};
  }

  @{$modification_tracker{has_tinker_mndo}}[0] = 1;

  print STDOUT "\nThe Tinker / MNDO configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}


sub set_general_tinker_g16 {
  my ($configuration, $modification_tracker) = @_;

  print STDOUT "\nSet-up parameters for TINKER / G16\n\n";

  my $section = 'tinker_g16mmp';
  my @parameters_to_modify = ( 'interface_path' );
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_tinker_g16}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_tinker_g16}};
  }

  @{$modification_tracker{has_tinker_g16}}[0] = 1;

  print STDOUT "\nThe Tinker / G16 configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}


sub set_general_mopac {
    my ($configuration, $modification_tracker) = @_;

    print STDOUT "\nSet-up parameters for MOPAC \n\n";

    my $section = 'mopac';
    my @parameters_to_modify = ( 'verbose' );
    foreach my $parameter (@parameters_to_modify) {
	nxinp_set_parameter($configuration, $section, $parameter);
	push @{$modification_tracker{has_mopac}}, $parameter
	    if not grep /$parameter/, @{$modification_tracker{has_mopac}};
    }

    @{$modification_tracker{has_mopac}}[0] = 1;

    print STDOUT "\nThe MOPAC configuration is now finished.\n";
    print STDOUT "Press any key to go back to the General Option menu.\n";
    $_ = <STDIN>;

    return 0;
}


sub set_general_exc_mopac {
    my ($configuration, $modification_tracker) = @_;

    print STDOUT "\nSet-up parameters for Exciton Model (MOPAC) \n\n";

    my $section = 'exc_mopac';
    my @parameters_to_modify = ( 'nchrom', 'nproc', 'gen_file',
				 'verbose');
    foreach my $parameter (@parameters_to_modify) {
	nxinp_set_parameter($configuration, $section, $parameter);
	push @{$modification_tracker{has_exc_mopac}}, $parameter
	    if not grep /$parameter/, @{$modification_tracker{has_exc_mopac}};
    }

    @{$modification_tracker{has_exc_mopac}}[0] = 1;

    $section = 'exc_inp';
    @parameters_to_modify = ( 'tresp', 'gs', 'dip', 'nat_array', 'nstat', 'coup_file');
    foreach my $parameter (@parameters_to_modify) {
	nxinp_set_parameter($configuration, $section, $parameter);
	push @{$modification_tracker{has_exc_inp}}, $parameter
	    if not grep /$parameter/, @{$modification_tracker{has_exc_inp}};
    }

    @{$modification_tracker{has_exc_inp}}[0] = 1;

    print STDOUT "\nThe Exciton Model (MOPAC) configuration is now finished.\n";
    print STDOUT "Press any key to go back to the General Option menu.\n";
    $_ = <STDIN>;

    return 0;
}


sub set_general_exc_gaussian {
    my ($configuration, $modification_tracker) = @_;

    print STDOUT "\nSet-up parameters for Exciton Model (GAUSSIAN) \n\n";

    my $section = 'exc_gaussian';
    my @parameters_to_modify = ( 'nchrom', 'nproc', 'nstattd', 'nproc_gau',
				 'functional', 'basis', 'force_field',
				 'verbose');
    foreach my $parameter (@parameters_to_modify) {
	nxinp_set_parameter($configuration, $section, $parameter);
	push @{$modification_tracker{has_exc_gaussian}}, $parameter
	    if not grep /$parameter/, @{$modification_tracker{has_exc_gaussian}};
    }

    @{$modification_tracker{has_exc_gaussian}}[0] = 1;

    $section = 'exc_inp';
    @parameters_to_modify = ( 'tresp', 'gs', 'dip', 'nat_array', 'nstat', 'coup_file');
    foreach my $parameter (@parameters_to_modify) {
	nxinp_set_parameter($configuration, $section, $parameter);
	push @{$modification_tracker{has_exc_inp}}, $parameter
	    if not grep /$parameter/, @{$modification_tracker{has_exc_inp}};
    }

    @{$modification_tracker{has_exc_inp}}[0] = 1;

    print STDOUT "\nThe Exciton Model (GAUSSIAN) configuration is now finished.\n";
    print STDOUT "Press any key to go back to the General Option menu.\n";
    $_ = <STDIN>;

    return 0;
}


sub set_nad_tdse_sh {
  my ($configuration, $modification_tracker) = @_;

  my @parameters_to_modify = ( 'integrator', 'ms', 'getphase', 'nohop', 'nrelax',
			       'seed', 'probmin',  'tully',
			       'decay', 'mom', 'adjmom'
			     );
  my $section = 'sh';
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_sh}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_sh}};
  }

  if ($configuration->value('sh', 'adjmom') == 2) {
      nxinp_set_parameter($configuration, $section, 'adjtheta');
      push @{$modification_tracker{has_sh}}, 'adjtheta'
	  if not grep /adjtheta/, @{$modification_tracker{has_sh}};
  }

  nxinp_set_parameter($configuration, $section, 'popdev');
  push @{$modification_tracker{has_sh}}, 'popdev'
      if not grep /popdev/, @{$modification_tracker{has_sh}};

  @{$modification_tracker{has_sh}}[0] = 1;
  print STDOUT "\nThe Surface Hopping configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;
  return 0;
}


sub set_nad_compute_nac {
  my ($configuration, $modification_tracker) = @_;

  my @parameters_to_modify = ( 'kross', 'cascade', 'current', 'never_state',
			       'include_pair',
			     );
  my $section = 'nad_setup';
  foreach my $parameter (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $parameter);
    push @{$modification_tracker{has_nad_setup}}, $parameter
      if not grep /$parameter/, @{$modification_tracker{has_nad_setup}};
  }

  @{$modification_tracker{has_nad_setup}}[0] = 1;
  print STDOUT "\nThe NAC computing configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}


sub set_nad_how_nac {
  my ($configuration) = @_;

  # if ($configuration->value('nxconfig', 'derivative_couplings') == 1) {
  #   print STDOUT "\n\nNon-adiabatic coupling vectors will be used (vdoth = 0)\n";
  #   print STDOUT "There is nothing to do here !!\n";
  #   print STDOUT "Please enter a key to return to the previous menu.";
  #   $_ = <STDIN>;
  #   return 1;
  # }

  my $dc_file = $ENV{NXHOME}."/data/derivative_couplings.yml";
  my $dc_config = YAML::Tiny->read($dc_file)
      or die "Couldn't process '$dc_file' ($!), stopped";

  my ($progname, $method) = prog_name($configuration->value('nxconfig', 'prog'));

  if (! defined "$method") { $method = 'default' ;}
  if (! defined $dc_config->[0]->{$progname}->{$method}) { $method = 'default' ; }

  # Determine the program we want to use (if any)
  my $section = 'cioverlap';
  my $dc_method = $configuration->value('nxconfig', 'dc_method');
  my $question = "Current value of dc_method is: "."$dc_method \n";
  $question = $question."Do you wish to change this value";
  my $answer = ask_y_or_n($question);
  if ($answer) {
      nxinp_set_parameter($configuration, 'nxconfig', 'dc_method');
  }
  $dc_method = $configuration->value('nxconfig', 'dc_method');

  if ($dc_method == 1) {
      # In this case there is nothing to do, as the NAD will be determined from
      # the QM program.
      @{$modification_tracker{has_cioverlap}}[0] = 0;
      print STDOUT "\nReading from QM program requested: nothing else to do here.\n\n";
      print STDOUT "\nThe Time Derivative couplings configuration is now finished.\n";
      print STDOUT "Press any key to go back to the General Option menu.\n";
      $_ = <STDIN>;

      return 0;
  }
  elsif ($dc_method == 3) {
      # Auxiliary models for obtaining the couplings: switch off cioverlap !
      @{$modification_tracker{has_cioverlap}}[0] = 0;

      @{$modification_tracker{has_auxnac}}[0] = 1;
      $section = 'auxnac';
      nxinp_set_parameter($configuration, $section, 'model');
      push @{$modification_tracker{has_auxnac}}, 'model'
	  if not grep /model/, @{$modification_tracker{has_auxnac}};

      my $model = $configuration->value('auxnac', 'model');
      if ($model == 0) {
	  my @params_to_set = ( 'ba_smooth', 'ba_dh', 'ba_de', 'ba_dv' );
	  foreach my $param (@params_to_set) {
	      nxinp_set_parameter($configuration, $section, $param);
	      push @{$modification_tracker{has_auxnac}}, "$param"
		  if not grep /$param/, @{$modification_tracker{has_auxnac}};
	  }
      }
      # Auxiliary models for obtaining the couplings
      @{$modification_tracker{has_cioverlap}}[0] = 1;
  }
  elsif ($dc_method == 2) {
      nxinp_set_parameter($configuration, $section, 'ovl_prog');
      push @{$modification_tracker{has_cioverlap}}, 'ovl_prog'
	  if not grep /ovl_prog/, @{$modification_tracker{has_cioverlap}};

      my $ovl_prog = $configuration->value($section, 'ovl_prog');
      if ("$ovl_prog" eq 'NULL') {
	  # No parameters are available (not implemented)
	  print STDOUT "\nNo configuration available.\n";
	  print STDOUT "\nThis ovl_prog option is probably not available for the chosen prog / method combination.\n\n";

	  # Remove the cioverlap option
	  @{$modification_tracker{has_cioverlap}}[0] = 0;
	  print STDOUT "\nThe Time Derivative couplings configuration is now finished.\n";
	  print STDOUT "Press any key to go back to the General Option menu.\n";
	  $_ = <STDIN>;

	  return 0;
      }
      if ($ovl_prog == 1) {
	  # cioverlap by Jiri Pittner
	  my @parameters = sort( keys %{ $dc_config->[0]->{$progname}->{$method}->{'cioverlap'}} );
	  foreach my $parameter (@parameters) {
	      nxinp_set_parameter($configuration, $section, $parameter);
	      push @{$modification_tracker{has_cioverlap}}, $parameter
		  if not grep /$parameter/, @{$modification_tracker{has_cioverlap}};
	  }
      }
      elsif ($ovl_prog == 2) {
	  # cioverlap-od
	  my @parameters = sort( keys %{ $dc_config->[0]->{$progname}->{$method}->{'cioverlap_od'}} );
	  foreach my $parameter (@parameters) {
	      nxinp_set_parameter($configuration, $section, $parameter);
	      push @{$modification_tracker{has_cioverlap}}, $parameter
		  if not grep /$parameter/, @{$modification_tracker{has_cioverlap}};
	  }
      }
      elsif ($ovl_prog == 3) {
	  # wfoverlap by Felix Plasser
	  my @parameters = sort( keys %{ $dc_config->[0]->{$progname}->{$method}->{'wfoverlap'}} );
	  foreach my $parameter (@parameters) {
	      nxinp_set_parameter($configuration, $section, $parameter);
	      push @{$modification_tracker{has_cioverlap}}, $parameter
		  if not grep /$parameter/, @{$modification_tracker{has_cioverlap}};
	  }
      }
      @{$modification_tracker{has_cioverlap}}[0] = 1;
  }

  print STDOUT "\nThe Time Derivative couplings configuration is now finished.\n";
  print STDOUT "Press any key to go back to the General Option menu.\n";
  $_ = <STDIN>;

  return 0;
}

sub set_traj {

  my ($configuration, $modification_tracker) = @_;
  my $section = 'generate_traj';

  # Check if input files are there
  if (! -e "initial_condition") {
    print STDOUT "\n\t\tI couldn't find 'initial_condition'. Please create it before going further.\n";
    print STDOUT "\n\t\tPress any key to go back to the Main Menu.\n";
    $_ = <STDIN>;
    return 1;
  }
  if ((! -e "JOB_AD") and (! -e "JOB_NAD")) {
    print STDOUT "\n\t\tI couldn't find 'JOB_AD' or 'JOB_NAD' directories.";
    print STDOUT "\n\t\tPlease create it before going further.\n\n";
    print STDOUT "\n\t\tPress any key to go back to the Main Menu.\n";
    $_ = <STDIN>;
    return 1;
  }
  nxinp_set_parameter($configuration, $section, 'nis');
  push @{$modification_tracker{has_generate_traj}}, 'nis'
    if not grep /nis/, @{$modification_tracker{has_generate_traj}};

  nxinp_set_parameter($configuration, $section, 'screen');
  push @{$modification_tracker{has_generate_traj}}, 'screen'
    if not grep /screen/, @{$modification_tracker{has_generate_traj}};

  if ($configuration->value('generate_traj', 'screen') == 2) {
    nxinp_set_parameter($configuration, $section, 'e_center');
    push @{$modification_tracker{has_generate_traj}}, 'e_center'
      if not grep /e_center/, @{$modification_tracker{has_generate_traj}};

    nxinp_set_parameter($configuration, $section, 'e_var');
    push @{$modification_tracker{has_generate_traj}}, 'e_var'
      if not grep /e_var/, @{$modification_tracker{has_generate_traj}};
  }

  my ($e_min, $e_0, $e_max) = set_energy_window($configuration);


  my @parameters_to_modify = ( 'os_condon', 'norm', 'seed', 'run_is' );
  foreach my $param (@parameters_to_modify) {
    nxinp_set_parameter($configuration, $section, $param);
    push @{$modification_tracker{has_generate_traj}}, $param
      if not grep /$param/, @{$modification_tracker{has_generate_traj}};
  }

  # Now find the largest oscillator strengths for renormalization
  my $norm = find_max_intensity($configuration, $e_min, $e_max);

  @{$modification_tracker{has_generate_traj}}[0] = 1;


  save_config($configuration, $modification_tracker);

  # # Create TRAJECTORIES folder
  if (! -e "TRAJECTORIES") {
    mkdir("TRAJECTORIES");
  }
  else {
    my $question = "'TRAJECTORIES' folder already exists. Do you want to remove it ?";
    my $answer = ask_y_or_n($question);
    if ($answer) {
      system("rm -rf TRAJECTORIES");
    }
    mkdir("TRAJECTORIES");
  }

  # Now select the initial conditions based on the selected parameters.
  # generate_initial_trajectories($configuration, $e_min, $e_0, $e_max);
  generate_initial_trajectories($config, $norm, $e_min, $e_max);

  print STDOUT "\nThe input files for running the trajectories have been generated.\n";
  print STDOUT "Press any key to go quit the Newton-X Input Generator\n";
  $_ = <STDIN>;

  exit;
  # return 0;
}


sub set_h5md {
    my ($configuration, $modification_tracker) = @_;
    my $section = 'h5md';

    my @parameters_to_modify = ( 'user_name', 'user_email', 'h5file');

    foreach my $parameter (@parameters_to_modify) {
	nxinp_set_parameter($configuration, $section, $parameter);
	push @{$modification_tracker{has_h5md}}, $parameter
	    if not grep /$parameter/, @{$modification_tracker{has_h5md}};
    }

    @{$modification_tracker{has_h5md}}[0] = 1;
    print STDOUT "\nThe H5MD output configuration is now finished.\n";
    print STDOUT "Press any key to go back to the General Option menu.\n";
    $_ = <STDIN>;
}

sub set_energy_window {
  my ($configuration) = @_;

  # These are the output of this function
  my ($e_min, $e_0, $e_max);
  my $screen = $configuration->value('generate_traj', 'screen');
  my $e_center = $configuration->value('generate_traj', 'e_center');
  my $nis = $configuration->value('generate_traj', 'nis');
  my $e_var = $configuration->value('generate_traj', 'e_var');

  if ($screen == 0) {
    # Do not apply any restriction
    $e_min = "-infinity";
    $e_0 = 0;
    $e_max = "infinity";
  }
  elsif ($screen == 1) {
    # Keep already applied restrictions
    $e_min = 0;
    $e_0 = 0;
    $e_max = 0;
  }
  else {
    # Apply new restrictions
    if ($e_center =~ /ref/i) {
      my ($g, $iref) = split /\s+/, $e_center;
      my $file = "initial_condition.$nis.$iref";
      open(my $fh, '<:encoding(UTF-8)', $file)
	or die "Couln't open '$file' $!, stopped";
      my $ind = 0;
      while (<$fh>) {
	if (/Equilibrium geometry/) {
	  $ind++;
	  while (<$fh>) {
	    if (/Vertical excitation/) {
	      # $e_0 = get_energy($_);
	      my @tmp = split /\s+/;
	      $e_0 = $tmp[-1];
	      last;
	    } # if 'vertical excitation'
	  } # while 2
	} # if equilibrium energy
      } # while 1
      close($fh);
      if ($ind == 0) {
	die "Error: $file must contain an \"Equilibrium energy\" card.\n";
      }
    } # if Reference state was given (i.e. e_center = ref n)
    else {
      $e_0 = $e_center;
    }
    $e_min = $e_0 - $e_var;
    $e_max = $e_0 + $e_var;
    print STDOUT "Energy transitions will be restricted to the range $e_min to $e_max with center at $e_0.\n ";
  } # External loop

  return $e_min, $e_0, $e_max;
}


sub find_max_intensity {
  my ($config, $e_min, $e_max) = @_;
  open(my $fh, '<:encoding(UTF-8)', 'initial_condition')
    or die "Couldn't open 'initial_condition, $! stopped at";
  my $max = 0;
  my $en = 0.0;
  my $is_local = ($config->value('generate_traj', 'norm') eq "local");
  my $delta = 1;

  while (<$fh>) {
    if (/Vertical excitation/) {
      my @tmp = split /\s+/;
      $en = $tmp[4];
    }
    if (/Oscillator strength/) {
      my @tmp = split /\s+/;
      my $osc = $tmp[-1];
      if ($is_local) {
	if (($en < $e_min) or ($en > $e_max)) {
	  $delta = 0;
	}
	else {
	  $delta = 1;
	}
      }
      if ($delta*$osc > $max) {
	$max = $osc;
      }
    }
  }
  close($fh);

  return $max;
}


sub generate_initial_trajectories {
  my ($configuration, $norm, $e_min, $e_max) = @_;

  open(my $fh, '<:encoding(UTF-8)', 'initial_condition')
    or die "Couldn't open 'initial_condition', $! stopped at ";


  my @input_files = ( 'user_config.nml', 'JOB_AD', 'JOB_NAD' );

  my $i;
  my $geom = "";
  my $veloc = "";
  my $osc_str = "";
  my $de;
  my $nat = $configuration->value('nxconfig', 'nat');
  my $delta = 1;
  my $it = 0;
  my $is_eq = 1;
  while (<$fh>) {
    if (/Initial condition/) {$is_eq = 0};

    if (/Geometry/) {
      # Extract geometry
      for ($i = 1; $i <= $nat ; $i++) {
	$_ = <$fh>;
	$geom = $geom.$_;
      }
    }
    if (/Velocity/) {
      # Extract velocity;
      for ($i = 1; $i <= $nat ; $i++) {
	$_ = <$fh>;
	$veloc = $veloc.$_;
      }
    }
    if (/Vertical excitation/) {
      my @tmp = split /\s+/;
      $de = $tmp[4];
      if (($de < $e_min) or ($de > $e_max)) {
	$delta = 0;
      }
      else {
	$delta = 1;
      }
    }
    if (/Oscillator strength/) {
      if ($configuration->value('generate_traj', 'os_condon') == -1) {
	my @tmp = split /\s+/;
	$osc_str = $tmp[-1];
      }
      else {
	$osc_str = $configuration->value('generate_traj', 'os_condon');
      }

      # Now we can decide if we take this structure or not, only if no prior
      # screening was applied.
      my $gen_this = 1;    
  if ($configuration->value('generate_traj', 'screen') != 0) {
	  my $prob = $osc_str * $delta / $norm;
	  my $rd = rand();
	  if ($rd > $prob) {
	      $gen_this = 0;
	  }
      }

      if (($gen_this) and (! $is_eq)) {
	$it++;
	mkdir("TRAJECTORIES/TRAJ$it");

	foreach my $ff (@input_files) {
	  if (-e "$ff") {
	    fcopy("$ff", "TRAJECTORIES/TRAJ$it/");
	  }
	  if (-d "$ff") {
	    mkdir("TRAJECTORIES/TRAJ$it/$ff/");
	    dircopy("$ff", "TRAJECTORIES/TRAJ$it/$ff/");
	  }
	}

	open(my $gf, '>:encoding(UTF-8)', "TRAJECTORIES/TRAJ$it/geom.orig");
	print $gf "$geom";
	close($gf);

	mkdir("TRAJECTORIES/TRAJ$it");
	open(my $vf, '>:encoding(UTF-8)', "TRAJECTORIES/TRAJ$it/veloc.orig");
	print $vf "$veloc";
	close($gf);
      }

      $geom = "";
      $veloc = "";
    }
  }

  close($fh);
}


sub transition_intensity {
  # This subroutine is trivial for now, and will be expanded when
  # more options are added to the program.
  my ($os, $de) = @_;

  $os = abs($os);
  $de = abs($de);

  my $eca = $os * $de**2;
  my $ecb = $eca / $de**3;

  my $intensity = $os;

  return $intensity;
}


sub energy_screening {
  my ($config, $de, $e_min, $e_max) = @_;
  my ($delta, $window);
  my $screen = $config->value('generate_traj', 'screen');
  if ($screen == 0) {
    $delta = 1;
    $window = "YES";
  }
  elsif ($screen == 1) {
    $delta = 1;
  }
  else {
    if (($de >= $e_min) and ($de <= $e_max)) {
      $delta = 1;
      $window = "YES";
    }
    else {
      $delta = 0;
      $window = "NO";
    }
  }

  return $delta, $window;
}


sub set_stats {
  return 0;
}

sub save_config {
  my ($configuration, $modification_tracker) = @_;
  my $user_config_file = 'user_config.nml';
  my $backup = 'user_config.nml.bck';

  # If a configuration file is found, delete it !
  if (-s $user_config_file) {
    print STDOUT "\nI will backup $user_config_file as $backup !\n";
    system("cp $user_config_file $backup");
    unlink $user_config_file;
  }

  # This table will be used to hold the parameters to print in the input
  # for each flag from modification tracker. It is initialized to the
  # parameters set in basic input
  shift @{$modification_tracker{has_nxconfig}};
  my @keywords = @{$modification_tracker{has_nxconfig}};
  if (! grep /nxrestart/, @{ $modification_tracker{has_nxconfig} } ) {
      push @keywords, 'nxrestart';
  }

  my $prog = $configuration->value('nxconfig', 'prog');
  my ($progname, $methodname) = prog_name($prog);
  $configuration->value('nxconfig', 'progname', $progname);
  $configuration->value('nxconfig', 'methodname', $methodname);
  $configuration->export_subsection_of_config($user_config_file, 'nxconfig', \@keywords);

  # General configuration
  # @keywords = ();
  # if (@{$modification_tracker{has_general_dynamics}}[0] == 1) {
  #   shift @{$modification_tracker{has_general_dynamics}};
  #   push @keywords, @{$modification_tracker{has_general_dynamics}};
  #   $configuration->export_subsection_of_config($user_config_file, 'nxconfig', \@keywords);
  # }

  # Adaptive time-step
  @keywords = ();
  if (@{$modification_tracker{has_adapt_dt}}[0] == 1) {
    shift @{$modification_tracker{has_adapt_dt}};
    push @keywords, @{$modification_tracker{has_adapt_dt}};
    $configuration->export_subsection_of_config($user_config_file, 'adapt_dt', \@keywords);
  }

  # Thermostat configuration
  @keywords = ();
  if (@{$modification_tracker{has_thermostat}}[0] == 1) {
    shift @{$modification_tracker{has_thermostat}};
    push @keywords, @{$modification_tracker{has_thermostat}};
    $configuration->export_subsection_of_config($user_config_file, 'thermostat', \@keywords);
  }

  # ZPE correction
  @keywords = ();
  if (@{$modification_tracker{has_zpe_correction}}[0] == 1) {
    shift @{$modification_tracker{has_zpe_correction}};
    push @keywords, @{$modification_tracker{has_zpe_correction}};
    $configuration->export_subsection_of_config($user_config_file, 'zpe_correction', \@keywords);
  }

  # Surface hopping configuration
  @keywords = ();
  if (@{$modification_tracker{has_sh}}[0] == 1) {
    shift @{$modification_tracker{has_sh}};
    push @keywords, @{$modification_tracker{has_sh}};
    $configuration->export_subsection_of_config($user_config_file, 'sh', \@keywords);
  }

  # NACs and time-derivative couplings configuration
  @keywords = ();
  if (@{$modification_tracker{has_cioverlap}}[0] == 1) {
    shift @{$modification_tracker{has_cioverlap}};
    push @keywords, @{$modification_tracker{has_cioverlap}};
    $configuration->export_subsection_of_config($user_config_file, 'cioverlap', \@keywords);
  }

  @keywords = ();
  if (@{$modification_tracker{has_auxnac}}[0] == 1) {
      shift @{$modification_tracker{has_auxnac}};
      push @keywords, @{$modification_tracker{has_auxnac}};
      $configuration->export_subsection_of_config($user_config_file, 'auxnac', \@keywords);
  }

  # NAD setup configuration
  @keywords = ();
  if (@{$modification_tracker{has_nad_setup}}[0] == 1) {
    shift @{$modification_tracker{has_nad_setup}};
    push @keywords, @{$modification_tracker{has_nad_setup}};
    $configuration->export_subsection_of_config($user_config_file, 'nad_setup', \@keywords);
  }

  # Electronic structure configuration
  @keywords = ();
  if (@{$modification_tracker{has_analytical}}[0] == 1) {
    shift @{$modification_tracker{has_analytical}};
    push @keywords, @{$modification_tracker{has_analytical}};
    # $configuration->export_subsection_of_config($user_config_file, 'analytical', \@keywords);
    if (@{$modification_tracker{has_sbh}}[0] == 1) {
      @keywords = ();
      shift @{$modification_tracker{has_sbh}};
      push @keywords, @{$modification_tracker{has_sbh}};
      $configuration->export_subsection_of_config($user_config_file, 'sbh', \@keywords);
    }
    if (@{$modification_tracker{has_recohmod}}[0] == 1) {
	@keywords = ();
	shift @{$modification_tracker{has_recohmod}};
	push @keywords, @{$modification_tracker{has_recohmod}};
	$configuration->export_subsection_of_config($user_config_file, 'recohmodel', \@keywords);
    }
    if (@{$modification_tracker{has_onedim}}[0] == 1) {
	@keywords = ();
	shift @{$modification_tracker{has_onedim}};
	push @keywords, @{$modification_tracker{has_onedim}};
	$configuration->export_subsection_of_config($user_config_file, 'onedim_model', \@keywords);
    }
  }

  @keywords = ();
  if (@{$modification_tracker{has_columbus}}[0] == 1) {
    shift @{$modification_tracker{has_columbus}};
    push @keywords, @{$modification_tracker{has_columbus}};
    $configuration->export_subsection_of_config($user_config_file, 'columbus', \@keywords);
  }

  @keywords = ();
  if (@{$modification_tracker{has_g09}}[0] == 1) {
    shift @{$modification_tracker{has_g09}};
    push @keywords, @{$modification_tracker{has_g09}};
    $configuration->export_subsection_of_config($user_config_file, 'g09', \@keywords);
  }

  @keywords = ();
  if (@{$modification_tracker{has_gaussian}}[0] == 1) {
    shift @{$modification_tracker{has_gaussian}};
    push @keywords, @{$modification_tracker{has_gaussian}};
    $configuration->export_subsection_of_config($user_config_file, 'gaussian', \@keywords);
  }

  @keywords = ();
  if (@{$modification_tracker{has_turbomole}}[0] == 1) {
    shift @{$modification_tracker{has_turbomole}};
    push @keywords, @{$modification_tracker{has_turbomole}};
    $configuration->export_subsection_of_config($user_config_file, 'turbomole', \@keywords);
  }

  @keywords = ();
  if (@{$modification_tracker{has_dftb}}[0] == 1) {
    shift @{$modification_tracker{has_dftb}};
    push @keywords, @{$modification_tracker{has_dftb}};
    $configuration->export_subsection_of_config($user_config_file, 'dftb', \@keywords);
  }

  @keywords = ();
  if (@{$modification_tracker{has_tinker_mndo}}[0] == 1) {
      shift @{$modification_tracker{has_tinker_mndo}};
      push @keywords, @{$modification_tracker{has_tinker_mndo}};
      $configuration->export_subsection_of_config($user_config_file, 'tinker_mndo', \@keywords);
  }

  @keywords = ();
  if (@{$modification_tracker{has_tinker_g16}}[0] == 1) {
      shift @{$modification_tracker{has_tinker_g16}};
      push @keywords, @{$modification_tracker{has_tinker_g16}};
      $configuration->export_subsection_of_config($user_config_file, 'tinker_g16mmp', \@keywords);
  }

  @keywords = ();
  if (@{$modification_tracker{has_mopac}}[0] == 1) {
      shift @{$modification_tracker{has_mopac}};
      push @keywords, @{$modification_tracker{has_mopac}};
      $configuration->export_subsection_of_config($user_config_file, 'mopac', \@keywords);
  }

  @keywords = ();
  if (@{$modification_tracker{has_exc_mopac}}[0] == 1) {
      shift @{$modification_tracker{has_exc_mopac}};
      push @keywords, @{$modification_tracker{has_exc_mopac}};
      $configuration->export_subsection_of_config($user_config_file, 'exc_mopac', \@keywords);
  }

  @keywords = ();
  if (@{$modification_tracker{has_exc_gaussian}}[0] == 1) {
      shift @{$modification_tracker{has_exc_gaussian}};
      push @keywords, @{$modification_tracker{has_exc_gaussian}};
      $configuration->export_subsection_of_config($user_config_file, 'exc_gaussian', \@keywords);
  }

  @keywords = ();
  if (@{$modification_tracker{has_exc_inp}}[0] == 1) {
      shift @{$modification_tracker{has_exc_inp}};
      push @keywords, @{$modification_tracker{has_exc_inp}};
      $configuration->export_subsection_of_config($user_config_file, 'exc_inp', \@keywords);
  }

  @keywords = ();
  if (@{$modification_tracker{has_h5md}}[0] == 1) {
    shift @{$modification_tracker{has_h5md}};
    push @keywords, @{$modification_tracker{has_h5md}};
    $configuration->export_subsection_of_config($user_config_file, 'h5md', \@keywords);
  }


  # Generate trajectories
  @keywords = ();
  if (@{$modification_tracker{has_generate_traj}}[0] == 1) {
    shift @{$modification_tracker{has_generate_traj}};
    push @keywords, @{$modification_tracker{has_generate_traj}};
    $configuration->export_subsection_of_config($user_config_file, 'generate_traj', \@keywords);
  }

  # exit;
}


sub generate_missing_defaults {
    my ($configuration, $sec, $imported_parameters) = @_;


    my $imported_section = %{ $imported_parameters }{$sec};
    my $conf_section = $configuration->section($sec);

    foreach my $param (keys %{ $conf_section }) {
	if (! grep /$param/, @$imported_section) {
	    if (defined $conf_section->{$param}->{func}) {
		my $func = $conf_section->{$param}->{func};
		require NX::InputGenerator::SetValues;
		$func = $NX::InputGenerator::safe_set{$func};
		if (defined $func) {
		    my $value = $func->($configuration);
		    $configuration->value($sec, $param, $value);
		}
	    }
	}
    }
}

__END__

=head1 NAME

nxinp - Input generator for Newton-X NS

=head1 SYNOPSIS

nxinp

=head1 DESCRIPTION

This program is used to interactively generate a suitable input for Newton-X.  The input file is
written only when terminating the program (Option 7 from the main menu), and will be called
I<user_config.nml>.

The file written contains only the section that have been explicitly modified during input generation,
and thus contain only the modification with respect to the default values.  The file is written in
a namelist format.

The input is intended to be parsed with the I<moldyn> script, to generate the complete configuration
to run the dynamics.

=head1 ENVIRONMENT

=over

=item NXHOME

Path to the main Newton-X NS folder.

=back

=head1 FILES

The script will need the following files:

=over

=item $NXHOME/data/default.yml: YAML file containing the default configuration

=back

=head1 AUTHOR

Baptiste Demoulin <baptiste.demoulin@univ-amu.fr>

=head1 BUGS

Please report all bugs to https://gitlab.com/light-and-molecules/newtonx.

=head1 COPYRIGHT

Copyright (C) 2022 Light and Molecules Group. License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>

=cut
