!-*- mode: F90 -*-!
!------------------------------------------------------------!
! Copyright (C) 2026 Wannier Developer Group                 !
!                                                            !
! This library is free software; you can redistribute it     !
! and/or modify it under the terms of the GNU Lesser General !
! Public License as published by the Free Software           !
! Foundation; either version 2.1 of the License, or (at your !
! option) any later version.                                 !
!                                                            !
! This library 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 Lesser General Public License for    !
! more details.                                              !
!                                                            !
! You should have received a copy of the GNU Lesser General  !
! Public License along with this library; if not, see        !
! <https://www.gnu.org/licenses/>.                           !
!                                                            !
! The webpage of the Wannier90 code is                       !
! <https://www.wannier.org>.                                 !
!                                                            !
! The Wannier90 code is hosted on GitHub                     !
! <https://github.com/wannier-developers/wannier90>          !
!------------------------------------------------------------!
!                                                            !
!  w90_types: derived types encapsulating data required by   !
!      both wannier90.x and postw90.x                        !
!                                                            !
!------------------------------------------------------------!

module w90_types

  !! Definition of types encapsulating various quantities, data and parameters.
  !! Variables are grouped according to physical meaning and their use in the Wannier90 project.
  !!
  !! Here are defined types used by both wannier90.x and postw90.x.
  !! Types specific to wannier90.x (not used by postw90.x) are defined in wannier90_types.F90.
  !! Types specific to postw90.x (not used by wannier90.x) are defined in postw90/postw90_types.F90.

  use w90_constants, only: dp, maxlen

  implicit none

  public

  type print_output_type
    !!==================================================
    !! Contains variables to control output file formatting and verbosity.
    !!==================================================
    ! verbosity flags - w90_readwrite_read_verbosity
    integer :: iprint = 1
    ! Controls the verbosity of the output
    integer :: timing_level = 1
    ! REVIEW_2021-07-22: we agree that we don't need both length_unit and lenconfac;
    ! REVIEW_2021-07-22: instead could have a utility function.
    character(len=20) :: length_unit = 'ang' ! MAYBE, just have a separate variable?
    ! Units for length
    real(kind=dp) :: lenconfac = 1.0_dp !lots of write statements in wannier90
  end type print_output_type

  type w90_system_type
    !!==================================================
    !! Contains physical information about the material being calculated.
    !!==================================================
    integer :: num_valence_bands ! **no sensible default**
    integer :: num_elec_per_state = 2 ! used in: wannierise and postw90 dos and boltzwann
    logical :: spinors = .false.  !are our WF spinors? !kmesh, plot, wannier_lib, postw90/gyrotropic
  end type w90_system_type

  ! timer from io.F90
  type timing_data_type                                          !! Data about each stopwatch - for timing routines
    integer :: ncalls                                            !! Number of times stopwatch has been called
    real(kind=DP) :: ctime                                       !! Total time on stopwatch
    real(kind=DP) :: ptime                                       !! Temporary record of time when watch is started
    character(len=60) :: label                                   !! What is this stopwatch timing
  end type timing_data_type

  integer, parameter :: nmax = 100                               !! Maximum number of stopwatches
  type timer_list_type
    type(timing_data_type) :: clocks(nmax)                         !! Data for the stopwatches
    integer :: nnames = 0                                          !! Number of active stopwatches
    logical :: overflow = .false.
  end type timer_list_type

  type ws_region_type
    logical :: use_ws_distance = .true. !ws_distance, plot and postw90_common
    real(kind=dp) :: ws_distance_tol = 1.e-5_dp !ws_distance, hamiltonian and postw90_common
    !! absolute tolerance for the distance to equivalent positions
    integer :: ws_search_size(3) = 2 ! ws_distance, hamiltonian
    !! maximum extension in each direction of the supercell of the BvK cell
    !! to search for points inside the Wigner-Seitz cell
  end type ws_region_type

  ! used by ws_distance
  type ws_distance_type
    integer, allocatable :: irdist(:, :, :, :, :)!(3,ndegenx,num_wann,num_wann,nrpts)
    !! The integer number of unit cells to shift Wannier function j to put its centre
    !! inside the Wigner-Seitz of wannier function i. If several shifts are
    !! equivalent (i.e. they take the function on the edge of the WS) they are
    !! all listed. First index: xyz, second index: number of degenerate shifts,
    !! third and fourth indices: i,j; fifth index: index on the R vector.
    real(DP), allocatable :: crdist(:, :, :, :, :)!(3,ndegenx,num_wann,num_wann,nrpts)
    !! Cartesian version of irdist_ws, in angstrom
    integer, allocatable :: ndeg(:, :, :)!(num_wann,num_wann,nrpts)
    !! The number of equivalent vectors for each set of (i,j,R) (that is, loops on
    !! the second index of irdist_ws(:,:,i,j,R) go from 1 to wdist_ndeg(i,j,R))
    !
    logical :: done = .false.
    !! Global variable to know if the properties were already calculated, and avoid
    !! recalculating them when the [[ws_translate_dist]] function is called multiple times
  end type ws_distance_type

  ! setup in wannierise, but used by plot, ws_distance etc
  type wannier_data_type
    !!==================================================
    !! Contains the centres and spreads of the MLWFs
    !!==================================================
    ! Wannier centres and spreads
    real(kind=dp), allocatable :: centres(:, :)
    real(kind=dp), allocatable :: spreads(:)
    ! REVIEW_2021-07-22: Do we want to expose other related variables such as the decomposition
    ! REVIEW_2021-07-22: of the spread, matrix elements of r and r^2, etc. (TO FINISH)
  end type wannier_data_type

  ! The maximum number of shells we need to satisfy B1 condition in kmesh
  integer, parameter :: max_shells = 6
  integer, parameter :: num_nnmax = 12

  type kmesh_input_type
    !!==================================================
    !! Contains information that can be provided by the user about determining the kmesh
    !!==================================================
    integer :: num_shells = 0
    !! no longer an input keyword
    !! higher-order finite difference
    integer :: max_shells_h ! \sum ((3, 2i)) (combination with rep.) = n(4n*2 + 15n + 17)/6
    integer :: max_shells_aux ! = 6 for higher-order simple algorithm, n(4n*2 + 15n + 17)/6 for search algorithm
    integer :: num_nnmax_h ! 2*n(4n*2 + 15n + 17)/6
    integer :: higher_order_n = 1
    logical :: higher_order_nearest_shells = .false. ! experimental feature
    !! A simpler algorithm of determining bvectors
    logical :: kmesh_shell_from_file = .false.
    !! allow specifying b-vector shells from file (for complex cases where the automatic procedure fails)
    logical :: skip_B1_tests = .false.
    !! do not check the B1 condition
    integer, allocatable :: shell_list(:)
    integer :: search_shells = 36
    integer :: search_supcell_size = 5 !Size of supercell (of recip cell) in which to search for k-point shells
    real(kind=dp) :: tol = 0.000001_dp
  end type kmesh_input_type

  !AAM: There are a number of ways one can handle the initial guess. (i) specify explicit
  !AAM: projections; (ii) use random (s-orbital) projections; (iii) a combination of (i) and
  !AAM: (ii); (iv) use the phases from the Bloch functions directly; (v) SCDM method. (i), (ii)
  !AAM: and (iii) require the arrays defined in the "projection_type" below. (iv) and (v) do not.
  !AAM: (vi) An external code may also simply supply to w90 an Amn(k) matrix that is has independently
  !AAM: generated, in which case projection_type is not needed.
  !AAM: It makes sense to keep the projection sites separate from the projection_type data below.
  type proj_type
    !!==================================================
    !! Contains information that can be provided by the user about the projections
    !!==================================================
    ! REVIEW_2021-07-22: site(:,:) has dual usage: for projections and for guiding centres.
    ! REVIEW_2021-07-22: Make a new type for guiding centres that only contains sites.
    ! REVIEW_2021-07-22: In the future this can be logically distinct from the projection sites.
    ! REVIEW_2021-07-22: For now, when defining proj_input_type, also define sites inside the
    ! REVIEW_2021-07-22: new guiding centres type.

    ! regarding defaults: specific flow in readwrite means all values are assigned anyway
    ! using defaults here requires restructuring get_projections() (readwrite.F90)
    ! site, l, m don't have reasonable defaults
    integer :: l, m, s
    integer :: radial = 1
    real(kind=dp) :: site(3)
    real(kind=dp) :: s_qaxis(3) = (/0.0_dp, 0.0_dp, 1.0_dp/)
    real(kind=dp) :: z(3) = (/0.0_dp, 0.0_dp, 1.0_dp/)
    real(kind=dp) :: x(3) = (/1.0_dp, 0.0_dp, 0.0_dp/)
    real(kind=dp) :: zona = 1.0_dp
  end type proj_type

  ! kmesh information (set in kmesh)
  type kmesh_info_type
    !!==================================================
    !! Contains derived information about the kmesh
    !!==================================================
    integer              :: nnh               ! the number of b-directions (bka)
    integer              :: nntot             ! total number of neighbours for each k-point
    integer, allocatable :: nnlist(:, :)      ! list of neighbours for each k-point
    integer, allocatable :: neigh(:, :)
    integer, allocatable :: nncell(:, :, :)   ! gives BZ of each neighbour of each k-point
    ! JJ the following two arrays allow the Stengel-Spaldin method to be used without sorting b-vectors globally
    ! the order of b-vectors is explicit in ".mmn" files, but unfortunately not in ".uHu" files
    ! these arrays could replace sorting bk(:,:, ik), but this is not exploited now
    ! these are alloc'd and assigned only in the use_ss_functional=t case
    integer, allocatable :: nnord(:, :)       ! ordering of b-vectors: bk(:,nnord(nn,ik),ik) = bk(:,nn,1)
    integer, allocatable :: nninv(:, :)       ! ordering of b-vectors: bk(:,nninv(nn,ik),1)  = bk(:,nn,ik)
    integer, allocatable :: nnrev(:, :)       ! ordering of b-vectors: bk(:,nnrev(nn,ik),ik) = - bk(:,nn,1) (opposite vec)
    real(kind=dp)              :: wbtot
    real(kind=dp), allocatable :: wb(:)       ! weights associated with neighbours of each k-point
    real(kind=dp), allocatable :: bk(:, :, :) ! the b-vectors that go from each k-point to its neighbours
    real(kind=dp), allocatable :: bka(:, :)   ! the b-directions from 1st k-point to its neighbours
    logical :: explicit_nnkpts
    !! nnkpts block is in the input file (allowed only for post-proc setup)
  end type kmesh_info_type

  ! this contains data which described the disentangled manifold, also used in postw90
  type dis_manifold_type
    !!==================================================
    !! Contains information about the manifold of states from which the MLWFs are to be disentangled.
    !!==================================================
    real(kind=dp) :: win_min = -huge(0.0_dp)
    !! lower bound of the disentanglement outer window
    real(kind=dp) :: win_max = huge(0.0_dp)
    !! upper bound of the disentanglement outer window
    real(kind=dp) :: froz_min = -huge(0.0_dp)
    !! lower bound of the disentanglement inner (frozen) window
    real(kind=dp) :: froz_max = huge(0.0_dp)
    !! upper bound of the disentanglement inner (frozen) window
    logical :: frozen_states = .false.
    !! whether to use energy frozen window, i.e., froz_min/froz_max
    real(kind=dp) :: proj_min = -1.0_dp
    !! lower threshold of the projectability disentanglement, below are discarded
    !! (negative = unset: set explicitly or determined automatically via proj_auto)
    real(kind=dp) :: proj_max = -1.0_dp
    !! upper threshold of the projectability disentanglement, equal and above are frozen
    !! (negative = unset: set explicitly or determined automatically via proj_auto)
    logical :: frozen_proj
    !! whether to use projectability frozen window, i.e., proj_min/proj_max
    logical :: proj_auto = .true.
    !! whether to determine proj_min/proj_max automatically via multi-Otsu thresholding
    !! (ignored when proj_min/proj_max are set explicitly)
    integer :: proj_auto_num_classes = 5
    !! number of classes for the automatic multi-Otsu projectability thresholds
    ! disentangle parameters
    ! Used by plot, hamiltonian, wannierise, postw90_common, get_oper - not read
    integer, allocatable :: ndimwin(:)
    !! number of bands in outer window at each k
    integer, allocatable :: nfirstwin(:)
    !! index of first band in outer window at each k
    logical, allocatable :: lwindow(:, :)
  end type dis_manifold_type

  ! Atom sites - often used in the write_* routines
  ! hamiltonian, wannierise, plot, transport, wannier_lib
  type atom_data_type
    !!==================================================
    !! Contains information about the atoms (and maybe the cell...) of the system being calculated.
    !!==================================================
    real(kind=dp), allocatable :: pos_cart(:, :, :)
    integer, allocatable :: species_num(:)
    character(len=maxlen), allocatable :: label(:)
    character(len=2), allocatable :: symbol(:)
    integer :: num_atoms = 0
    integer :: num_species = 0
  end type atom_data_type

  ! plot.F90 and postw90/kpath
  type kpoint_path_type
    !!==================================================
    !! Contains information that specifies the k-point path for plotting and other purposes.
    !! Note: The length of bands_label and the second index of bands_spec_points is twice the
    !! number of segments specified by the user. Each pair of special points defines a segment.
    !!==================================================
    integer :: num_points_first_segment = 100
    character(len=20), allocatable :: labels(:)
    real(kind=dp), allocatable :: points(:, :)
    logical :: bands_kpt_explicit = .false. ! use user provided list of kpoints for bands kpath
    real(kind=dp), allocatable ::bands_kpt_frac(:, :) ! explicit bands kpoints in fractional coordinate
  end type kpoint_path_type

  type settings_data
    !!==================================================
    !! structure to hold a scalar and array settings
    !!==================================================
    ! for simplicity, consider arrays of different rank
    ! as different types; otherwise reshape, etc.
    character(len=:), allocatable :: keyword ! token
    character(len=:), allocatable :: txtdata ! text data item
    character(len=:), allocatable :: c2d(:)
    ! integer data
    integer, allocatable :: i1d(:)
    integer, allocatable :: i2d(:, :)
    integer, allocatable :: idata
    ! logical data
    logical, allocatable :: l1d(:)
    logical, allocatable :: ldata
    ! fp data
    real(kind=dp), allocatable :: r1d(:)
    real(kind=dp), allocatable :: r2d(:, :)
    real(kind=dp), allocatable :: rdata
  end type settings_data

  type settings_type
    !!==================================================
    !! structure to hold input var/values
    !! contents of .win file and settings set by library interface
    !!==================================================
    integer :: num_entries = 0, num_entries_max = 0 ! number of keywords stored and max
    type(settings_data), allocatable :: entries(:)
    ! data for processing input file
    integer :: num_lines
    character(len=maxlen), allocatable :: in_data(:) ! contents of .win file
  end type settings_type

end module w90_types
