PROGRAM atomsk
!
!**********************************************************************************
!*     o---o                                                         O            *
!*    o---o|                      A T O M S K                        |            *
!*    |   |o          Atom/Molecule/Material Software Kit           ,0;-o         *
!*    o---o           ===--=--------=--------=--------=--         o'   'O         *
!**********************************************************************************
!* Atomsk is a command-line program that aims at creating, manipulating, and      *
!* converting atomic systems, for the purpose of ab initio calculations,          *
!* classical potential simulations, or visualization. It supports many file       *
!* formats, among which LAMMPS, VASP, Quantum Espresso, Atomeye CFG format, and   *
!* xCrySDen XSF format (see the list of supported formats). Additionnaly, Atomsk  *
!* can also perform some simple transformations of atomic positions (thanks to    *
!* the many options), like creating supercells, cutting crystal planes, applying  *
!* stress, or inserting dislocations.                                             *
!*                                                                                *
!* Please refer to the contents of /doc for a detailed description, or go to:     *
!*                                                                                *
!*     https://atomsk.univ-lille.fr/                                              *
!*                                                                                *
!**********************************************************************************
!* Version of the program: see "/include/comv.f90"                                *
!* (C) Feb. 2010 - Pierre Hirel                                                   *
!*     Université de Lille, Sciences et Technologies                              *
!*     UMR CNRS 8207, UMET - C6, F-59655 Villeneuve D'Ascq, France                *
!*     pierre.hirel@univ-lille.fr                                                 *
!* Last modification: P. Hirel - 23 Oct. 2023                                     *
!**********************************************************************************
!* 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 <http://www.gnu.org/licenses/>.          *
!**********************************************************************************
!
!
!Load modules
USE comv        !common variables
USE constants   !math and physics constants
USE functions   !functions used by the program
USE messages
USE subroutines !subroutines for this program
USE readconf    !read the config file (atomsk.conf)
USE read_cla    !read command-line arguments
!
!Modules managing modes
USE modes
USE mode_interactive
!
!
!
!Declare variables
IMPLICIT NONE
CHARACTER(LEN=3):: outfileformat
CHARACTER(LEN=5),DIMENSION(:),ALLOCATABLE:: outfileformats !list of formats to write
CHARACTER(LEN=5):: zone
CHARACTER(LEN=8):: date
CHARACTER(LEN=10):: time
CHARACTER(LEN=12):: mode     !mode in which the program runs
CHARACTER(LEN=16):: helpsection
CHARACTER(LEN=128):: homedir  !home directory and .atomsk file
CHARACTER(LEN=128):: temp
CHARACTER(LEN=128):: username !the user's name
CHARACTER(LEN=4096):: clarg
CHARACTER(LEN=16),DIMENSION(20):: opt_file !options read from a file
CHARACTER(LEN=4096),DIMENSION(5):: pfiles !pfiles(1)=file1
                                          !pfiles(2)=file2
                                          !pfiles(3)=filefirst
                                          !pfiles(4)=filesecond
                                          !pfiles(5)=listfile
CHARACTER(LEN=128),DIMENSION(:),ALLOCATABLE:: options_array !options and their parameters
CHARACTER(LEN=4096),DIMENSION(:),ALLOCATABLE:: cla, cla_new  !command-line arguments
CHARACTER(LEN=128),DIMENSION(:),ALLOCATABLE:: mode_param  !parameters for some special modes
LOGICAL:: fileexists, logopened
INTEGER:: i, j, m, n, ioptions
INTEGER:: Nmodes   !number of modes in command-line
INTEGER:: Nthreads !number of OpenMP threads
INTEGER:: strlength
INTEGER,DIMENSION(8):: valuesi, valuesf !initial and final time
REAL(dp):: time_total
REAL(dp):: cpu_t1, cpu_t2
CALL CPU_TIME(cpu_t1)
!
!Get the current date and time
CALL DATE_AND_TIME(DATE, TIME, ZONE, VALUESi)
!VALUES(1): 	The year
!VALUES(2): 	The month
!VALUES(3): 	The day of the month
!VALUES(4): 	Time difference with UTC in minutes
!VALUES(5): 	The hour of the day
!VALUES(6): 	The minutes of the hour
!VALUES(7): 	The seconds of the minute
!VALUES(8): 	The milliseconds of the second
!
!
!Initialize variables
IF(ALLOCATED(options_array)) DEALLOCATE(options_array)
fileexists = .FALSE.
 clarg = ''
outfileformat=''
username = ''
pfiles(:) = ''
strlength=0
nwarn = 0
nerr = 0
Nmodes = 0
Nthreads = 0
ioptions = 0
j=0
!
!Set default parameters of the program
lang = 'en'
langBigYes = "Y"
langyes = "y"
langno = "n"
logfile='atomsk.log'
neighsearch = "auto"
helpsection = 'general'
mode='normal'  !By default run in normal mode, unless no parameter is given in command-line
overw=.FALSE.  !By default, don't overwrite files
ignore=.FALSE. !By default, don't ignore files already converted
!Define the default level of verbosity:
!    0=silent; no message is ever displayed nor written in log file
!    1=all messages are written on the screen only.
!    2=all messages are written in log file only.
!    3=messages are written in log+screen.
!    4=debug, additional messages are written in log file.
!This default value should be set to 4 only for programming/debugging purposes
verbosity = 1
!
!
!Environment is set at compilation time (use flag -cpp).
!When compiling for Microsoft Windows(R), use the flag -DWINDOWS
!otherwise UNIX/Linux setup is assumed
#if defined(WINDOWS)
  !COMPILATION FOR MICROSOFT WINDOWS
  !Set path of personal config file (%HOMEPATH%\atomsk.ini)
  CALL GET_ENVIRONMENT_VARIABLE('HOMEPATH',homedir)
  homedir = TRIM(ADJUSTL(homedir))//'\atomsk.ini'
#else
  !COMPILATION FOR UNIX OR Linux
  !Look for a system-wide config file (/etc/atomsk.conf)
  homedir = "/etc/atomsk.conf"
  INQUIRE(FILE=homedir,EXIST=fileexists)
  !If file exists, read parameters from it
  IF(fileexists) THEN
    CALL READ_CONF(homedir)
  ENDIF
  !
  !Set path to the user's personal config file
  !First, check if a directory for config files is defined
  CALL GET_ENVIRONMENT_VARIABLE('XDG_CONFIG_HOME',homedir)
  IF( LEN_TRIM(homedir)<=0 ) THEN
    !None is defined => default is ~/.config/
    CALL GET_ENVIRONMENT_VARIABLE('HOME',homedir)
    homedir = TRIM(ADJUSTL(homedir))//"/.config"
  ENDIF
  !Append name of config file (atomsk.conf)
  homedir = TRIM(ADJUSTL(homedir))//'/atomsk.conf'
  !
  !Detect environment language
  CALL GET_ENVIRONMENT_VARIABLE('LANG',clarg)
  !
  !Check if it is a recognizable language, otherwise default to English
  IF( INDEX(clarg,'fr').NE.0 .OR. INDEX(clarg,'FR').NE.0 ) THEN
    lang = 'fr'
  ELSEIF( INDEX(clarg,'de').NE.0 .OR. INDEX(clarg,'DE').NE.0 ) THEN
    lang = 'de'
  ELSE
    lang = 'en'
  ENDIF
  !
  !Get user name
  CALL GET_ENVIRONMENT_VARIABLE('USER',username)
  !
  !Display a warning if user is root
  IF( username=="root" ) THEN
    nwarn=nwarn+1
    CALL ATOMSK_MSG(750,(/""/),(/0.d0/))
  ENDIF
#endif
!
!
!Read the user's config file (if it exists)
INQUIRE(FILE=homedir,EXIST=fileexists)
!If file exists, read parameters from it
IF(fileexists) THEN
  CALL READ_CONF(homedir)
ENDIF
!
!Check for a config file in current directory
#if defined(WINDOWS)
homedir = "atomsk.ini"
#else
homedir = "atomsk.conf"
#endif
INQUIRE(FILE=homedir,EXIST=fileexists)
!If file exists, read parameters from it
IF(fileexists) THEN
  CALL READ_CONF(homedir)
ENDIF
#if defined(WINDOWS)
!Force disabling of couloured emssages for Windows
colourtext=.FALSE.
#endif
!
!
!
100 CONTINUE
!Read command-line arguments
IF( IARGC()<=0 ) THEN
  mode = 'interactive'
ELSE
  !Array cla(:) will contain all command-line arguments
  ALLOCATE( cla(IARGC()) )
  cla(:) = ''
  !Copy command-line arguments to the array cla(:) and
  !find the size of the options_array(:) from the command-line options
  ioptions=0
  i=0
  j=0
  DO WHILE ( i<IARGC() )
    i=i+1 !index of current command-line argument
    CALL GETARG(i,clarg)
    !
    !Catch special keywords: 'version', 'help', 'license'
    !they print something and then exit the program
    IF(clarg=='--version' .OR. clarg=='-version' .OR. clarg=='-V') THEN
      CALL DISPLAY_COPYRIGHT()
      GOTO 1100
    ELSEIF(clarg=='--help' .OR. clarg=='help') THEN
      i=i+1
      CALL GETARG(i,clarg)
      READ(clarg,*,END=110,ERR=110) helpsection
      IF( helpsection.NE.'general' .AND. helpsection.NE.'modes' .AND.   &
        & helpsection.NE.'options' .AND. helpsection.NE.'formats'     ) &
        & helpsection='general'
      110 CONTINUE
      CALL DISPLAY_HELP(helpsection)
      GOTO 1100
    ELSEIF(clarg=='--license' .OR. clarg=='--licence') THEN
      CALL DISPLAY_LICENSE()
      GOTO 1100
    !
    !Catch keywords setting the behavior of the program
    ELSEIF(clarg=='-ignore' .OR. clarg=='-ig') THEN
      ignore = .TRUE.
    ELSEIF(clarg=='-overwrite' .OR. clarg=='-ow') THEN
      overw = .TRUE.
    ELSEIF(clarg=='-verbose' .OR. clarg=='--verbose') THEN
      verbosity = 4
    ELSEIF(clarg=='-verbosity' .OR. clarg=='-v') THEN
      i=i+1
      CALL GETARG(i,clarg)
      READ(clarg,*,END=120,ERR=120) verbosity
    ELSEIF(clarg=='-v0') THEN
      verbosity = 0
    ELSEIF(clarg=='-v1') THEN
      verbosity = 1
    ELSEIF(clarg=='-v2' .OR. clarg=='-vv') THEN
      verbosity = 2
    ELSEIF(clarg=='-v3' .OR. clarg=='-vvv') THEN
      verbosity = 3
    ELSEIF(clarg=='-v4' .OR. clarg(1:5)=='-vvvv' .OR. clarg=='-debug') THEN
      verbosity = 4
    ELSEIF(clarg=='-log') THEN
      i=i+1
      CALL GETARG(i,clarg)
      logfile = TRIM(ADJUSTL(clarg))
      IF(logfile=='') logfile='atomsk.log'
      IF( verbosity<=1 ) THEN
        !Set verbosity level to 3 = write info on screen + in log file
        verbosity = 3
      ENDIF
    ELSEIF(clarg=='-lang' .OR. clarg=='-language') THEN
      i=i+1
      CALL GETARG(i,clarg)
      READ(clarg,*,END=120,ERR=120) lang
      verbosity = 4
    ELSEIF(clarg=='-Nthreads' .OR. clarg=='-nthreads') THEN
      i=i+1
      CALL GETARG(i,clarg)
      READ(clarg,*,END=120,ERR=120) Nthreads
      IF(Nthreads<0) Nthreads=0
      !
    ELSE
      !Store command-line argument(s) in cla(:)
      j=j+1 !index in cla(:)
      cla(j) = clarg
      !
      IF(clarg=="-options" .OR. clarg=="-option" .OR. clarg=="-opt") THEN
        mode=""
        !Read options from a file
        i=i+1
        CALL GETARG(i,clarg)
        CALL CHECKFILE( clarg, "read" )
        j=j-1  !the command-line argument "-options <file>" will be replaced by options/arguments read from the file
        OPEN(UNIT=31,FILE=clarg,FORM="FORMATTED",STATUS="OLD")
        DO
          opt_file(:) = ''
          READ(31,'(a128)',ERR=115,END=115) temp
          temp = ADJUSTL(temp)
          !Ignore empty lines and lines starting with dash sign
          !Also ignore lines that use the keyword "options" (no redundancy here)
          IF( LEN_TRIM(temp)>0 .AND. temp(1:1).NE."#" .AND. temp(1:7).NE."options" .AND. temp(1:7).NE."-options" ) THEN
            n=0
            !Read option name and parameters
            DO WHILE (LEN_TRIM(temp).NE.0)
              n=n+1
              strlength = SCAN( temp , " " )
              opt_file(n) = temp(1:strlength)
              temp = ADJUSTL( temp(LEN_TRIM(opt_file(n))+1:) )
            ENDDO
            !
            !The option and its arguments are now in opt_file(:)
            !=> store them in cla(:)
            IF(ALLOCATED(cla_new)) DEALLOCATE(cla_new)
            IF( j+2==i ) THEN
              !Current option is the first one read from file
              !=> the two arguments "-option <file>" must be removed from cla(:)
              ALLOCATE(cla_new(SIZE(cla)+n-2))
            ELSE
              !Current option is not the first one => it comes after previous option
              ALLOCATE(cla_new(SIZE(cla)+n))
            ENDIF
            cla_new(:) = ""
            !Store command-line arguments preceeding "-options" at the beginning of cla_new(:)
            DO m=1,j
              cla_new(m) = cla(m)
            ENDDO
            !Store option read from the file in cla_new(:)
            ioptions=ioptions+1
            n=1
            DO WHILE (LEN_TRIM(opt_file(n)).NE.0)
              j=j+1
              cla_new(j) = opt_file(n)
              n=n+1
            ENDDO
            IF(ALLOCATED(cla)) DEALLOCATE(cla)
            ALLOCATE(cla(SIZE(cla_new)))
            cla(:) = cla_new(:)
            IF(ALLOCATED(cla_new)) DEALLOCATE(cla_new)
          ENDIF
        ENDDO
        115 CONTINUE
        CLOSE(31)
      !
      !If it starts with a double dash sign, it is probably a mode => do NOT run in interactive mode
      ELSEIF(clarg(1:2)=='--') THEN
        mode=""
        Nmodes = Nmodes+1
        !If several modes are present in command-line, display error and exit program
        IF( Nmodes>1) THEN
          CALL ATOMSK_MSG(4900,(/''/),(/0.d0/))
          GOTO 1000
        ENDIF
      !
      !If it is just "-", output to stdout and set verbosity to 0
      ELSEIF(clarg=='-') THEN
        ofu=6
        verbosity=0
      !
      !If it was none of the above but starts with a dash sign
      !then it is most probably an option
      ELSEIF(clarg(1:1)=='-') THEN
        ioptions=ioptions+1
        IF( IARGC()==1 ) THEN
          !Program was called only with option name
          !=> display help and leave
          CALL DISPLAY_HELP(clarg)
          GOTO 1100
        ENDIF
      !
      ELSE
        !some other command-line argument: can be a file name, etc.
      !
      ENDIF
      !
    ENDIF
    !
    120 CONTINUE
  ENDDO
  !
  IF( j==0 ) THEN
    mode = "interactive"
  ENDIF
  !
ENDIF
!
IF(verbosity<2) THEN
  !Low verbosity: no logfile is written, unit 20 is used as a virtual logfile
  OPEN(UNIT=20,STATUS='SCRATCH',FORM='FORMATTED')
ELSE
  !High verbosity: logfile is written (and replaced if one already exists)
  OPEN(UNIT=20,FILE=logfile,STATUS='REPLACE',FORM='FORMATTED')
ENDIF
!
!Allocate array for the options; this array will be
!filled by the routine "GET_CLA" below
ALLOCATE(options_array(ioptions))
options_array(:) = ''
!
!
!Print a nice message
IF( verbosity>0 ) THEN
  CALL DISPLAY_HEADER()
ENDIF
!Print message of the day
CALL DATE_MSG()
!
!
!!Read command-line options and try to understand them
CALL GET_CLA(cla,mode,options_array,outfileformats,pfiles,mode_param)
IF(ALLOCATED(cla)) DEALLOCATE(cla)
IF(nerr>0) GOTO 500
!
!
!
200 CONTINUE
!Deal with options that are not compatible
!Cannot both ignore and overwrite existing files: set overwrite to false
IF(overw.AND.ignore) overw=.FALSE.
!
!If user specified number of threads, set it now
IF(Nthreads>0) THEN
#if defined(OPENMP)
  CALL OMP_SET_NUM_THREADS(Nthreads)
#else
  CALL ATOMSK_MSG(751,(/"command line"/),(/0.d0/))
#endif
ENDIF
!
!Then deal with the different modes
IF( mode=="interactive" ) THEN
  CALL INTERACT()
ELSE
  CALL RUN_MODE(mode,options_array,outfileformats,pfiles,mode_param)
ENDIF
!
!
!
500 CONTINUE
!Terminating program
!
!
!
1000 CONTINUE
!Get the current date and time
CALL DATE_AND_TIME(DATE, TIME, ZONE, VALUESf)
!Compute the difference with previous time
VALUESf(:) = VALUESf(:) - VALUESi(:)
!Total time may not be correct when passing midnight and changing month,
!but what are the odds anyway? Give me a break.
time_total = 86400.d0*DBLE(VALUESf(3)) + &
           & 3600.d0*DBLE(VALUESf(5)) + 60.d0*DBLE(VALUESf(6)) + &
           & DBLE(VALUESf(7)) + 1.d-3*DBLE(VALUESf(8))
CALL CPU_TIME(cpu_t2)
 cpu_t2 = cpu_t2 - cpu_t1
!CALL ATOMSK_MSG(2,(/''/),(/time_total, cpu_t2/))
CALL ATOMSK_MSG(1,(/''/),(/time_total, cpu_t2/))
!
!
1100 CONTINUE
!Free memory
IF(ALLOCATED(options_array)) DEALLOCATE(options_array)
!Close logfile (if opened)
INQUIRE(UNIT=20,OPENED=logopened)
IF(logopened) CLOSE(20)
!
!
!
END PROGRAM atomsk
