module nonlinear_mod
   use anderson_mod
   use pseudotime_mod

   implicit none

   !> from Newton_type
   !> structure of the nonlinear solver, only Newton-like method implemented
   type, public :: NonlinearSol_t
      character(len=20) :: name
      character(len=20) ::  non_alg_stop  ! type of stopping criterion
      integer :: max_iter        ! maximal number of the Newton iterations
      integer :: iter            ! number of actual Newton iterations
      integer :: Aiter           ! number of actual Newton iterations in one adaptive cycle
      real :: tol, tol2          ! tolerances for the Newton
      integer :: min_update      ! number of minimal update of the Flux matrix Newton method
      integer :: updates         ! number of performed updates of the Flux matrix Newton method
      real :: norm_res
      real :: lambda, lambda1, lambda_old
      real :: theta
      real :: res, res0, res1, res_ref    ! residuum \f$ \| f(w)\|_{\ell^2} \f$
      real, dimension(:), allocatable ::  x, b, b1, rr  ! arrays for computation

      ! ideally this whole should be in the inner structure AndersonAcceleration_t
      ! FR moved to AndersonAcceleration_t
      !integer:: AA_max               ! Anderson acceleration, maximal number of considered vectors
      !real, dimension(:,:,:), allocatable ::  AA_x
      !real, dimension(:), allocatable ::  AA_update
      !integer :: AA_F         ! update d^{k+1} = w^{k+1} - w^{k}
      !integer :: AA_w         ! result of update w^{k+1}
      !integer :: AA_m         ! the maximal value AA_F, ...

      logical :: anderson ! do we use anderson acceleration
      type( AndersonAcceleration_t ), allocatable :: andersonAcc ! data used for Anderson acceleration

      logical :: pseudoT ! do we use the pseudo-time acceleration
      type( PseudoTimeStepping_t ), allocatable :: pseudo_time ! data used for Anderson acceleration

      logical :: implicitly                ! .true. = implicit performace !from state
      integer :: newton_count    ! number of iteration* numberOFDampingIterations since last fill of the matrix C(w)
      logical :: converged
      contains

      procedure :: init => initNonlinear
      procedure :: InitNLSolverSettings

   end type NonlinearSol_t

   type, public, extends ( NonlinearSol_t ) :: Newton_t

      contains

      procedure :: init => initNewton
      procedure :: prepareNewton

   end type Newton_t

   contains

   subroutine initNonlinear( this, name, non_alg_stop, tol, max_iter, min_update )
      class( NonlinearSol_t ), intent ( inout ) :: this
      character(len = 20), intent( in ) :: name
      character(len = 20), intent( in ) :: non_alg_stop
      real, intent( in ) :: tol
      integer, intent( in ) :: max_iter
      integer, intent( in ) :: min_update

      print*, 'Only abstract type Nonlinear_t. Should be changed to ABSTRACT!'


   end subroutine initNonlinear

        !> initialization of for the Newton methods, max_iter, tolerance ...
  subroutine InitNLSolverSettings( this, time_method )
    class( NonlinearSol_t ), intent ( inout ) :: this
    character :: time_method

    this%Aiter = 0
    this%updates = 0

    if( time_method == 'E') then       ! explicit time discretization
       this%implicitly = .false.
       this%max_iter = 1

    elseif( time_method == 'I') then   ! fully implicit time discretization
       this%implicitly = .true.

    elseif( time_method == 'S') then   ! semi-implicit time discretization
       this%implicitly = .true.
       this%max_iter = 1

   elseif( time_method == 'P') then   ! implicit method with the pseudo-time stepping
       this%implicitly = .false.
       !this%Newton%max_iter = 30


    endif

  end subroutine InitNLSolverSettings

   subroutine initNewton( this, name, non_alg_stop, tol, max_iter, min_update )
      class( Newton_t ), intent ( inout ) :: this
      character(len = 20), intent( in ) :: name
      character(len = 20), intent( in ) :: non_alg_stop
      real, intent( in ) :: tol
      integer, intent( in ) :: max_iter
      integer, intent( in ) :: min_update

      if ( name == 'Newton' ) then
         this%name = name
         this%anderson = .false.
         this%pseudoT = .false.

      ! the Anderson acceleration
      else if ( name == 'NewtonA' ) then
         this%name = 'Newton'
         this%anderson = .true.
         this%pseudoT = .false.

      ! pseudo-time stepping
      else if ( name == 'NewtonP' ) then
         this%name = 'Newton'
         this%anderson = .false.
         this%pseudoT = .true.
         !stop "NewtonP is NOT YET INSTALLED !!! "
      else
         stop 'Unknown type of Newton solver in initNewton'
      endif

      this%non_alg_stop = non_alg_stop
      this%tol = tol
      this%max_iter = max_iter
      this%min_update = min_update
      this%newton_count = 0

      ! Anderson acceleration is used - allocate its data
      if (this%anderson)  allocate( this%andersonAcc )

      ! pseudo-time stepping is used - allocate its data
      if (this%pseudoT)  allocate( this%pseudo_time )

      if(this%non_alg_stop == 'aRES' .or. this%non_alg_stop == 'rezL2' ) then
        write(*,'(a45,a6,a8, es9.2, a11, i3, a13, i3)') &
             '  # Newton-like solver: stopping criterion = ', &
             this%non_alg_stop,', tol = ', this%tol, &
             ', max_iter=', this%max_iter, ', min_update=', this%min_update
        if( this%non_alg_stop == 'aRES' ) then
           this%tol2 = this%tol
           this%tol = 1E-15
        else
           this%tol2 = -1.  ! NOT USED
        endif
      else if ( this%non_alg_stop == 'aDWR') then
        write(*,'(a45,a6,a8, es9.2, a11, i3, a13, i3)') &
             '  # Newton-like solver: stopping criterion = ', &
             this%non_alg_stop,', tol = ', this%tol, &
             ', max_iter=', this%max_iter, ', min_update=', this%min_update
        this%tol2 = -1

      else
        print*,' Unknown stopping criterion for Newton method,',  &
             ' only "aRES" and "rezL2" are implemented'
        stop
     endif

   end subroutine initNewton

   !> allocate Newton structures according to the given size
   subroutine prepareNewton( this, nsize )
      class( Newton_t ), intent ( inout ) :: this
      integer, intent( in ) :: nsize

      if(size(this%b(:) ) /= nsize  )then
          deallocate( this%b )
          allocate(this%b(1:nsize), source = 0.0 )
      endif
      if(size(this%x(:) ) /= nsize  )then
          deallocate( this%x )
          allocate(this%x(1:nsize), source = 0.0 )
      endif
      if(size(this%b1(:) ) /= nsize  )then
          deallocate( this%b1 )
          allocate(this%b1(1:nsize), source = 0.0 )
      endif
      if(size(this%rr(:) ) /= nsize  )then
          deallocate( this%rr )
          allocate(this%rr(1:nsize), source = 0.0 )
      endif

      open(54, file='newton-update', status='unknown', position='append')
       open(55, file='newton', status='unknown', position='append')

       write(55,*) '  '
       write(55,*) '## it  i  l    ritr     ritr2   ', &
            '   |F(x)|/N  |F(x*)|/N   |F(x)|   |F(x*)|      rel_res ', &
            '     gmr_tol    lambda     theta       etaS    algeb   LAiter IM Pr'

       this%newton_count = 0
       this%iter  = 0



   end subroutine prepareNewton






end module nonlinear_mod
