{# busd = builder shared #}
{% set busd=builder.shared %}
{% set module=builder.module %}
!
! Manager for ``{{ module.name }}``'s ``{{ module.provides.name }}`` derived type
!
! In combination with ``{{ module.wrapper_module_name }}``,
! this allows the ``{{ module.provides.name }}`` derived type
! to be exposed to Python.
!
module {{ module.manager_module_name }}

    use {{ module.name }}, only: {{ module.provides.name }}
    use fgen_utils, only: &
        finalize_derived_type_instance_number, &
        get_derived_type_free_instance_number

    implicit none
    private

    integer, parameter :: N_INSTANCES = {{ busd.n_fortran_instances_to_expose }}

{#
    As part of https://gitlab.com/magicc/fgen/-/issues/4,
    I would update this so it is allocatable too.
    Then we just allocate when a new instance is requested
    and deallocate on finalisation.
#}
    type({{ module.provides.name }}), target, dimension(N_INSTANCES) :: instance_array
    logical, dimension(N_INSTANCES) :: instance_available = .true.

    public :: {{ busd.fortran_get_free_instance_number_callable_name }}, &
              {{ busd.fortran_get_instance_callable_name }}, &
              {{ busd.fortran_instance_finalise_callable_name }}

contains

    function {{ busd.fortran_get_free_instance_number_callable_name }}() result(instance_index)
        ! Get the index of a free instance

        integer :: instance_index
        ! Free instance index

        call get_derived_type_free_instance_number( &
            instance_index, &
            N_INSTANCES, &
            instance_available, &
            instance_array &
            )

    end function {{ busd.fortran_get_free_instance_number_callable_name }}

    subroutine {{ busd.fortran_get_instance_callable_name }}(instance_index, instance_pointer)
        ! Associate a pointer with the instance corresponding to the given model index
        !
        ! Stops execution if the instance has not already been initialised.

        integer, intent(in) :: instance_index
        ! Index of the instance to point to

        type({{ module.provides.name }}), pointer, intent(inout) :: instance_pointer
        ! Pointer to associate

        call check_index_claimed(instance_index)
        instance_pointer => instance_array(instance_index)

    end subroutine {{ busd.fortran_get_instance_callable_name }}

    subroutine {{ busd.fortran_instance_finalise_callable_name }}(instance_index)
        ! Finalise an instance

        integer, intent(in) :: instance_index
        ! Index of the instance to finalise

        call check_index_claimed(instance_index)
        call finalize_derived_type_instance_number( &
            instance_index, &
            N_INSTANCES, &
            instance_available, &
            instance_array &
            )

    end subroutine {{ busd.fortran_instance_finalise_callable_name }}

    subroutine check_index_claimed(instance_index)
        ! Check that an index has already been claimed
        !
        ! Stops execution if the index has not been claimed.

        integer, intent(in) :: instance_index
        ! Instance index to check

        if (instance_available(instance_index)) then
            print *, "Index ", instance_index, " has not been claimed"
            error stop 1
        end if

        if (instance_index < 1) then
            ! TODO: return error code to python
            print *, "Requested index is ", instance_index, " which is less than 1"
            error stop 1
        end if

        if (instance_array(instance_index) % instance_index < 1) then
            ! TODO: return error code to python
            print *, "Index ", instance_index, " is associated with an instance that has instance index < 1", &
                "instance's instance_index attribute ", instance_array(instance_index) % instance_index
            error stop 1
        end if

    end subroutine check_index_claimed

end module {{ module.manager_module_name }}
