#!/bin/bash

#set -vx

#=============================================================================
#  F. Hourdin : 2022/05/03
#
#  Objet :
#  -----
#  Script préparant les programes pour rejouer (replay) une paramétrisation
#  ou un morceau de paramétriation.
#
#
#  Applicabilité :
#  ---------------
#  Est fait pour rejouer des paramétisation de LMDZ organisées comme
#    des calcul sur des successions de colonnes indépendantes (boucle
#    interne en $klon) de $klev niveaux verticaux.
#  Demande que la paramétrisation répondent aux règles de codage suivantes :
#  1) Les routines de calcul et les routines ou fonctions qu'elles appellent
#     n'ont que deux interfaces : A) les arguments d'entrée
#                                 B) le use d'un unique module
#     Entre autre, les dimensions de la paramétrisation sont passés
#     en argument, ce qui permet une allocation dynamique implicite
#     de toutes les variables.
#  2) le module d'initialisation doit lui même être initialisé par 
#     une unique routine pilotée entièrement par ses arguments.
#  3) Toutes les variables d'interface doivent être déclarées intent in
#     out, ou inout
#  On appelera la paramétriation "param" et l'intialisation "param_ini"
#     mais les noms de fichiers sont en argument dans le script.
#
#
#  Principe :
#  ----------
#  Dans une première simulation, on écrit dans un fichier binaire toutes
#  les variables "intent in/inout" de la routine d'initialisation et de la routine
#  de calcul.
#
#
#  En pratique :
#  -------------
#
#  Le script a comme argument le nom de la routine à traiter, nommée $param
#   Des correpspondances en déduisent :
#   paramini=wake_ini  # nom de la subroutine d'intialisation
#   inimod= # nom du module contenant $param_ini
#   klon=klon ; klev=klev  # nom des dimensions horiz; vert utilisée
#
#  Le fichier ${paramfile} contenant $param est detecté automatiquement
#  Le script crée 5 fichiers
#  * dump_param1.h          : écriture de l'interface "in" de param (dump_param.bin fort.82)
#  * dump_param2.h          : écriture des sorties de la routines dans phys.nc
#  * call_param_replay : sous programme d'appelle en boucle à la param
#  * dump_ini_module.F90   : écriture de l'interface "in" de param_ini
#  * call_ini_replay.F90    : lecture de l'interface "in" de param_ini
#                dans ${param_ini}_mod.bin.
#  Par ailleurs, un programme replay1d a été ajouté dans phylmd/dyn1d
#        qui appelle call_param_replay
#  Le script ajoute par ailleurs quelques lignes dans ${paramfile} et
#        ${param_ini}.F90
#  replay_clean.sh élimine toutes les lignes ajoutées 
#
#
#  A travailler :
#  --------------
#  * détecter automatiquement le fichier contenant l'intialisation
#  * détecter automatiquement les dimensions
#  * avoir un moyen de vérifier si les variables intent in et out sont
#    bien les bonnes.
#  * Initialisation plus simple de la routine getin_p
#  * Des choix sur la facon de controler l'initialisation et le pb des getin
#
#=============================================================================

#-----------------------------------------------------------------------------
# Reading rguments
#-----------------------------------------------------------------------------
nconly=false
where=-before_return

if [ $# = 0 ] ; then $0 -h ; exit ; fi
while (($# > 0))
   do
   case $1 in
     -h|--help) cat <<........fin
           $0 [-nconly] [-ncvars var1,var2,...] [-pre prefix] [location_in_the_code] routine_name
           The prefix will be put on each variable stored in the nc file to avoid duplicate
                variables names
           If -ncvars is not specified, all the variables are output
           The prefix is used to prevent having twice the same name if the same variable is
              output at two locations in the code.
           location_in_the_code can be : -before_return               (the default valuer)
                                         -after_declarations
                                         -before_line  "line in code"
                                         -after_line   "line in code"
                                         -before_call  "param_name"   (TO BE DONE)
                                         -after_call   "param_name"   (TO BE DONE)
........fin
        exit ;;
     -nconly) nconly=true ; shift ;;
     -before_line|-after_line|-before_call|-after_call) where="$1 $2" ; shift ; shift ;;
     -before_return|-after_declarations) where=$1 ; shift ;;
     -pre) prefix=$2 ; shift ; shift ;;
     -ncvars) ncvars="`echo $2 | sed -e 's/,/ /g'`" ; shift ; shift ;;
     *) if (( $# > 1 )) ; then $0 -h ; exit ; fi ; param=`basename $1 .F90` ; shift
    esac
done
if [ "$param" = "" ] ; then $0 -h ; exit ; fi

case $param in
   vdif_k) paramini=None ; inimod=None ; klon=ngrid ; klev=nlay ;;
   thermcell_main|thermcell_plume_6A|thermcell_env|thermcell_height|thermcell_dry|\
      thermcell_closure|thermcell_height|thermcell_dq|thermcell_flux2|thermcell_down| \
      thermcell_updown_dq) \
      paramini=thermcell_ini ; inimod=lmdz_thermcell_ini ; klon=ngrid ; klev=nlay ;;
   wake|wake_popdyn_1|wake_popdyn_2) paramini=wake_ini ; inimod=lmdz_wake_ini ; klon=klon ; klev=klev ;;
   *) echo Cas non prevu ; exit
esac

replay_comment="replay automatic include"
paraminc1=dump_replay_${param}_head.h
paraminc2=dump_replay_${param}_nc_${prefix}.h
iniinc=dump_replay_ini.h
paramfile=`grep -i "subro.* ${param}[\ (]" *.F90 | sed -e 's/ //g' | grep "${param}(" | cut -d: -f1`
echo ===================================================================================
echo Equiping $param contained in file $paramfile
if [ `echo ${paramfile} | wc -w` != 1 ] ; then echo file $paramfile multiple  ; $0 -h ; exit ; fi


#-----------------------------------------------------------------------------
# Transformer l'entete d'une subroutine en un call
#-----------------------------------------------------------------------------

function get_subroutine_arg(){
   cat $2 | tr '[A-Z]' '[a-z]' > tmp
   line1=`sed -n -e '/subrou.*'\`echo $1 | tr '[A-Z]' '[a-z]'\`'.*(/=' tmp | head -1 `
   line2=`tail -n +$line1 tmp | sed -n -e '/)/=' | head -1`
   tail -n +$line1 tmp | sed -n -e 1,${line2}p
}

#-----------------------------------------------------------------------------
function dump_param_open(){
    #-----------------------------------------------------------------------------
    # Opening an direct access file with one record per time-step 
    # Each record has the size and contains the arguments in and inout of the
    # routine
    #-----------------------------------------------------------------------------
    inout=$1 ; shift
    case $inout in
       out) fort=81 ;;
       in) fort=82
    esac
    echo '! <<< dump_param_open'
    for var in $* ; do echo 'rec_length_replay=rec_length_replay+kind('$var')*size(['$var'])' ; done
    cat <<....eod
    open(${fort},file='dump_param_${inout}.bin',form='unformatted',access='direct',recl=rec_length_replay)  ! $replay_comment
....eod
    echo '! dump_param_open >>> '
}


#-----------------------------------------------------------------------------
function extract_subroutine(){
#-----------------------------------------------------------------------------
   # $1 nom de la subroutine
   # $2 nom du fichier
   # input <- routine under treatment with small caps only
   ( cpp $2 2>/dev/null ) | tr '[A-Z]' '[a-z]' > tmp
   name_min=`echo $1 | tr '[A-Z]' '[a-z]'`
   line1=`sed -n -e "/subrou.*${name_min}.*(/=" tmp | head -1 `
   tail -n +$line1 tmp > tmp2
   line2=`sed -n -e "/[Rr][Ee][Tt][Uu][Rr][Nn]/=" tmp2 | head -1`
   head -$line2 tmp2  > input
   \rm -f tmp tmp2
}


#-----------------------------------------------------------------------------
function echo_use_module(){
#-----------------------------------------------------------------------------
# Regle tacite : l'instruction MODULE commence à la premiere colonne.
#                Existe-t-i une facon de la faire tomber ?
#                En enlevant tous les blanc dans le input sans doute ...
   param_=$1 ; shift
   paramfile_=$1
   if [ ! -f $paramfile_ ] ; then echo plantage which_module ; exit 1 ; fi
   module=`grep '^[mM][oO][dD][uU][lL][eE] .*[a-Z]' $paramfile_ | head -1 | awk ' { print $2 } '`
   if [ "$module" != "" ] ; then
       echo USE $module, ONLY : $param_
   fi
}

#-----------------------------------------------------------------------------
function include_line(){
#-----------------------------------------------------------------------------
   # Including param_dump*.h in the parameterization
   #set -vx
   line_to_be_included=$1  ; shift
   param_=$1 ; shift
   paramfile_=$1 ; shift
   name_min=`echo $param_ | tr [A-Z] [a-z]`
   line_subroutine=`cat $paramfile_ | tr '[A-Z]' '[a-z]' | sed -n -e "/subrou.*${name_min}.*(/=" | head -1 `
   tail -n +$line_subroutine $paramfile_ > tmp # file starting at the subroutine instruction
   line_return=`sed -n -e "/[Rr][Ee][Tt][Uu][Rr][Nn]/=" tmp | head -1`
   head -$line_return tmp > tmp2               # file containing the routine
   sed -e 's/\!.*$//' tmp2 > tmp3
   cpp tmp2 > tmp3 2>/dev/null
   cat tmp3 | tr '[A-Z]' '[a-z]' > tmp2_cpp   # same after cpp filtering
   last_line_declaration2="`sed -n -e 's/\!.*$//' -e 's/^/ /' -e '/[\ ,]real[\ ,]/p' -e '/[\ ,]integer[\ ,]/p' -e '/[\ ,]logical[\ ,]/p' -e '/[\ ,]character[\ ,]/p' tmp2_cpp | tail -1 | sed -e 's/  / /g' -e 's/ /.*/g'`"
   line_last_declaration=`cat tmp2 | tr '[A-Z]' '[a-z]' | sed -n -e "/$last_line_declaration2/="`
   echo OK0
   if [ "`grep -i 'end.*module' $paramfile_`" = "" ] ; then echo aka ;  line_end_module=`wc -l $paramfile_ | awk ' { print $1 } '` ; else echo akb ; line_end_module=`sed -n -e '/[eE][nN][dD] .*[mM][oO][dD][uU][lL][eE]/=' $paramfile_` ; fi
   echo OK1
   echo $line_end_module
   if [ "$line_last_declaration" = "" ] ; then echo line_last_declaration $line_last_declaration line $last_line_declaration2  ; exit ; fi
   if (( $# > 0 )) ; then
      wtype=`echo $1 | awk ' { print $1 } '`
      case $wtype in
         -after_declarations)      targeted_line=$line_last_declaration ;;
         -before_return)           targeted_line=$line_return ;;
         -before_end_module)       targeted_line=$line_end_module ;;
         -before_line|-after_line) linestr=`echo $1 | sed -e "s/$wtype //"` ; targeted_line=`sed -n -e "/$linestr/=" tmp2` ;;
         *)                  echo Cas non prevu 0 where=$where in include_file
      esac
      case $wtype in
         -after_declarations|-after_line)  line0=$(( $line_subroutine - 1 )) ;;
         -before_return|-before_line)      line0=$(( $line_subroutine - 2 )) ;;
         -before_end_module)               line0=-1 ;;
         *)                  echo Cas non prevu 1 where=$where in include_file
      esac
      echo Including line $line0 + $targeted_line in $paramfile_ the line : $line_to_be_included
      linebefore_include=$(( $line0 + $targeted_line ))
     sed -i'' -e $linebefore_include"s/$/\n $line_to_be_included \!  $replay_comment/" $paramfile_ 
   fi
}




#-----------------------------------------------------------------------------
function block_Replay0() {
#-----------------------------------------------------------------------------
condition=$1 ; shift
suf1=$1 ; shift
suf2=$1 ; shift
if [ $# -gt 0 ] ; then
   vars=$*
   echo '   if ('$condition') then'
   for var in $vars ; do echo '      '$var$suf1=$var$suf2 ; done
   echo '   endif'
fi
}

#-----------------------------------------------------------------------------
function iotd_calls(){
#-----------------------------------------------------------------------------
    klev_=$1 ; shift
    pre=$1 ; shift
    if (( $# >> 0 )) ; then
       vars=$*
       for var in $vars ; do
          echo "call iotd_ecrit_seq('"$pre$var"',$klev_,'"$pre$var in $param"',' ',"$var")"
       done
    fi
}
         
#-----------------------------------------------------------------------------
function b_among_a() {
#-----------------------------------------------------------------------------
   a="$1"
   b="$2"
   o=""
   for v in $a ; do
       for vv in $b ; do
           if [ "$v" = "$vv" ] ; then o="$o $v" ;  fi
       done
   done
   echo $o
}


#-----------------------------------------------------------------------------
# On nettoye les inclusions précédente dans les fichiers .F90
#-----------------------------------------------------------------------------

if [ $nconly = false ] ; then
   for file in `grep "$replay_comment" *.F90 2> /dev/null | cut -d: -f1` ; do
      sed -i"" -e "/$replay_comment/d" $file
   done
   line=`sed -n -e "/CALL wake_ini/=" physiq_mod.F90 | head -1`
   sed -i"" -e "${line}s/$/\n   CALL iophys_ini(pdtphys) ! $replay_comment  ! $replay_comment/" physiq_mod.F90
fi

#-----------------------------------------------------------------------------
# Analysis of the variables to be stored and there nature
#-----------------------------------------------------------------------------

extract_subroutine $param $paramfile # -> input file
varin0=`grep inten.*.in input | sed -e 's/\!.*$//' | cut -d: -f3 | sed -e 's/,/ /g'`
varinout0=`grep inten.*.inout input | sed -e 's/\!.*$//' | cut -d: -f3 | sed -e 's/,/ /g'`
varin=`echo $varin0 | sed -e 's/ /,/g' -e "s/,,,/,/g" -e "s/,,/,/g"`
output=full # Attention, l'option full ne marche pas a cause de tableaux locaux undef
nvar0D=0 ; nvar1D=0 ; nvar2D=0
case $output in
   light) search_str='real.*intent' ;;
   full) search_str='real'
esac
var_1D_inout=`grep -i "$search_str" input | grep intent.*inout | grep $klon | sed -e 's/!.*$//' -e /$klev/d | cut -d: -f3 | sed -e 's/,/ /g'`
var_2D_inout=`grep -i "$search_str" input | grep intent.*inout | grep $klon | grep $klev | cut -d: -f3 | sed -e 's/!.*$//' -e 's/,/ /g'`
var_1D_noarg=`grep -i "$search_str" input | sed -e /intent/d | grep $klon | sed -e 's/!.*$//' -e /$klev/d | cut -d: -f3 | sed -e 's/,/ /g'`
var_2D_noarg=`grep -i "$search_str" input | sed -e /intent/d | grep $klon | grep $klev | cut -d: -f3 | sed -e 's/!.*$//' -e 's/,/ /g'`
var_1D=`grep -i "$search_str" input | grep $klon | sed -e 's/!.*$//' -e /$klev/d | cut -d: -f3 | sed -e 's/,/ /g'`
var_2D=`grep -i "$search_str" input | grep $klon | grep $klev | cut -d: -f3 | sed -e 's/!.*$//' -e 's/,/ /g'`
if [ "$ncvars" != "" ] ; then
   var_1D=`b_among_a "$var_1D" "$ncvars"`
   var_2D=`b_among_a "$var_2D" "$ncvars"`
fi
echo Variables in and inout : $varin0
echo 1D variables "(all)" : $var_1D
echo 2D variables "(all)" : $var_2D
echo 1D variables "intent(inout)" : $var_1D_inout
echo 2D variables "intent(inout)" : $var_2D_inout
echo local 1D variables : $var_1D_noarg
echo local 2D variables : $var_2D_noarg

#-----------------------------------------------------------------------------
# Ecriture de la routine d'appel a la parametrisation en mode replay
#-----------------------------------------------------------------------------

if [ $nconly = false ] ; then

cat > call_param_replay.F90 <<eod
subroutine call_param_replay($klon,$klev)
USE IOIPSL, ONLY : getin
`echo_use_module $param  $paramfile`
IMPLICIT NONE
integer :: ifin,irec,it,rec_length_replay=0,replay_irec0=1,replay_nt=1000
eod
grep 'intent.*::' input | sed -e 's/ //g' -e 's/,intent(.*[nt])//'>> call_param_replay.F90
# duplicating declaration for inout variables

for var in $varinout0 ; do
    sed -e 's/::/ :: /' -e 's/,/ , /g' -e 's/$/ /' input | grep 'intent.*inout.*::.* '$var' ' | sed -e 's/ //g' -e 's/,intent(.*[nt])//' | sed -e 's/::.*$/, allocatable, save :: '$var'_Replay0/' | sed -e 's/'$klon'/:/' -e 's/'$klev'/:/' >> call_param_replay.F90
done
# Initalisation, shared with dump_param1.sh
dump_param_open "in" $varin0 >> call_param_replay.F90
for var in $varinout0 ; do
    sed -e 's/::/ :: /' -e 's/,/ , /g' -e 's/$/ /' input | grep 'intent.*inout.*::.* '$var' ' | sed -e 's/intent(.*t)//' -e 's/^.*(/allocate('$var'_Replay0(/' -e 's/).*$/))/' >> call_param_replay.F90
done

cat >> call_param_replay.F90 <<eod
call getin('replay_nt',replay_nt)
call getin('replay_irec0',replay_irec0)
do it=1,replay_nt
   if (replay_irec0>=0) then
       irec=replay_irec0+it-1
   else
       irec=-replay_irec0
   endif
   print*,'Time step',it,', reading record',irec,'in dump_param.bin'
   read(82,rec=irec,iostat=ifin) $varin
   if (.NOT. ifin == 0 ) stop "Fin du fichier fort.82"
eod

block_Replay0 "it == 1"          _Replay0   ""       $varinout0 >> call_param_replay.F90
block_Replay0 "replay_irec0 < 0" ""         _Replay0 $varinout0 >> call_param_replay.F90
get_subroutine_arg $param $paramfile | sed -e 's/subroutine/   call/' >> call_param_replay.F90
block_Replay0 "replay_irec0 < 0" _Replay0   ""       $varinout0 >> call_param_replay.F90
cat >> call_param_replay.F90 <<eod
enddo
return
end
eod

fi # nconly = false

#-----------------------------------------------------------------------------
# Creating file dump_${param}_head.h
#-----------------------------------------------------------------------------

if [ $nconly = false ] ; then
\rm $paraminc1
cat> $paraminc1 <<eod
logical, save :: first_replay=.true.
integer, save :: rec_length_replay=0,irec=0
if (first_replay) then
eod
dump_param_open out $varin0 >> $paraminc1
cat>> $paraminc1 <<eod
first_replay=.false.
endif
irec=irec+1
write(81,rec=irec) $varin
eod
iotd_calls 1     in_${prefix} $var_1D_inout >> $paraminc1
iotd_calls $klev in_${prefix} $var_2D_inout >> $paraminc1
fi # nconly = false

if [ "`grep $paraminc1 $paramfile`" = "" ] ; then
   # Not changing $paraminc1 if it is already included in the file
   # To allow adding new nc outputs in a routine equiped for replay
   for var in $var_1D_noarg $var_2D_noarg ; do echo $var=0. >> $paraminc1 ; done
   include_line "include \"$paraminc1\"" $param $paramfile -after_declarations
fi

#-----------------------------------------------------------------------------
# Creating file dump_${param}_nc_${prefix}.h
#-----------------------------------------------------------------------------

\rm $paraminc2
iotd_calls 1     "${prefix}" $var_1D >> $paraminc2
iotd_calls $klev "${prefix}" $var_2D >> $paraminc2
include_line "include \"$paraminc2\"" $param $paramfile "$where"
  
#-----------------------------------------------------------------------------
# dump_ini_module gere l'ecriture des variables d'interface de l'intialisation
# du module en mode replay
#-----------------------------------------------------------------------------

if [ $nconly = false -a $paramini != None ] ; then
   varinmod=`grep -i 'intent.in' $inimod.F90 | sed -e 's/\!.*$//' | cut -d: -f3 | sed -e 's/,/ /g'`
   varinmodv=`echo $varinmod | sed -e 's/ /,/g'`
   cat > $iniinc <<...eod...............................................
   open(90,file='$inimod.bin',form='unformatted')
   write(90) $varinmodv
   close(90)
...eod...............................................
   include_line "include \"$iniinc\"" $paramini $inimod.F90  -before_return
fi # nconly = false


#-----------------------------------------------------------------------------
# call_ini_replay.F90 gere l'initialisation du module en mode replay
#-----------------------------------------------------------------------------
if [ $nconly = false -a $paramini != None ] ; then
    cat > call_ini_replay.F90 <<....eod............................................
    subroutine call_ini_replay
    use $inimod
    IMPLICIT NONE
....eod............................................
    grep -i intent.in $inimod.F90  |  tr '[A-Z]' '[a-z]' | sed -e 's/,.*intent.i.*)//' >> call_ini_replay.F90
    cat >> call_ini_replay.F90 <<....eod...........................................
    open(90,file='$inimod.bin',form='unformatted')
    read(90) $varinmodv
    close(90)
....eod...........................................
    #get_subroutine_arg $paramini $inimod.F90 ; exit
    get_subroutine_arg $paramini $inimod.F90 | sed -e 's/subroutine/call/' >> call_ini_replay.F90
    cat >> call_ini_replay.F90 <<....eod...........................................
    return
    end
....eod...........................................
fi # nconly = false
