! NOT USED
!> basis geometrical entities which can be stored in parallel mode

module elemental_mod
  use paramets

  implicit none

   private
  !> elemental arrays - 1 dimensional
   type, public :: Elemental1_t
      real, allocatable, dimension (:) :: x  ! block of matrix

      contains
!      procedure :: addElemental1
!      procedure :: subElemental1
!      procedure :: assignElemental1
      procedure :: init => initFromNumber
      procedure :: initFromArray
      procedure :: length
      procedure :: printme
      procedure :: delete => deleteElemental1
      ! final :: deleteElemental1

!      generic, public :: operator(+) => addElemental1
!      generic, public :: operator(-) => subElemental1
!      generic, public :: assignment(=) => assignElemental1
!     procedure :: CopyMblocK

    end type Elemental1_t

!    interface Elemental1_t
!       procedure :: constructFromNumber
!       procedure :: constructFromArray
!    end interface

    interface assignment (=)
      procedure :: copyElemental1
      procedure :: copyArrayToElemental1
!!      procedure :: copyElemental1ToArray
    end interface

    public :: assignment(=)


     ! 3 dimensional arrays
    !> elemental arrays - 3 dimensional
   type, public :: Elemental2_t
      real, allocatable, dimension (:,:) :: x  ! block of matrix

      contains

      procedure :: init => initFromNumber2
      procedure :: initFrom2DArray
      !procedure :: initFrom1DArray

      procedure :: getSize => getSize2
      procedure :: printme => printme2
      !procedure :: copyTo1Darray => copyElemental2to1Darray ! watch for the ordering
      procedure :: delete => deleteElemental2
      !procedure :: add => addElemental3
    end type Elemental2_t

    interface assignment (=)
      procedure :: copyElemental2
      procedure :: copyArrayToElemental2
    end interface

    ! 3 dimensional arrays
    !> elemental arrays - 3 dimensional
   type, public :: Elemental3_t
      real, allocatable, dimension (:,:,:) :: x  ! block of matrix

      contains

      procedure :: init => initFromNumber3
      procedure :: initFrom3DArray
      procedure :: initFrom1DArray

      procedure :: getSize => getSize3
      procedure :: printme => printme3
      procedure :: copyTo1Darray => copyElemental3to1Darray ! watch for the ordering
      procedure :: copyTo1Darray3_1_2 => copyElemental3to1Darray3_1_2
      procedure :: delete => deleteElemental3
      !procedure :: add => addElemental3

!      generic, public :: operator(+) => addElemental1
!      generic, public :: operator(-) => subElemental1
!      generic, public :: assignment(=) => assignElemental1
!     procedure :: CopyMblocK
    end type Elemental3_t

!    interface Elemental3_t
!       procedure :: constructFromNumber3
!       procedure :: constructFromArray3
!       procedure :: constructElemental3From1DArray
!    end interface

!    public:: operator(+), operator(-), operator(*)

!    interface operator(+)
!      module procedure addElemental3
!    end interface
!
!    interface operator(-)
!      module procedure subElemental3
!    end interface

    interface assignment (=)
      procedure :: copyElemental3
      procedure :: copyArrayToElemental3
    end interface


   public :: copy3Darrayto1Darray ! 3 1 2 permutation (for STDG )
   public :: copy1DarrayTo3Darray ! 3 1 2 permutation (for STDG )
   public :: deleteArrayElemental1
   public :: deleteArrayElemental2
   public :: deleteArrayElemental3

contains

   subroutine printme(this)
      class( Elemental1_t ), intent(inout) :: this

      write(*,*) 'Elemental_t%x:', this%x

   end subroutine printme

   elemental function length( this)
      class( Elemental1_t ), intent(in) :: this
      real :: length
      length = size( this%x(:) )
   end function length


    !> n = length of the vector
    subroutine initFromNumber(this, n)
        class( Elemental1_t ) :: this
        integer, intent(in) :: n
        if (allocated(this%x)) &
            deallocate(this%x)

        allocate(this%x(1:n), source=0.0)
    end subroutine initFromNumber

    subroutine initFromArray(this, array)
        class( Elemental1_t ) :: this
        real, dimension(:), intent(in) :: array
        allocate(this%x(1:size(array)))
        this%x = array

    end subroutine

!    function constructCopy( other ) result(this)
!        type( Elemental1_t ), intent(in) :: other
!        type( Elemental1_t ) :: this
!
!        allocate( this%x(1: other%length()) )
!        this%x = other%x
!    end function
!

   subroutine copyElemental1(this,other)
      class(Elemental1_t),intent(inout) :: this
      class(Elemental1_t),intent(in) :: other

      if (allocated(this%x)) then
         deallocate(this%x)
      end if

      allocate( this%x(1: other%length() ) )
      this%x = other%x
    end subroutine

    subroutine copyArrayToElemental1(this,array)
      class(Elemental1_t),intent(inout) :: this
      real, dimension(:),intent(in) :: array

      call copyElemental1(this, Elemental1_t( array ))
    end subroutine


   subroutine deleteElemental1(this)
      class( Elemental1_t ) :: this
      if (allocated(this%x)) &
            deallocate(this%x)

    end subroutine deleteElemental1

!!!!!!!!!!!! 2 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   subroutine printme2(this)
      class( Elemental2_t ), intent(inout) :: this

      write(*,*) 'Elemental2_t%x:', this%x

   end subroutine printme2

   function getSize2( this) result( length )
      class( Elemental2_t ), intent(in) :: this
      real, dimension(1:2) :: length
      length(1) = size( this%x(:,1) )
      length(2) = size( this%x(1,:) )
   end function getSize2


   subroutine deleteElemental2(this)
        class( Elemental2_t ) :: this
        if (allocated(this%x)) then
            deallocate(this%x)
        end if
    end subroutine deleteElemental2



  !> n = length of the vector
    subroutine initFromNumber2(this,a,b)
        class( Elemental2_t ) :: this
        integer, intent(in) :: a
        integer, intent(in) :: b

        if (allocated(this%x)) then
         deallocate(this%x)
        end if

        allocate(this%x(1:a,1:b), source = 0.0 )

    end subroutine initFromNumber2

    subroutine initFrom2DArray(this, array)
        class( Elemental2_t ) :: this
        real, dimension(:,:), intent(in) :: array
        integer :: k,l

        if (allocated(this%x)) then
         deallocate(this%x)
        end if

        k = size(array, 1)
        l = size(array, 2)

        allocate( this%x(1:k,1:l), &
                   source = array(1:k,1:l) )

    end subroutine initFrom2DArray




   subroutine copyElemental2(this,other)
      type(Elemental2_t),intent(inout) :: this
      type(Elemental2_t),intent(in) :: other
      integer :: k,l

      if (allocated(this%x)) then
         deallocate(this%x)
         print*, 'it was allocated dasasa'
      end if

      if (.not. allocated(other%x)) then
         stop 'problem asdassas'
      end if


      k = size(other%x, 1)
      l = size(other%x, 2)

      !print*, 'k l m ', k,l,m, size(other%x)

      allocate( this%x(1:k,1:l), source = other%x(1:k,1:l) )
      !this%x(1:k,1:l,1:m)  = other%x(1:k,1:l)

    end subroutine copyElemental2

    subroutine copyArrayToElemental2(this,array)
      class(Elemental2_t),intent(inout) :: this
      real, dimension(:,:), intent(in) :: array
      integer :: k,l

      if (allocated(this%x)) then
         deallocate(this%x)
      end if
      k = size(array, 1)
      l = size(array, 2)

      allocate( this%x(1:k,1:l), source = array(1:k,1:l) )

    end subroutine copyArrayToElemental2

    subroutine deleteArrayElemental2(array)
      type(Elemental2_t), dimension(:), intent(inout) :: array
      integer :: i , l
      !if (allocated( array ) ) then
      l = size( array(:) )
      do i = 1,l
        call array(i)%delete()
      enddo

    end subroutine deleteArrayElemental2





!!!!!!!!!!!! 3 !!!!!!!!!!!!!!!!!!!
   subroutine printme3(this)
      class( Elemental3_t ), intent(inout) :: this

      write(*,*) 'Elemental3_t%x:', this%x

   end subroutine printme3

   function getSize3( this) result( length )
      class( Elemental3_t ), intent(in) :: this
      real, dimension(1:3) :: length
      length(1) = size( this%x(:,1,1) )
      length(2) = size( this%x(1,:,1) )
      length(3) = size( this%x(1,1,:) )
   end function getSize3


    subroutine deleteElemental3(this)
        class( Elemental3_t ) :: this
        if (allocated(this%x)) then
            deallocate(this%x)
        end if
    end subroutine deleteElemental3

    !> n = length of the vector
    subroutine initFromNumber3(this,a,b,c)
        class( Elemental3_t ) :: this
!        type( Elemental3_t ) :: try
        integer, intent(in) :: a
        integer, intent(in) :: b
        integer, intent(in) :: c


        if (allocated(this%x)) then
         deallocate(this%x)
        end if


        !allocate(try%x(1:2,1:2,1:2), source=0.0)

        allocate(this%x(1:a,1:b,1:c), source = 0.0 )

    end subroutine initFromNumber3

    subroutine initFrom3DArray(this, array)
        class( Elemental3_t ) :: this
        real, dimension(:,:,:), intent(in) :: array
        integer :: k,l,m

        if (allocated(this%x)) then
         deallocate(this%x)
        end if

        k = size(array(:,1,1))
        l = size(array(1,:,1))
        m = size(array(1,1,:))
        allocate( this%x(1:k,1:l,1:m), &
                   source = array(1:k,1:l,1:m) )

    end subroutine initFrom3DArray

    subroutine initFrom1DArray(this, array, n1, n2, n3)
        class( Elemental3_t ), intent(inout) :: this
        real, dimension(:), intent(in) :: array
        integer, intent(in) :: n1, n2, n3
        integer :: nn, i, k

        nn = 0

        if ( size(array) /= n1*n2*n3) &
          stop 'array size mismatch in constructElemental3From1DArray'

        if (allocated(this%x)) then
         deallocate(this%x)
        end if

        allocate( this%x(1:n1,1:n2,1:n3 ), source = 0.0 )

        do i = 1, n3 ! start with the 3rd index (TDOF)
          do k = 1, n2 ! 2st index ndim ,
            this%x( 1:n1 , k ,i ) = array(nn+1:nn+n1) !1nd index - dof
            nn = nn + n1
          end do !k
          !nn = nn + n1
        end do ! n

    end subroutine initFrom1DArray


   subroutine copyElemental3(this,other)
      type(Elemental3_t),intent(inout) :: this
      type(Elemental3_t),intent(in) :: other
      type(Elemental3_t) :: try
      integer :: k,l,m

      if (allocated(this%x)) then
         deallocate(this%x)
         print*, 'it was allocated das'
      end if

      if (.not. allocated(other%x)) then
         stop 'problem asdas'
      end if


      k = size(other%x(:,1,1))
      l = size(other%x(1,:,1))
      m = size(other%x(1,1,:))

      allocate( try%x(1:2,1:2,1:2) , source = 0.0  )
      deallocate( try%x )

      print*, 'k l m ', k,l,m, size(other%x)

      allocate( this%x(1:k,1:l,1:m), source = 0.0 )

      !print*, 'k l m ', k,l,m, size(other%x) , size(this%x)

      this%x(1:k,1:l,1:m)  = other%x(1:k,1:l,1:m)

    end subroutine copyElemental3

    subroutine copyArrayToElemental3(this,array)
      class(Elemental3_t),intent(inout) :: this
      real, dimension(:,:,:),intent(in) :: array
      integer :: k,l,m

      if (allocated(this%x)) then
         deallocate(this%x)
      end if
      k = size(array(:,1,1))
      l = size(array(1,:,1))
      m = size(array(1,1,:))

      allocate( this%x(1:k,1:l,1:m), source = array(1:k,1:l,1:m) )

    end subroutine copyArrayToElemental3

    ! copy Elemental3_t to 1D array , permutation sets the ordering
    ! standard permutation for STDGM (/ 3,1,2 /)
    ! watch for the ordering 1:Tdof, 1:ndim, 1:dof - 3,1,2
    function copyElemental3to1Darray3_1_2( this, nsize) result(array)
      class(Elemental3_t),intent(in) :: this
      integer, dimension(1:3), intent(in) :: nsize
      real, dimension(1:nsize(1)*nsize(2)*nsize(3)) :: array
      integer, dimension(1:3) :: length
      integer :: i, k
      integer :: nn

      length = this%getSize()

      if ( any(nsize > length) ) then
!      if ( size( array(:) ) /= m*n*p ) then
          print*, 'nsize =', nsize, 'size(array)', size( array(:) )
          print*, "Dimensions of the Elemental3_t = ", length
         stop 'wrong size of the array in copyElemental3to1Darray3_1_2'
      end if

      nn = 0

      do i = 1, nsize(3)!p ! start with the 3rd index (TDOF)
         do k = 1, nsize(1) !m ! 1st index - ndim
            array(nn+1:nn+nsize(2)) = this%x(k,1:nsize(2),i )
            nn = nn + nsize(2)
         end do !k

      end do ! n

    end function copyElemental3to1Darray3_1_2

    ! watch for the ordering - 3,2,1 - ordering
    function copyElemental3to1Darray( this ) result(array)
      class(Elemental3_t),intent(in) :: this
      real, dimension(1: size(this%x) ) :: array
      integer, dimension(1:3) :: length
      integer :: i, k
      integer :: m, n , p, nn

      length = this%getSize()
      m = length(1)
      n = length(2)
      p = length(3)

      if ( size( array(:) ) /= m*n*p ) &
         stop 'wrong size of the array in copyElemental3to1Darray'

      nn = 0

      do i = 1, p ! start with the 3rd index (TDOF)
         do k = 1, n ! 2nd index - ndim
            array(nn+1:nn+m) = this%x(1:m,k,i)
            nn = nn + m
         end do !k
      end do ! i

    end function copyElemental3to1Darray


    ! copy3D array to 1D array
    ! standard permutation for STDGM (/ 3,1,2 /)
    ! watch for the ordering 1:Tdof, 1:ndim, 1:dof - 3,1,2
    function copy3DarrayTo1Darray( array3, nsize) result(array)
      real, dimension(:,:,:), intent(in) :: array3
      integer, intent(in) :: nsize
      real, dimension(1:nsize) :: array
      integer, dimension(1:3) :: length
      integer :: i, k
      integer :: m, n , p, nn

      m = size( array3(:,1,1) )
      n = size( array3(1,:,1) )
      p = size( array3(1,1,:) )

      if ( nsize /= m*n*p ) then
         print*, nsize, m,n,p
         stop 'wrong size of the array in copy3DarrayTo1Darray'
      endif
      nn = 0

      do i = 1, p ! start with the 3rd index (TDOF)
         do k = 1, m ! 1st index ndim ,
            array(nn+1:nn+n) = array3(k,1:n,i ) !2nd index - dof
            nn = nn + n
         end do !k
      end do ! n

    end function copy3DarrayTo1Darray

    ! distribute 1D array to 3D array
    ! the ordering it typical for the STDG setting
    ! (/ 3,1,2 /)
    !
    ! 1:ndim, 1:dof, 1:Tdof is ordered Tdof->ndim->dof
    function copy1DarrayTo3Darray( array1, n1, n2, n3 ) result (array3)
      real, dimension(:), intent(in) :: array1
      integer, intent(in) :: n1, n2, n3
      real, dimension(1:n1,1:n2,1:n3) :: array3

      integer :: i, k
      integer :: nn
      nn = 0

      !stop "copy1DarrayTo3Darray not correct !"

      do i = 1, n3 ! start with the 3rd index (TDOF)
         do k = 1, n1 ! 1st index ndim ,
            array3(k, 1:n2,i) = array1(nn+1:nn+n2) !2nd index - dof
            nn = nn + n2
         end do !k
      end do ! n

      if ( size(array1(:)) /= nn) then
         print*,  n1,n2,n3, nn, size(array1(:))
         stop 'wrong sizes in copy1DarrayTo3Darray'
      endif

    end function copy1DarrayTo3Darray

    subroutine deleteArrayElemental1(array)
      type(Elemental1_t), dimension(:), intent(inout) :: array
      integer :: i , l
      !if (allocated( array ) ) then
      l = size( array(:) )
      do i = 1,l
        call array(i)%delete()
      enddo

    end subroutine deleteArrayElemental1


    subroutine deleteArrayElemental3(array)
      type(Elemental3_t), dimension(:), intent(inout) :: array
      integer :: i , l
      !if (allocated( array ) ) then
      l = size( array(:) )
      do i = 1,l
        call array(i)%delete()
      enddo

    end subroutine deleteArrayElemental3

end module elemental_mod
