!> \~japanese
!! \brief 各タイムレベルの速度場や高度場といった物理場データを管理するモジュール.
!! \~english
!! \brief This module provides the data of some physical fields at each time level, such as velocity and height field.
!!
!! <br><br>
!! \b Copyright (C) GFD Dennou Club, 2011-2012. All rights reserved. <br>
!! \b license ?? <br>
!! \author Yuta Kawai
!!
module field_manager

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

  !* gtool **
  !* 

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

  !* IGMBaseLib **
  !*

  ! 正二十面体格子を管理する管理するクラス
  ! Manager class of icosahedral grid
  !
  use IcGrid2D_FVM_Manager, only: &
    & IcGrid2D_FVM

  ! 正二十面体格子上の物理場データを管理するクラス
  ! Manager class of a physical field data on an icosahedral grid
  !
  use Field_IcGrid2D_Manager, only: &
    & Field_IcGrid2D, &
    & Field_IcGrid2D_Init, paste_field2D_margin
  
  ! 正二十面体格子上の物理場に対して微分演算を提供するクラス
  ! Class providing some differential operators acting on a physcial field on icosahedral grid
  !
  use Derivate_Field_IcGrid2D_Manager, only: &
    & Derivate_Field_IcGrid2D, Derivate_Field_IcGrid2D_Init

  ! 典型的な物理場パターンの生成
  ! Generation of the typical physical fields pattern
  !
  use Field_Pattern_Builder, only: &
    & create_planetaryVorticity_field

  !
  !
  !
  use IcGrid_ncWriter_mod, only: &
    & IcGrid_ncWriter, &
    & IcGrid_ncWriter_Init

  !* IGModel-SW
  !*

  ! シミュレーションパラメータの管理
  ! Manager of simulation parameters
  !
  use param_manager, only: &
    & alpha, Omega

  ! 宣言文 ; Declaration statements
  !
  implicit none
  private
  
  ! 公開変数
  ! Public variables
  !

  ! 各時間レベルを表す配列インデックス管理する変数の宣言.
  ! 時間微分を含む物理場変数は, そうでない変数に対して分別的に時間レベルを管理する.
  !

  !> \~japanese 時間微分を含まない物理場変数の時間レベル n+1 を表す配列インデックスを管理する変数.
  !! \~english
  integer, public :: TL_Nplus1 = 1
  
  !> \~japanese 時間微分を含まない物理場変数の時間レベル n を表す配列インデックスを管理する変数.
  !! \~english
  integer, public :: TL_N      = 2

  !> \~japanese 時間微分を含まない物理場変数に対する時間レベルの総数.
  !! \~english
  integer, parameter, public :: TL_NUMS = 2

  !> \~japanese 時間微分を含む物理場変数の時間レベル n を表す配列インデックスを管理する変数.
  !! \~english
  integer, public :: TL_DDT_N       = 1
  
  !> \~japanese 時間微分を含む物理場変数の時間レベル n-1 を表す配列インデックスを管理する変数.
  !! \~english
  integer, public :: TL_DDT_Nminus1 = 2
  
  !> \~japanese 時間微分を含む物理場変数の時間レベル n-2 を表す配列インデックスを管理する変数.
  !! \~english
  integer, public :: TL_DDT_Nminus2 = 3

  !> \~japanese 時間微分を含む物理場変数に対する時間レベルの総数.
  !! \~english
  integer, parameter, public :: TL_DDT_NUMS = 3

  ! 正二十面格子上の物理場データを管理する構造型 Field_IcGrid2D の変数の宣言.
  !

  !> \~japanese 惑星渦度場データを管理する構造型 Field_IcGrid2D の変数.
  !! \~english The variable of derived type Field_IcGrid2D to manage the field data of planetary vorticity.
  type(Field_IcGrid2D), public, save :: xy_Coli
  
  !> \~japanese 下部境界の地形の高度場データを管理する構造型 Field_IcGrid2D の変数.
  !! \~english The variable of derived type Field_IcGrid2D to manage the field data of the height of underlying mountain.
  type(Field_IcGrid2D), public, save :: xy_Htopo

  !> \~japanese 各時間レベル(N, N+1)の速度場データを管理する構造型 Field_IcGrid2D の変数.
  !! \~english The Field_IcGrid2D array object managing the field data of velocity at each time level(N, N+1).
  type(Field_IcGrid2D), public, save :: xy_Vel_TL_list(TL_NUMS)
  
  !> \~japanese 各時間レベル(N, N+1)の表面高度場データを管理する構造型 Field_IcGrid2D の配列変数.
  !! \~english The array variable of derived type Field_IcGrid2D to manage the surface height field data of height at each time level(N, N+1).
  type(Field_IcGrid2D), public, save :: xy_Height_TL_list(TL_NUMS)

  !> \~japanese 各時間レベル(N, N-1, N-2)の速度場データの時間微分を管理するオブジェクト.
  !! \~english The array variable of derived type Field_IcGrid2D to manage the time derivative of the velocity field data at each time level(N, N-1, N-2).
  type(Field_IcGrid2D), public, save :: DVelDt_TL_list(TL_DDT_NUMS)
 
  !> \~japanese 各時間レベル(N, N-1, N-2)の表面高度場データの時間微分を管理するオブジェクト.
  !! \~english The array variable of derived type Field_IcGrid2D to manage the time derivative of the surface height field data at each time level(N, N-1, N-2).
  type(Field_IcGrid2D), public, save :: DHeightDt_TL_list(TL_DDT_NUMS)
  
  !> \~japanese 正二十面体格子上に分布する物理場に対する微分演算を提供するの必要な情報を管理するするための構造体 Derivate_Field_IcGrid2D の変数.
  !! \~english The variable of derived type Derivate_Field_IcGrid2D to manage the information nesessary to provide some differential operation
  !! acting on the physical field on icosahedral grid.
  type(Derivate_Field_IcGrid2D), public :: diff_eval


  ! 公開手続き
  ! Public procedure
  !
  public :: init_field_manager, update_timeLevel

contains

!
!> \~japanese
!! \brief field_manager モジュールを初期化する.
!!
!! @param[in] icgrid 構造型 IcGrid2D_FVM の変数.
!!
!! \~english
!! \brief Initialize the module field_manager.
!!
!! @param[in] icgrid The variable of derived type IcGrid2D_FVM.
!! 
subroutine init_field_manager( &
  & icgrid &
  & )

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid2D_FVM), intent(in) :: icgrid
 
  ! 作業変数
  ! Work variables
  !
  integer :: tlID
  
  ! 実行文 ; Executable statements
  !

!  write(*,*) 'init field_manager'

  ! 海底地形の高度場を保持する Field_IcGrid2D クラスのインスタンスの初期化.
  call Field_IcGrid2D_Init(self=xy_Htopo, icgrid=icgrid, name='h_s', rank=1)

  ! 惑星渦度場を保持する Field_IcGrid2D クラスのインスタンスの初期化.
  call Field_IcGrid2D_Init(self=xy_Coli, icgrid=icgrid, name='planetary_vorticity', rank=1)
  call create_planetaryVorticity_field(xy_Coli, Omega, alpha)

  ! タイムレベル n+1,n に対する速度・高度場のデータを保持する Field_IcGrid2D クラスのインスタンスの初期化.
  do tlID=1, TL_NUMS

    call Field_IcGrid2D_Init( &
      & self=xy_Vel_TL_list(tlID), icgrid=icgrid, &
      & name='v', rank=3, long_name='velocity', units='m/s' )

    call Field_IcGrid2D_Init( &
      & self=xy_Height_TL_list(tlID), icgrid=icgrid, &
      & name='h', rank=1, long_name='surface_height', units='m' )

  end do

  ! タイムレベル n, n-1, n-2 の速度・高度場を局所時間微分した場のデータを保持する, 
  ! Field_IcGrid2D クラスのインスタンスの初期化する.
  do tlID=1, TL_DDT_NUMS
    call Field_IcGrid2D_Init( &
      & self=DVelDt_TL_list(tlID), icgrid=icgrid, &
      & name='dvdt', rank=3 )

    call Field_IcGrid2D_Init( &
      & self=DHeightDt_TL_list(tlID), icgrid=icgrid, &
      & name='dhdt', rank=1 )

  end do

  ! 微分演算を提供するクラスのインスタンスを初期化する.
  call Derivate_Field_IcGrid2D_Init(diff_eval, icgrid)

end subroutine init_field_manager

! 
!> \~japanese
!! \brief 時間レベルを管理する変数を更新する. 時間積分を行う do ループ内で, 呼び出されなければならない.
!!
!! 具体的には, 予報されたタイムレベル n+1 の物理場に対して, 袖領域の物理場データの同期を行う.
!! そして, 各タイムレベルを管理する変数に, 各タイムレベルの物理場を保持する構造型 Field_IcGrid2D の変数の配列インデックスを
!! それぞれ設定する.
!!
!! \~english
!! \brief Updates the variables which manage the time level. This subroutine must be called in the loop for the temporal integration.
!!
!! Specifically, for the predicted physical fields at the time level n+1,
!! their data in the shadow zones are syncronized.
!! And the variable managing each time level is set to the array index of the Field_IcGrid2D object
!! which stores the physical field data at each time level.
!!
!
! 例. 
! Ex.
!              |  v_n      :  v_n+1
!---------------------------------------------
! current step |  TL_N=1   :  TL_N=2
! next step    |  TL_N=2   :  TL_N=1
!
!
subroutine update_timelevel()

  ! 作業変数 
  ! Work variables
  !
  integer :: tlID

  ! 実行文 ; Executable statement
  !
  
  ! タイムレベル n+ 1 の高度・速度場
  call paste_field2D_margin(xy_Vel_TL_list(TL_Nplus1))
  call paste_field2D_margin(xy_Height_TL_list(TL_Nplus1))

  !
  TL_Nplus1 = get_next_TLid(TL_Nplus1, TL_NUMS)
  TL_N = get_next_TLid(TL_N, TL_NUMS)

  TL_DDT_N = get_next_TLid( TL_DDT_N, TL_DDT_NUMS)
  TL_DDT_Nminus1 = get_next_TLid( TL_DDT_Nminus1, TL_DDT_NUMS)
  TL_DDT_Nminus2 = get_next_TLid( TL_DDT_Nminus2, TL_DDT_NUMS)
  
  !write(*,*) 'dt_n', TL_DDT_N
  !write(*,*) 'dt_n-1', TL_DDT_Nminus1
  !write(*,*) 'dt_n-2', TL_DDT_Nminus2
end subroutine update_timelevel

!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! 
! 非公開手続き
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

!
!> update_timelevel においてタイムレベルが更新される際に,
!! 新しいタイムレベルでの各タイムレベルの配列インデックスの値を計算し, 取得する. 
!!
!! @param[in] target_TL     対象となるタイムレベル. 
!! @param[in] TimeLevelNums タイムレベルの総数. 
!! @return 新しいタイムレベルにおける target_TL の ID. 
!!
function get_next_TLid( &
  & target_TL, TimeLevelNums  &
  & ) result( NextTLid )
  
  ! 宣言文 ; Declaration statement
  !
  integer, intent(in) :: target_TL
  integer, intent(in) :: TimeLevelNums
  integer nextTLid
  
  ! 実行文 ; Executable statement
  !
  
  nextTLid = target_TL - 1

  if ( nextTLid == 0 ) then
    nextTLid = TimeLevelNums
  end if
end function get_next_TLid

end module field_manager
