! See copyright notice in the COPYRIGHT file.
! ****************************************************************************** !
!> Module to describe additional elmental properties, like boundary conditions.
!!
!! Every element in the octree structure can be
!! assigned several properties.
!! The description of the properties is kept abstract, such that very different
!! properties can be assigned to elements in a similar manner.
!! As 64 bits are available to describe additional properties, we could attach
!! 64 different kinds of data to each element and each one combinable with all
!! other properties.
!! The most important data that has to be attached to elements in nearly every
!! mesh are the boundary conditions in [[tem_bc_module]], others are for
!! example vertex displacements in [[tem_vrtx_module]]
!! and regional influences.
!! As the boundary conditions are such a basic part of the general mesh
!! description, we present their property implementation exemplary in more
!! detail.
!! Each property is indicated by a bit in the property bit-mask.
!! Which bit is attached to what kind of property is defined in the header file.
!! The property might then be linked to further information stored in additional
!! files.
!! See [[tem_bc_module]] for details of the boundary implementation.
!!
!!
!! The amount and kind of additional data that might be attached to the elements
!! by the properties is arbitrary and could involve several more
!! indirections.
!! For example an additional non-elemental file is typically required to
!! describe vertex displacements of elements.
!! Thus for elements, where the vertices should be moved, there is an elemental
!! list of integers indicating which vertices to use for the eight corners of
!! the element.
!! The actual coordinates are stored in a separate file with the coordinates for
!! each vertex.
!!
module tem_property_module

  ! include treelm modules
  use mpi
  use env_module,          only: long_k
  use tem_prophead_module, only: tem_prophead_type

  implicit none

  private

  public :: tem_property_type
  public :: init_propertylist
  public :: destroy_propertylist
  public :: gather_property


  !> An auxilary data type to describe modifications and additional features
  !! for some elements.
  type tem_property_type
    !> Number of local elements with this property
    integer :: nElems

    !> Offset of the local chunk of elements with this property in the list of
    !! all elements with that properties on disk
    integer(kind=long_k) :: Offset

    !> The indices of elements in the local partition, that have this
    !! property.
    integer, allocatable :: ElemID(:)
  end type

  !! Properties forseen at the moment:
  !!
  !! * Boundary Conditions (direct, second, third neighbors)
  !! * Deformed Elements
  !! * Changed Element type (tetrahedrons, prism, pyramid)
  !!
  !> Element properties
  !HK: the proposition is to just have a single property
  !HK: indicating boundary conditions on any of the sides of any given element
  !HK: there is no undefined status!
  !HK: each element might have up to 64 properties attached to it
  !HK: the bitpos indicates just the position of the bit in the field
  !HK: indicating, which should flag the corresponding property
  !HK: A 0 indicates the absence of the property and a 1 the presence
  !HK: Thus an element with all bits set to 0 has no additional properties
  !! Must be smaller than 64, because the respective bits will be set

  !> fluid
  integer,parameter,public :: prp_fluid    = 1
  !> solid
  integer,parameter,public :: prp_solid    = 2
  !> The element has boundaries. closer defined in the property module
  integer,parameter,public :: prp_hasBnd   = 3
  !> deformed Elements
  integer,parameter,public :: prp_defElems = 4
  !> changed Elements
  !> This is now used for adaptive refinement
  integer,parameter,public :: prp_chgElems = 5
  !> Fluidifiable Elements
  integer,parameter,public :: prp_fluidify = 6
  !> vertex information
  integer,parameter,public :: prp_vrtx     = 7
  !> has qVal elements
  integer,parameter,public :: prp_hasQVal  = 8
  !> the element is a parent element of one or more surface data points
  integer,parameter,public :: prp_hasIBM   = 9
  !> element has a remote neighbor
  integer,parameter,public :: prp_hasRemoteNgh = 10
  !> is this a fluid element which needs to be send to an attached proc
  integer,parameter,public :: prp_sendHalo = 12
  !> Solidification is not allowed for this element.
  integer,parameter,public :: prp_noSolidification = 13
  !> the complete neighborhood is required for this element
  integer,parameter,public :: prp_requireFullNeighborhood = 20

  !> Indicate if the element has some coloring attached to it.
  integer, parameter, public :: prp_isColored = 30

  !> This bit indicates wether the color in the element is further resolved by
  !! polynomial information.
  integer, parameter, public :: prp_hasPolynomial = 31


contains


  ! **************************************************************************** !
  !> Defines how many properties there are.
  !!
  !! Allocate the property and header lists accordingly.
  !!
  subroutine init_propertylist(header, property, nproperties)
    ! ---------------------------------------------------------------------------
    !>
    type(tem_prophead_type),  pointer :: header(:)
    !>
    type(tem_property_type),  pointer :: property(:)
    !>
    integer, intent(in) :: nproperties
    ! ---------------------------------------------------------------------------

    allocate(property(nProperties), header(nProperties))

  end subroutine init_propertylist
  ! **************************************************************************** !

  ! **************************************************************************** !
  !! deallocate the property and header lists accordingly.
  !!
  subroutine destroy_propertylist(header, property)
    ! ---------------------------------------------------------------------------
    !>
    type(tem_prophead_type),  pointer :: header(:)
    !>
    type(tem_property_type),  pointer :: property(:)

    deallocate(property)
    deallocate(header)
  end subroutine destroy_propertylist
  ! **************************************************************************** !

! ****************************************************************************** !
  !> Gather the informations on a property from the bit fields of all elements
  !!
  subroutine gather_property(Property, Header, BitField, comm)
    ! ---------------------------------------------------------------------------
    !> Property to gather
    type(tem_property_type), intent(out) :: Property
    !> Header for this property
    type(tem_prophead_type), intent(in) :: Header
    !> The BitField for the properties of all local elements
    integer(kind=long_k), intent(in) :: BitField(:)
    !> Communicator to act on
    integer, intent(in) :: comm
    ! ---------------------------------------------------------------------------
    integer :: nElems
    integer :: iElem, PropElem
    integer(kind=long_k) :: myElems
    integer :: iError
    ! ---------------------------------------------------------------------------

    nElems = size(BitField)

    ! First count the number of local elements with the given property.
    Property%nElems = count(btest(BitField, Header%BitPos))
    myElems = Property%nElems

    Property%Offset = 0
    ! Calculate offset on each process, by summing the number of elements
    ! on all lower ranks.
    call MPI_Exscan( myElems, Property%Offset, 1, MPI_INTEGER8, MPI_SUM,       &
      &              comm, iError)

    ! Allocate an array to store the link from the list of elements with this
    ! property to the list of all elements.
    ! (Property%ElemID -> tree%treeID)
    allocate(Property%ElemID(Property%nElems))

    PropElem = 0
    do iElem=1,nElems
      ! Run over all elements.
      if (btest(BitField(iElem), Header%BitPos)) then
        ! If the element has the property, increase the counter for elements
        ! with this property and store this position for later lookups.
        PropElem = PropElem + 1
        Property%ElemID(PropElem) = iElem
      end if
    end do

  end subroutine gather_property
! ****************************************************************************** !


end module tem_property_module
! ****************************************************************************** !
