!> アプリ設定の型定義と、設定由来の粒子数計算をまとめるモジュール。
module bem_app_config_types
  use bem_kinds, only: dp, i32, i64
  use bem_types, only: sim_config
  implicit none

  !> 設定配列の初期確保サイズ（上限ではない）。
  integer, parameter :: max_templates = 8
  integer, parameter :: max_particle_species = 8

  !> 1粒子種の注入設定を表す。
  type :: particle_species_spec
    logical :: enabled = .false.
    integer(i32) :: npcls_per_step = 0_i32
    logical :: has_npcls_per_step = .false.
    character(len=16) :: source_mode = 'volume_seed'
    real(dp) :: number_density_cm3 = 0.0d0
    real(dp) :: number_density_m3 = 0.0d0
    logical :: has_number_density_cm3 = .false.
    logical :: has_number_density_m3 = .false.
    real(dp) :: q_particle = -1.602176634d-19
    real(dp) :: m_particle = 9.10938356d-31
    real(dp) :: w_particle = 1.0d0
    logical :: has_w_particle = .false.
    integer(i32) :: target_macro_particles_per_batch = 0_i32
    logical :: has_target_macro_particles_per_batch = .false.
    real(dp) :: pos_low(3) = [-0.4d0, -0.4d0, 0.2d0]
    real(dp) :: pos_high(3) = [0.4d0, 0.4d0, 0.5d0]
    character(len=16) :: velocity_distribution = 'maxwellian'
    character(len=256) :: velocity_grid_path = ''
    character(len=16) :: velocity_grid_pdf_kind = 'phase_space'
    character(len=16) :: velocity_grid_sampling = 'auto'
    real(dp) :: particle_flux_m2_s = 0.0d0
    logical :: has_particle_flux_m2_s = .false.
    real(dp) :: current_density_a_m2 = 0.0d0
    logical :: has_current_density_a_m2 = .false.
    real(dp) :: drift_velocity(3) = [0.0d0, 0.0d0, -8.0d5]
    real(dp) :: temperature_k = 2.0d4
    real(dp) :: temperature_ev = -1.0d0
    logical :: has_temperature_k = .false.
    logical :: has_temperature_ev = .false.
    real(dp) :: emit_current_density_a_m2 = 0.0d0
    integer(i32) :: rays_per_batch = 0_i32
    logical :: deposit_opposite_charge_on_emit = .false.
    logical :: has_deposit_opposite_charge_on_emit = .false.
    character(len=32) :: photo_escape_model = 'none'
    logical :: has_photo_escape_model = .false.
    real(dp) :: normal_drift_speed = 0.0d0
    real(dp) :: ray_direction(3) = [0.0d0, 0.0d0, 0.0d0]
    logical :: has_ray_direction = .false.
    character(len=16) :: inject_face = ''
  end type particle_species_spec

  !> 1つのテンプレート形状の有効化フラグと幾何パラメータを保持する。
  type :: template_spec
    logical :: enabled = .false.
    character(len=16) :: kind = 'plane'
    character(len=16) :: surface_model = 'insulator'
    real(dp) :: epsilon_r = 1.0d0
    real(dp) :: center(3) = 0.0d0
    real(dp) :: size_x = 1.0d0
    real(dp) :: size_y = 1.0d0
    real(dp) :: size(3) = [1.0d0, 1.0d0, 1.0d0]
    integer(i32) :: nx = 1
    integer(i32) :: ny = 1
    integer(i32) :: nz = 1
    real(dp) :: radius = 0.5d0
    real(dp) :: inner_radius = 0.25d0
    real(dp) :: height = 1.0d0
    integer(i32) :: n_theta = 24
    integer(i32) :: n_r = 4
    integer(i32) :: n_z = 1
    logical :: cap = .true.
    logical :: cap_top = .true.
    logical :: cap_bottom = .true.
    logical :: has_cap_top = .false.
    logical :: has_cap_bottom = .false.
    integer(i32) :: n_lon = 24
    integer(i32) :: n_lat = 12
  end type template_spec

  !> 実行条件・メッシュ入力・粒子注入・出力設定を一元管理する。
  type :: app_config
    character(len=16) :: mesh_mode = 'auto'
    character(len=256) :: obj_path = 'examples/simple_plate.obj'
    character(len=16) :: mesh_surface_model = 'insulator'
    real(dp) :: mesh_epsilon_r = 1.0d0
    real(dp) :: obj_scale = 1.0d0
    real(dp) :: obj_rotation(3) = [0.0d0, 0.0d0, 0.0d0]
    real(dp) :: obj_offset(3) = [0.0d0, 0.0d0, 0.0d0]
    integer(i32) :: n_templates = 0_i32
    type(template_spec), allocatable :: templates(:)

    integer(i32) :: n_particles = 0_i32
    integer(i32) :: n_particle_species = 0_i32
    type(particle_species_spec), allocatable :: particle_species(:)

    logical :: write_output = .true.
    logical :: write_mesh_potential = .false.
    logical :: write_potential_history = .false.
    character(len=256) :: output_dir = 'outputs/latest'
    character(len=256) :: output_restart_from = ''
    integer(i32) :: history_stride = 1
    logical :: resume_output = .false.

    type(sim_config) :: sim
  end type app_config

contains

  !> 有効な粒子種の `npcls_per_step` を合計し、1バッチあたりの粒子数を返す。
  !! 1つ以上の粒子種が有効で、かつ合計が正でない場合は停止する。
  !! @param[in] cfg 粒子種設定を含むアプリ設定。
  !! @return batch_n 1バッチあたりの総粒子数。
  integer(i32) function particles_per_batch_from_config(cfg) result(batch_n)
    type(app_config), intent(in) :: cfg
    integer(i32) :: s
    logical :: has_dynamic_source

    if (cfg%n_particle_species <= 0) then
      error stop 'At least one [[particles.species]] entry is required.'
    end if

    batch_n = 0_i32
    has_dynamic_source = .false.
    do s = 1, cfg%n_particle_species
      if (.not. cfg%particle_species(s)%enabled) cycle
      select case (trim(cfg%particle_species(s)%source_mode))
      case ('volume_seed')
        if (cfg%particle_species(s)%npcls_per_step < 0_i32) then
          error stop 'particles.species.npcls_per_step must be >= 0.'
        end if
        batch_n = batch_n + cfg%particle_species(s)%npcls_per_step
      case ('reservoir_face')
        has_dynamic_source = .true.
      case ('photo_raycast')
        has_dynamic_source = .true.
      case default
        error stop 'Unknown particles.species.source_mode.'
      end select
    end do

    if (batch_n <= 0_i32 .and. .not. has_dynamic_source) then
      error stop 'At least one enabled [[particles.species]] entry must have npcls_per_step > 0.'
    end if
  end function particles_per_batch_from_config

  !> バッチ数と1バッチ粒子数から総粒子数を返す。
  !! @param[in] cfg バッチ数と粒子種設定を含むアプリ設定。
  !! @return total_n 総粒子数。
  integer(i32) function total_particles_from_config(cfg) result(total_n)
    type(app_config), intent(in) :: cfg
    integer(i64) :: total_n_i64

    if (cfg%sim%batch_count <= 0_i32) then
      error stop 'sim.batch_count must be > 0.'
    end if
    total_n_i64 = int(cfg%sim%batch_count, i64)*int(particles_per_batch_from_config(cfg), i64)
    if (total_n_i64 > int(huge(0_i32), i64)) then
      error stop 'total particle count overflows 32-bit integer. Reduce batch_count or npcls_per_step.'
    end if
    total_n = int(total_n_i64, i32)
  end function total_particles_from_config

  !> `app_config` を既定値で初期化し、TOML 上書き前の状態を作る。
  !! @param[out] cfg 既定値で初期化したアプリ設定。
  subroutine default_app_config(cfg)
    type(app_config), intent(out) :: cfg

    cfg%mesh_mode = 'auto'
    cfg%obj_path = 'examples/simple_plate.obj'
    cfg%mesh_surface_model = 'insulator'
    cfg%mesh_epsilon_r = 1.0d0
    cfg%obj_scale = 1.0d0
    cfg%obj_rotation = [0.0d0, 0.0d0, 0.0d0]
    cfg%obj_offset = [0.0d0, 0.0d0, 0.0d0]
    cfg%n_templates = 0_i32
    cfg%n_particles = 0_i32
    cfg%n_particle_species = 0_i32
    cfg%write_output = .true.
    cfg%write_mesh_potential = .false.
    cfg%write_potential_history = .false.
    cfg%output_dir = 'outputs/latest'
    cfg%output_restart_from = ''
    cfg%history_stride = 1_i32
    cfg%resume_output = .false.
    cfg%sim = sim_config()
    allocate (cfg%templates(max_templates))
    allocate (cfg%particle_species(max_particle_species))
    cfg%particle_species = particle_species_spec()
    cfg%sim%dt = 1.0d-9
    cfg%sim%rng_seed = 12345_i32
    cfg%sim%batch_count = 1_i32
    cfg%sim%max_step = 400
    cfg%sim%tol_rel = 1.0d-8
    cfg%sim%softening = 1.0d-6
    cfg%sim%field_solver = 'auto'
    cfg%sim%field_normalization = 'si'
    cfg%sim%field_length_scale = 1.0d0
    cfg%sim%field_bc_mode = 'free'
    cfg%sim%field_periodic_image_layers = 1_i32
    cfg%sim%field_periodic_far_correction = 'none'
    cfg%sim%field_periodic_ewald_alpha = 0.0d0
    cfg%sim%field_periodic_ewald_layers = 4_i32
    cfg%sim%tree_theta = 0.5d0
    cfg%sim%tree_leaf_max = 16_i32
    cfg%sim%tree_min_nelem = 256_i32
    cfg%sim%e0 = [0.0d0, 0.0d0, 0.0d0]
    cfg%sim%has_e0_vector = .false.
    cfg%sim%e0_abs = 0.0d0
    cfg%sim%has_e0_abs = .false.
    cfg%sim%e0_phi_xy_deg = 0.0d0
    cfg%sim%has_e0_phi_xy_deg = .false.
    cfg%sim%e0_phi_z_deg = 0.0d0
    cfg%sim%has_e0_phi_z_deg = .false.
    cfg%sim%b0 = [0.0d0, 0.0d0, 0.0d0]
    cfg%sim%reservoir_potential_model = 'none'
    cfg%sim%phi_infty = 0.0d0
    cfg%sim%open_boundary_model = 'escape'
    cfg%sim%injection_face_phi_grid_n = 3_i32
    cfg%sim%raycast_max_bounce = 16_i32
    cfg%sim%sheath_injection_model = 'none'
    cfg%sim%sheath_alpha_deg = 60.0d0
    cfg%sim%sheath_photoelectron_ref_density_cm3 = 64.0d0
    cfg%sim%sheath_reference_coordinate = 0.0d0
    cfg%sim%has_sheath_reference_coordinate = .false.
    cfg%sim%sheath_electron_drift_mode = 'normal'
    cfg%sim%sheath_ion_drift_mode = 'normal'
    cfg%n_templates = 1_i32
    cfg%n_particles = 0_i32

    cfg%templates(1)%enabled = .true.
    cfg%templates(1)%kind = 'plane'
    cfg%templates(1)%size_x = 1.0d0
    cfg%templates(1)%size_y = 1.0d0
    cfg%templates(1)%nx = 1
    cfg%templates(1)%ny = 1
    cfg%templates(1)%center = [0.0d0, 0.0d0, 0.0d0]
  end subroutine default_app_config

  !> `[[particles.species]]` の既定値を返す。
  !! 現行仕様では、列挙された粒子種は既定で有効とみなす。
  !! @return spec 粒子種の既定設定。
  pure function species_from_defaults() result(spec)
    type(particle_species_spec) :: spec

    spec = particle_species_spec()
    spec%enabled = .true.
    spec%npcls_per_step = 0_i32
  end function species_from_defaults

end module bem_app_config_types
