!= NAMELIST ファイル入力に関するユーティリティ
!
!= Utilities for NAMELIST file input
!
! Authors::   Yasuhiro MORIKAWA
! Version::   $Id: gridset.f90,v 1.5 2007/06/09 13:12:30 sugiyama Exp $ 
! Tag Name::  $Name:  $
! Copyright:: Copyright (C) GFD Dennou Club, 2008. All rights reserved.
! License::   See COPYRIGHT[link:../../../COPYRIGHT]
!

module namelist_util
  !
  != NAMELIST ファイル入力に関するユーティリティ
  !
  != Utilities for NAMELIST file input
  !
  !== Procedures List
  !
  ! NmlutilInit      :: NAMELIST ファイル名の設定
  ! NmlutilMsg       :: NAMELIST ファイル入力に関するメッセージ表示
  ! NmlutilAryValid  :: NAMELIST ファイルから読み込んだ配列の有効性をチェック
  ! ------------  :: ------------
  ! NmlutilInit      :: Settings of NAMELIST file name
  ! NmlutilMsg       :: Print messages about NAMELIST file input
  ! NmlutilAryValid  :: Check validation of arrays loaded from NAMELIST file

  ! モジュール引用 ; USE statements
  !

  ! 種別型パラメタ
  ! Kind type parameter
  !
  use dc_types, only: STRING  ! 文字列. Strings. 

  ! 宣言文 ; Declaration statements
  !
  implicit none
  private

  ! 公開手続き
  ! Public procedure
  !
  public:: NmlutilInit, NmlutilMsg, NmlutilAryValid

  ! 公開変数
  ! Public variables
  !
  logical, save, public:: initialized = .false.
                              ! 初期設定フラグ. 
                              ! Initialization flag
  character(STRING), save, public:: namelist_filename = ''
                              ! NAMELIST ファイルの名称. 
                              ! NAMELIST file name
  integer, save, public:: MaxNmlArySize = 256
                              ! NAMELIST から読み込む配列の最大サイズ. 
                              ! Maximum size of arrays loaded from NAMELIST

  ! 非公開変数
  ! Private variables
  !

  !  NAMELIST 変数群
  !  NAMELIST group name
  !
  namelist /namelist_util_nml/ MaxNmlArySize

  character(*), parameter:: module_name = 'namelist_util'
                              ! モジュールの名称. 
                              ! Module name
  character(*), parameter:: version = &
    & '$Name:  $' // &
    & '$Id: $'
                              ! モジュールのバージョン
                              ! Module version

  ! INTERFACE 文 ; INTERFACE statements
  !
  interface NmlutilInit
    module procedure NmlutilInit
  end interface

  interface NmlutilMsg
    module procedure NmlutilMsg
  end interface

  interface NmlutilAryValid
    module procedure NmlutilAryValid
  end interface

contains

  subroutine NmlutilInit
    !
    ! namelist_util モジュールの初期設定を行います. 
    !
    ! Initialize "namelist_util" module. 
    !

    ! モジュール引用 ; USE statements
    !

    ! ファイル入出力補助
    ! File I/O support
    !
    use dc_iounit, only: FileOpen

    ! コマンドライン引数解析処理
    ! Command line option parser
    !
    use option_parser, only: opt_nml_file => namelist_filename

    ! 宣言文 ; Declaration statements
    !
    implicit none

    integer:: unit_nml        ! NAMELIST ファイルオープン用装置番号. 
                              ! Unit number for NAMELIST file open
    integer:: iostat_nml      ! NAMELIST 読み込み時の IOSTAT. 
                              ! IOSTAT of NAMELIST read

    ! 実行文 ; Executable statement

    if ( initialized ) return
    call InitCheck
    namelist_filename = opt_nml_file

    ! NAMELIST の読み込み
    ! NAMELIST is input
    !
    call FileOpen( unit_nml, &          ! (out)
      & namelist_filename, mode = 'r' ) ! (in)

    rewind( unit_nml )
    read( unit_nml, &               ! (in)
      & nml = namelist_util_nml, &  ! (out)
      & iostat = iostat_nml )       ! (out)
    close( unit_nml )

    call NmlutilMsg( iostat_nml, module_name ) ! (in)

    initialized = .true.
  end subroutine NmlutilInit

  subroutine NmlutilMsg( iostat, module_name ) ! (in)
    !
    ! NAMELIST ファイル入力ステータスから
    ! 適切なメッセージを表示します. 
    !
    ! Appropriate messages are output from 
    ! status of NAMELIST file loading. 
    !

    ! メッセージ出力
    ! Message output
    !
    use dc_message, only: MessageNotify
    implicit none
    integer, intent(in):: iostat
                              ! NAMELIST 読み込み時のステータス. 
                              ! Status of NAMELIST loading
    character(*), intent(in):: module_name
                              ! モジュールの名称. 
                              ! Module name
  continue
    if ( iostat == 0 ) then
      call MessageNotify( 'M', '', &
        & 'NAMELIST group "%c" is loaded from "%c".', &
        & c1 = trim(module_name) // '_nml', &
        & c2 = trim(namelist_filename) )
    else
      call MessageNotify( 'W', '', &
        & 'NAMELIST group "%c" is not found in "%c" (iostat=%d).', &
        & c1 = trim(module_name) // '_nml', &
        & c2 = trim(namelist_filename), &
        & i = (/iostat/) )
    end if
  end subroutine NmlutilMsg

  subroutine NmlutilAryValid( name, &             ! (in)
    & array, array_name, need_num, need_num_name, & ! (in)
    & valid_limit )                                 ! (in) optional
    !
    ! NAMELIST から読み込んだ配列型データの妥当性を
    ! チェックします. 
    ! 
    ! デフォルトでは, 正の値を有効と扱います. 
    ! 無効であると検証された場合には, エラーを発生させます. 
    ! 
    ! Check validation of array data loaded from NAMELIST. 
    !
    ! By defaut, positive values are treated as valid values.
    ! If invalidation is checked, an error is occurred. 
    ! 

    ! モジュール引用 ; USE statements
    !

    ! 種別型パラメタ
    ! Kind type parameter
    !
    use dc_types, only: DP      ! 倍精度実数型. Double precision. 

    ! メッセージ出力
    ! Message output
    !
    use dc_message, only: MessageNotify

    ! 宣言文 ; Declaration statements
    !
    implicit none
    character(*), intent(in):: name
                              ! このサブルーチンを呼び出すモジュールの名称. 
                              ! Module name calling this subroutine
    real(DP), intent(in):: array(:)
                              ! 検証すべき配列データ. 
                              ! Checked array data 
    character(*), intent(in):: array_name
                              ! 検証すべき配列データの名前. 
                              ! Name of checked array data 
    integer, intent(in):: need_num
                              ! 必要なデータ数. 
                              ! 1 未満の数を与えるとエラーを生じます. 
                              ! 
                              ! Number of needed data. 
                              ! If number less than 1, an error is occurred. 
    character(*), intent(in):: need_num_name
                              ! 必要なデータ数を示す変数の名前. 
                              ! Name of a variable that indicates number of needed data
    real(DP), intent(in), optional:: valid_limit
                              ! 有効下限値 (デフォルトは 0.0). 
                              ! Lower limit of validation (defalt is 0.0) 

    ! 作業変数
    ! Work variables
    !
    real(DP):: valid_limit_work
                              ! 有効下限値 (デフォルトは 0.0). 
                              ! Lower limit of validation (defalt is 0.0) 
    integer:: valid_count     ! 配列データの有効値の数. 
                              ! Number of valid values in an array
    integer:: size_array      ! 配列データのサイズ
                              ! Size of array data

    ! 実行文 ; Executable statement

    ! need_num が正であることをチェック
    ! Check that "need_num" is positive
    !
    if ( need_num < 1 ) then
      call MessageNotify( 'E', name, '%c=<%d> must be more than 0.', &
        &                 c1 = trim(need_num_name), i = (/ need_num /) )
    end if

    ! array のサイズが十分であることをチェック
    ! Check that size of "array" is enough
    !
    size_array = size(array)
    if ( need_num > size_array ) then
      call MessageNotify( 'W', name, &
        &  'Maximum size=<%d> of "%c" is too smaller than %c=<%d>. ' // &
        &  'Please add a following phrase to NAMELIST file. ' // &
        &  ' "&namelist_util_nml  MaxNmlArySize=%d /"', &
        &  i = (/ size_array, need_num, size_array /), &
        &  c1 = trim(array_name), c2 = trim(need_num_name) )
    end if

    ! array をチェック
    ! Check "array"
    !
    valid_limit_work = 0.0_DP
    if ( present( valid_limit ) ) valid_limit_work = valid_limit

    if ( any( array(1:need_num) < valid_limit_work ) ) then
      valid_count = count( .not. ( array(1:need_num) < valid_limit_work ) )
      if ( valid_count > 0 ) then
        call MessageNotify( 'E', name, &
          &   'Number of valid data of %c=<%*f> is %d. ' // &
          &   'Valid data is %c=<%d> necessary.', &
          &   c1 = trim( array_name ), c2 = trim( need_num_name ), &
          &   d = array(1:valid_count), n = (/ valid_count /), &
          &   i = (/ valid_count, need_num /) )
      else
        call MessageNotify( 'E', name, &
          &   'Valid data of %c is nothing. ' // &
          &   'Valid data is %c=<%d> necessary.', &
          &   c1 = trim( array_name ), c2 = trim( need_num_name ), &
          &   i = (/ need_num /) )
      end if
    end if

  end subroutine NmlutilAryValid

  subroutine InitCheck
    !
    ! 依存モジュールの初期化チェック
    !
    ! Check initialization of dependency modules

    ! モジュール引用 ; USE statements
    !

    ! コマンドライン引数解析処理
    ! Command line option parser
    !
    use option_parser, only: option_parser_init => initialized

    ! メッセージ出力
    ! Message output
    !
    use dc_message, only: MessageNotify

    ! 実行文 ; Executable statement

    if ( .not. option_parser_init ) &
      & call MessageNotify( 'E', module_name, '"option_parser" module is not initialized.' )

  end subroutine InitCheck

!!$  subroutine InitDepmod
!!$    !
!!$    ! 依存モジュールの初期化
!!$    !
!!$    ! Initialize dependency modules
!!$
!!$    ! モジュール引用 ; USE statements
!!$    !
!!$
!!$    ! コマンドライン引数解析処理
!!$    ! Command line option parser
!!$    !
!!$    use option_parser, only: OptParseInit
!!$  continue
!!$
!!$    call OptParseInit
!!$
!!$  end subroutine InitDepmod

end module namelist_util
