module fortplot_mathtext_layout
    !! Font-independent vertical boxes, measured in units of the base font size.
    use, intrinsic :: iso_fortran_env, only: wp => real64
    use fortplot_mathtext, only: mathtext_element_t, parse_mathtext, &
        ELEMENT_NORMAL, ELEMENT_SQRT, ELEMENT_FRACTION, &
                                ELEMENT_SPACE
    implicit none
    private
    public :: mathtext_vertical_bounds, fraction_vertical_offsets

contains

    recursive subroutine mathtext_vertical_bounds(elements, above, below)
        type(mathtext_element_t), intent(in) :: elements(:)
        real(wp), intent(out) :: above, below
        real(wp) :: child_above, child_below, scale
        integer :: i

        above = 0.0_wp
        below = 0.0_wp
        do i = 1, size(elements)
            call element_vertical_bounds(elements(i), child_above, child_below)
            scale = elements(i)%font_size_ratio
            above = max(above, elements(i)%vertical_offset + scale*child_above)
            below = max(below, -elements(i)%vertical_offset + scale*child_below)
        end do
    end subroutine mathtext_vertical_bounds

    recursive subroutine element_vertical_bounds(element, above, below)
        type(mathtext_element_t), intent(in) :: element
        real(wp), intent(out) :: above, below
        type(mathtext_element_t), allocatable :: children(:)
        real(wp) :: other_above, other_below, numerator_y, denominator_y

        if (element%element_type == ELEMENT_SPACE) then
            above = 0.0_wp
            below = 0.0_wp
            return
        end if
        if (element%element_type == ELEMENT_NORMAL) then
            above = 0.72_wp
            below = 0.0_wp
            if (scan(element%text, 'gjpqy') > 0) below = 0.22_wp
            return
        end if
        children = parse_mathtext(element%text, element%italic)
        call mathtext_vertical_bounds(children, above, below)
        if (element%element_type == ELEMENT_SQRT) above = above + 0.08_wp
        if (element%element_type == ELEMENT_FRACTION) then
            call fraction_vertical_offsets(element, numerator_y, denominator_y)
            children = parse_mathtext(element%denominator, element%italic)
            call mathtext_vertical_bounds(children, other_above, other_below)
            above = numerator_y + 0.7_wp*above
            below = -denominator_y + 0.7_wp*other_below
        end if
    end subroutine element_vertical_bounds

    recursive subroutine fraction_vertical_offsets(element, numerator_y, denominator_y)
        type(mathtext_element_t), intent(in) :: element
        real(wp), intent(out) :: numerator_y, denominator_y
        type(mathtext_element_t), allocatable :: children(:)
        real(wp) :: above, below

        children = parse_mathtext(element%text, element%italic)
        call mathtext_vertical_bounds(children, above, below)
        numerator_y = 0.375_wp + 0.7_wp*below
        children = parse_mathtext(element%denominator, element%italic)
        call mathtext_vertical_bounds(children, above, below)
        denominator_y = 0.125_wp - 0.7_wp*above
    end subroutine fraction_vertical_offsets

end module fortplot_mathtext_layout
