<reponame>pbellec/SUITPy
# -*- coding: utf-8 -*-
"""Functions related to the documentation.

@author: maedbhking

docdict contains the standard documentation entries
used across SUITPy (source: Nilearn and Eric Larson and MNE-python team.
https://github.com/mne-tools/mne-python/blob/main/mne/utils/docs.py
"""

import sys

###################################
# Standard documentation entries
#
docdict = dict()

NILEARN_LINKS = {"landing_page": "http://nilearn.github.io"}
NILEARN_LINKS["input_output"] = (
    "{}/manipulating_images/input_output.html".format(
        NILEARN_LINKS["landing_page"])
)

# Verbose
verbose = """
verbose : :obj:`int`, optional
    Verbosity level (0 means no message).
    Default={}."""
docdict['verbose'] = verbose.format(1)
docdict['verbose0'] = verbose.format(0)

# Resume
docdict['resume'] = """
resume : :obj:`bool`, optional
    Whether to resume download of a partly-downloaded file.
    Default=True."""

# Data_dir
docdict['data_dir'] = """
data_dir : :obj:`pathlib.Path` or :obj:`str`, optional
    Path where data should be downloaded. By default,
    files are downloaded in home directory."""

# URL
docdict['url'] = """
url : :obj:`str`, optional
    URL of file to download.
    Override download URL. Used for test only (or if you
    setup a mirror of the data).
    Default=None."""

# Smoothing_fwhm
docdict['smoothing_fwhm'] = """
smoothing_fwhm : :obj:`float`, optional.
    If ``smoothing_fwhm`` is not ``None``, it gives
    the full-width at half maximum in millimeters
    of the spatial smoothing to apply to the signal."""

# Standardize
standardize = """
standardize : :obj:`bool`, optional.
    If ``standardize`` is True, the data are centered and normed:
    their mean is put to 0 and their variance is put to 1 in the
    time dimension.
    Default={}."""
docdict['standardize'] = standardize.format('True')
docdict['standardize_false'] = standardize.format('False')

# detrend
docdict['detrend'] = """
detrend : :obj:`bool`, optional
    Whether to detrend signals or not."""

# Target_affine
docdict['target_affine'] = """
target_affine : :class:`numpy.ndarray`, optional.
    If specified, the image is resampled corresponding to this new affine.
    ``target_affine`` can be a 3x3 or a 4x4 matrix.
    Default=None."""

# Target_shape
docdict['target_shape'] = """
target_shape : :obj:`tuple` or :obj:`list`, optional.
    If specified, the image will be resized to match this new shape.
    ``len(target_shape)`` must be equal to 3.

    .. note::
        If ``target_shape`` is specified, a ``target_affine`` of shape
        ``(4, 4)`` must also be given.

    Default=None."""

# Low_pass
docdict['low_pass'] = """
low_pass : :obj:`float` or None, optional
    Low cutoff frequency in Hertz.
    If None, no low-pass filtering will be performed.
    Default=None."""

# High pass
docdict['high_pass'] = """
high_pass : :obj:`float`, optional
    High cutoff frequency in Hertz.
    Default=None."""

# t_r
docdict['t_r'] = """
t_r : :obj:`float` or None, optional
    Repetition time, in seconds (sampling period).
    Set to ``None`` if not provided.
    Default=None."""

# mask_img
docdict['mask_img'] = """
mask_img : Niimg-like object
    Object used for masking the data."""

# Memory
docdict['memory'] = """
memory : instance of :class:`joblib.Memory` or :obj:`str`
    Used to cache the masking process.
    By default, no caching is done. If a :obj:`str` is given, it is the
    path to the caching directory."""

# n_parcels
docdict['n_parcels'] = """
n_parcels : :obj:`int`, optional
    Number of parcels to divide the data into.
    Default=50."""

# random_state
docdict['random_state'] = """
random_state : :obj:`int` or RandomState, optional
    Pseudo-random number generator state used for random sampling."""

# Memory_level
memory_level = """
memory_level : :obj:`int`, optional.
    Rough estimator of the amount of memory used by caching. Higher value
    means more memory for caching.
    Default={}."""
docdict['memory_level'] = memory_level.format(0)
docdict['memory_level1'] = memory_level.format(1)

# n_jobs
n_jobs = """
n_jobs : :obj:`int`, optional.
    The number of CPUs to use to do the computation. -1 means 'all CPUs'.
    Default={}."""
docdict['n_jobs'] = n_jobs.format("1")
docdict['n_jobs_all'] = n_jobs.format("-1")

# img
docdict['img'] = """
img : Niimg-like object
    See `input-output <%(input_output)s>`_.
""" % NILEARN_LINKS

# imgs
docdict['imgs'] = """
imgs : :obj:`list` of Niimg-like objects
    See `input-output <%(input_output)s>`_.
""" % NILEARN_LINKS

# cut_coords
docdict['cut_coords'] = """
cut_coords : None, a :obj:`tuple` of :obj:`float`, or :obj:`int`, optional
    The MNI coordinates of the point where the cut is performed.

        - If ``display_mode`` is 'ortho' or 'tiled', this should
          be a 3-tuple: ``(x, y, z)``
        - For ``display_mode == 'x'``, 'y', or 'z', then these are
          the coordinates of each cut in the corresponding direction.
        - If ``None`` is given, the cuts are calculated automaticaly.
        - If ``display_mode`` is 'mosaic', and the number of cuts is the same
          for all directions, ``cut_coords`` can be specified as an integer.
          It can also be a length 3 tuple specifying the number of cuts for
          every direction if these are different.

        .. note::

            If ``display_mode`` is 'x', 'y' or 'z', ``cut_coords`` can be
            an integer, in which case it specifies the number of
            cuts to perform.

"""

# output_file
docdict['output_file'] = """
output_file : :obj:`str`, or None, optional
    The name of an image file to export the plot to. Valid extensions
    are .png, .pdf, .svg. If ``output_file`` is not None, the plot
    is saved to a file, and the display is closed."""

# display_mode
docdict['display_mode'] = """
display_mode : {'ortho', 'tiled', 'mosaic','x',\
'y', 'z', 'yx', 'xz', 'yz'}, optional
    Choose the direction of the cuts:

        - 'x': sagittal
        - 'y': coronal
        - 'z': axial
        - 'ortho': three cuts are performed in orthogonal
          directions
        - 'tiled': three cuts are performed and arranged
          in a 2x2 grid
        - 'mosaic': three cuts are performed along
          multiple rows and columns

    Default='ortho'."""

# figure
docdict['figure'] = """
figure : :obj:`int`, or :class:`matplotlib.figure.Figure`, or None,  optional
    Matplotlib figure used or its number. If ``None`` is given, a
    new figure is created."""

# axes
docdict['axes'] = """
axes : :class:`matplotlib.axes.Axes`, or 4 tuple\
of :obj:`float`: (xmin, ymin, width, height), optional
    The axes, or the coordinates, in matplotlib figure
    space, of the axes used to display the plot.
    If ``None``, the complete figure is used."""

# title
docdict['title'] = """
title : :obj:`str`, or None, optional
    The title displayed on the figure.
    Default=None."""

# threshold
docdict['threshold'] = """
threshold : a number, None, or 'auto', optional
    If ``None`` is given, the image is not thresholded.
    If a number is given, it is used to threshold the image:
    values below the threshold (in absolute value) are plotted
    as transparent. If 'auto' is given, the threshold is determined
    magically by analysis of the image.
"""

# annotate
docdict['annotate'] = """
annotate : :obj:`bool`, optional
    If ``annotate`` is ``True``, positions and left/right annotation
    are added to the plot. Default=True."""

# draw_cross
docdict['draw_cross'] = """
draw_cross : :obj:`bool`, optional
    If ``draw_cross`` is ``True``, a cross is drawn on the plot to indicate
    the cut position. Default=True."""

# black_bg
docdict['black_bg'] = """
black_bg : :obj:`bool`, or 'auto', optional
    If ``True``, the background of the image is set to be black.
    If you wish to save figures with a black background, you
    will need to pass facecolor='k', edgecolor='k'
    to :func:`matplotlib.pyplot.savefig`."""

# colorbar
docdict['colorbar'] = """
colorbar : :obj:`bool`, optional
    If ``True``, display a colorbar on the right of the plots."""

# symmetric_cbar
docdict['symmetric_cbar'] = """
symmetric_cbar : :obj:`bool`, or 'auto', optional
    Specifies whether the colorbar should range from ``-vmax`` to ``vmax``
    or from ``vmin`` to ``vmax``. Setting to 'auto' will select the latter
    if the range of the whole image is either positive or negative.

    .. note::

        The colormap will always range from ``-vmax`` to ``vmax``.

"""

# cbar_tick_format
docdict['cbar_tick_format'] = """
cbar_tick_format : :obj:`str`, optional
    Controls how to format the tick labels of the colorbar.
    Ex: use "%%.2g" to display using scientific notation."""

# bg_img
docdict['bg_img'] = """
bg_img : Niimg-like object, optional
    See `input_output <%(input_output)s>`_.
    The background image to plot on top of.
""" % NILEARN_LINKS

# vmin
docdict['vmin'] = """
vmin : :obj:`float`, optional
    Lower bound of the colormap. If ``None``, the min of the image is used.
    Passed to :func:`matplotlib.pyplot.imshow`.
"""

# vmax
docdict['vmax'] = """
vmax : :obj:`float`, optional
    Upper bound of the colormap. If ``None``, the max of the image is used.
    Passed to :func:`matplotlib.pyplot.imshow`.
"""

# bg_vmin
docdict['bg_vmin'] = """
bg_vmin : :obj:`float`, optional
    vmin for ``bg_img``."""

# bg_vmax
docdict['bg_vmax'] = """
bg_vmin : :obj:`float`, optional
    vmax for ``bg_img``."""

# resampling_interpolation
docdict['resampling_interpolation'] = """
resampling_interpolation : :obj:`str`, optional
    Interpolation to use when resampling the image to
    the destination space. Can be:

        - "continuous": use 3rd-order spline interpolation
        - "nearest": use nearest-neighbor mapping.

            .. note::

                "nearest" is faster but can be noisier in some cases.

"""

# cmap
docdict['cmap'] = """
cmap : :class:`matplotlib.colors.Colormap`, or :obj:`str`, optional
    The colormap to use. Either a string which is a name of
    a matplotlib colormap, or a matplotlib colormap object."""

# Dimming factor
docdict['dim'] = """
dim : :obj:`float`, or 'auto', optional
    Dimming factor applied to background image. By default, automatic
    heuristics are applied based upon the background image intensity.
    Accepted float values, where a typical span is between -2 and 2
    (-2 = increase contrast; 2 = decrease contrast), but larger values
    can be used for a more pronounced effect. 0 means no dimming."""

# avg_method
docdict['avg_method'] = """
avg_method : {'mean', 'median', 'min', 'max', custom function}, optional
    How to average vertex values to derive the face value:

        - ``mean``: results in smooth boundaries
        - ``median``: results in sharp boundaries
        - ``min`` or ``max``: for sparse matrices
        - ``custom function``: You can also pass a custom function
          which will be executed though :func:`numpy.apply_along_axis`.
          Here is an example of a custom function:

            .. code-block:: python

                def custom_function(vertices):
                    return vertices[0] * vertices[1] * vertices[2]

"""

# hemi
docdict['hemi'] = """
hemi : {'left', 'right'}, optional
    Hemisphere to display. Default='left'."""

# hemispheres
docdict['hemispheres'] = """
hemispheres : list of :obj:`str`, optional
    Hemispheres to display. Default=['left', 'right']."""

# view
docdict['view'] = """
view : {'lateral', 'medial', 'dorsal', 'ventral',\
        'anterior', 'posterior'}, optional
    View of the surface that is rendered.
    Default='lateral'.
"""

# bg_on_data
docdict['bg_on_data'] = """
bg_on_data : :obj:`bool`, optional
    If ``True``, and a ``bg_map`` is specified,
    the ``surf_data`` data is multiplied by the background
    image, so that e.g. sulcal depth is visible beneath
    the ``surf_data``.

        .. note::
            This non-uniformly changes the surf_data values according
            to e.g the sulcal depth.

"""

# darkness
docdict['darkness'] = """
darkness : :obj:`float` between 0 and 1, optional
    Specifying the darkness of the background image:

        - '1' indicates that the original values of the background are used
        - '.5' indicates that the background values are reduced by half
          before being applied.

"""

# linewidth
docdict['linewidths'] = """
linewidths : :obj:`float`, optional
    Set the boundary thickness of the contours.
    Only reflects when ``view_type=contours``."""

# fsaverage options
docdict['fsaverage_options'] = """

        - 'fsaverage3': the low-resolution fsaverage3 mesh (642 nodes)
        - 'fsaverage4': the low-resolution fsaverage4 mesh (2562 nodes)
        - 'fsaverage5': the low-resolution fsaverage5 mesh (10242 nodes)
        - 'fsaverage5_sphere': the low-resolution fsaverage5 spheres

            .. deprecated:: 0.8.0
                This option has been deprecated and will be removed in v0.9.0.
                fsaverage5 sphere coordinates can now be accessed through
                attributes sphere_{left, right} using mesh='fsaverage5'

        - 'fsaverage6': the medium-resolution fsaverage6 mesh (40962 nodes)
        - 'fsaverage7': same as 'fsaverage'
        - 'fsaverage': the high-resolution fsaverage mesh (163842 nodes)

            .. note::
                The high-resolution fsaverage will result in more computation
                time and memory usage

"""

docdict_indented = {}


def _indentcount_lines(lines):
    """Minimum indent for all lines in line list.

    >>> lines = [' one', '  two', '   three']
    >>> _indentcount_lines(lines)
    1
    >>> lines = []
    >>> _indentcount_lines(lines)
    0
    >>> lines = [' one']
    >>> _indentcount_lines(lines)
    1
    >>> _indentcount_lines(['    '])
    0

    """
    indentno = sys.maxsize
    for line in lines:
        stripped = line.lstrip()
        if stripped:
            indentno = min(indentno, len(line) - len(stripped))
    if indentno == sys.maxsize:
        return 0
    return indentno


def fill_doc(f):
    """Fill a docstring with docdict entries.

    Parameters
    ----------
    f : callable
        The function to fill the docstring of. Will be modified in place.

    Returns
    -------
    f : callable
        The function, potentially with an updated ``__doc__``.

    """
    docstring = f.__doc__
    if not docstring:
        return f
    lines = docstring.splitlines()
    # Find the minimum indent of the main docstring, after first line
    if len(lines) < 2:
        icount = 0
    else:
        icount = _indentcount_lines(lines[1:])
    # Insert this indent to dictionary docstrings
    try:
        indented = docdict_indented[icount]
    except KeyError:
        indent = ' ' * icount
        docdict_indented[icount] = indented = {}
        for name, dstr in docdict.items():
            lines = dstr.splitlines()
            try:
                newlines = [lines[0]]
                for line in lines[1:]:
                    newlines.append(indent + line)
                indented[name] = '\n'.join(newlines)
            except IndexError:
                indented[name] = dstr
    try:
        f.__doc__ = docstring % indented
    except (TypeError, ValueError, KeyError) as exp:
        funcname = f.__name__
        funcname = docstring.split('\n')[0] if funcname is None else funcname
        raise RuntimeError('Error documenting %s:\n%s'
                           % (funcname, str(exp)))
    return f

# -*- coding: utf-8 -*-
"""Functions related to the documentation.

@author: maedbhking

docdict contains the standard documentation entries
used across SUITPy (source: Nilearn and Eric Larson and MNE-python team.
https://github.com/mne-tools/mne-python/blob/main/mne/utils/docs.py
"""

import sys

###################################
# Standard documentation entries
#
docdict = dict()

NILEARN_LINKS = {"landing_page": "http://nilearn.github.io"}
NILEARN_LINKS["input_output"] = (
    "{}/manipulating_images/input_output.html".format(
        NILEARN_LINKS["landing_page"])
)

# Verbose
verbose = """
verbose : :obj:`int`, optional
    Verbosity level (0 means no message).
    Default={}."""
docdict['verbose'] = verbose.format(1)
docdict['verbose0'] = verbose.format(0)

# Resume
docdict['resume'] = """
resume : :obj:`bool`, optional
    Whether to resume download of a partly-downloaded file.
    Default=True."""

# Data_dir
docdict['data_dir'] = """
data_dir : :obj:`pathlib.Path` or :obj:`str`, optional
    Path where data should be downloaded. By default,
    files are downloaded in home directory."""

# URL
docdict['url'] = """
url : :obj:`str`, optional
    URL of file to download.
    Override download URL. Used for test only (or if you
    setup a mirror of the data).
    Default=None."""

# Smoothing_fwhm
docdict['smoothing_fwhm'] = """
smoothing_fwhm : :obj:`float`, optional.
    If ``smoothing_fwhm`` is not ``None``, it gives
    the full-width at half maximum in millimeters
    of the spatial smoothing to apply to the signal."""

# Standardize
standardize = """
standardize : :obj:`bool`, optional.
    If ``standardize`` is True, the data are centered and normed:
    their mean is put to 0 and their variance is put to 1 in the
    time dimension.
    Default={}."""
docdict['standardize'] = standardize.format('True')
docdict['standardize_false'] = standardize.format('False')

# detrend
docdict['detrend'] = """
detrend : :obj:`bool`, optional
    Whether to detrend signals or not."""

# Target_affine
docdict['target_affine'] = """
target_affine : :class:`numpy.ndarray`, optional.
    If specified, the image is resampled corresponding to this new affine.
    ``target_affine`` can be a 3x3 or a 4x4 matrix.
    Default=None."""

# Target_shape
docdict['target_shape'] = """
target_shape : :obj:`tuple` or :obj:`list`, optional.
    If specified, the image will be resized to match this new shape.
    ``len(target_shape)`` must be equal to 3.

    .. note::
        If ``target_shape`` is specified, a ``target_affine`` of shape
        ``(4, 4)`` must also be given.

    Default=None."""

# Low_pass
docdict['low_pass'] = """
low_pass : :obj:`float` or None, optional
    Low cutoff frequency in Hertz.
    If None, no low-pass filtering will be performed.
    Default=None."""

# High pass
docdict['high_pass'] = """
high_pass : :obj:`float`, optional
    High cutoff frequency in Hertz.
    Default=None."""

# t_r
docdict['t_r'] = """
t_r : :obj:`float` or None, optional
    Repetition time, in seconds (sampling period).
    Set to ``None`` if not provided.
    Default=None."""

# mask_img
docdict['mask_img'] = """
mask_img : Niimg-like object
    Object used for masking the data."""

# Memory
docdict['memory'] = """
memory : instance of :class:`joblib.Memory` or :obj:`str`
    Used to cache the masking process.
    By default, no caching is done. If a :obj:`str` is given, it is the
    path to the caching directory."""

# n_parcels
docdict['n_parcels'] = """
n_parcels : :obj:`int`, optional
    Number of parcels to divide the data into.
    Default=50."""

# random_state
docdict['random_state'] = """
random_state : :obj:`int` or RandomState, optional
    Pseudo-random number generator state used for random sampling."""

# Memory_level
memory_level = """
memory_level : :obj:`int`, optional.
    Rough estimator of the amount of memory used by caching. Higher value
    means more memory for caching.
    Default={}."""
docdict['memory_level'] = memory_level.format(0)
docdict['memory_level1'] = memory_level.format(1)

# n_jobs
n_jobs = """
n_jobs : :obj:`int`, optional.
    The number of CPUs to use to do the computation. -1 means 'all CPUs'.
    Default={}."""
docdict['n_jobs'] = n_jobs.format("1")
docdict['n_jobs_all'] = n_jobs.format("-1")

# img
docdict['img'] = """
img : Niimg-like object
    See `input-output <%(input_output)s>`_.
""" % NILEARN_LINKS

# imgs
docdict['imgs'] = """
imgs : :obj:`list` of Niimg-like objects
    See `input-output <%(input_output)s>`_.
""" % NILEARN_LINKS

# cut_coords
docdict['cut_coords'] = """
cut_coords : None, a :obj:`tuple` of :obj:`float`, or :obj:`int`, optional
    The MNI coordinates of the point where the cut is performed.

        - If ``display_mode`` is 'ortho' or 'tiled', this should
          be a 3-tuple: ``(x, y, z)``
        - For ``display_mode == 'x'``, 'y', or 'z', then these are
          the coordinates of each cut in the corresponding direction.
        - If ``None`` is given, the cuts are calculated automaticaly.
        - If ``display_mode`` is 'mosaic', and the number of cuts is the same
          for all directions, ``cut_coords`` can be specified as an integer.
          It can also be a length 3 tuple specifying the number of cuts for
          every direction if these are different.

        .. note::

            If ``display_mode`` is 'x', 'y' or 'z', ``cut_coords`` can be
            an integer, in which case it specifies the number of
            cuts to perform.

"""

# output_file
docdict['output_file'] = """
output_file : :obj:`str`, or None, optional
    The name of an image file to export the plot to. Valid extensions
    are .png, .pdf, .svg. If ``output_file`` is not None, the plot
    is saved to a file, and the display is closed."""

# display_mode
docdict['display_mode'] = """
display_mode : {'ortho', 'tiled', 'mosaic','x',\
'y', 'z', 'yx', 'xz', 'yz'}, optional
    Choose the direction of the cuts:

        - 'x': sagittal
        - 'y': coronal
        - 'z': axial
        - 'ortho': three cuts are performed in orthogonal
          directions
        - 'tiled': three cuts are performed and arranged
          in a 2x2 grid
        - 'mosaic': three cuts are performed along
          multiple rows and columns

    Default='ortho'."""

# figure
docdict['figure'] = """
figure : :obj:`int`, or :class:`matplotlib.figure.Figure`, or None,  optional
    Matplotlib figure used or its number. If ``None`` is given, a
    new figure is created."""

# axes
docdict['axes'] = """
axes : :class:`matplotlib.axes.Axes`, or 4 tuple\
of :obj:`float`: (xmin, ymin, width, height), optional
    The axes, or the coordinates, in matplotlib figure
    space, of the axes used to display the plot.
    If ``None``, the complete figure is used."""

# title
docdict['title'] = """
title : :obj:`str`, or None, optional
    The title displayed on the figure.
    Default=None."""

# threshold
docdict['threshold'] = """
threshold : a number, None, or 'auto', optional
    If ``None`` is given, the image is not thresholded.
    If a number is given, it is used to threshold the image:
    values below the threshold (in absolute value) are plotted
    as transparent. If 'auto' is given, the threshold is determined
    magically by analysis of the image.
"""

# annotate
docdict['annotate'] = """
annotate : :obj:`bool`, optional
    If ``annotate`` is ``True``, positions and left/right annotation
    are added to the plot. Default=True."""

# draw_cross
docdict['draw_cross'] = """
draw_cross : :obj:`bool`, optional
    If ``draw_cross`` is ``True``, a cross is drawn on the plot to indicate
    the cut position. Default=True."""

# black_bg
docdict['black_bg'] = """
black_bg : :obj:`bool`, or 'auto', optional
    If ``True``, the background of the image is set to be black.
    If you wish to save figures with a black background, you
    will need to pass facecolor='k', edgecolor='k'
    to :func:`matplotlib.pyplot.savefig`."""

# colorbar
docdict['colorbar'] = """
colorbar : :obj:`bool`, optional
    If ``True``, display a colorbar on the right of the plots."""

# symmetric_cbar
docdict['symmetric_cbar'] = """
symmetric_cbar : :obj:`bool`, or 'auto', optional
    Specifies whether the colorbar should range from ``-vmax`` to ``vmax``
    or from ``vmin`` to ``vmax``. Setting to 'auto' will select the latter
    if the range of the whole image is either positive or negative.

    .. note::

        The colormap will always range from ``-vmax`` to ``vmax``.

"""

# cbar_tick_format
docdict['cbar_tick_format'] = """
cbar_tick_format : :obj:`str`, optional
    Controls how to format the tick labels of the colorbar.
    Ex: use "%%.2g" to display using scientific notation."""

# bg_img
docdict['bg_img'] = """
bg_img : Niimg-like object, optional
    See `input_output <%(input_output)s>`_.
    The background image to plot on top of.
""" % NILEARN_LINKS

# vmin
docdict['vmin'] = """
vmin : :obj:`float`, optional
    Lower bound of the colormap. If ``None``, the min of the image is used.
    Passed to :func:`matplotlib.pyplot.imshow`.
"""

# vmax
docdict['vmax'] = """
vmax : :obj:`float`, optional
    Upper bound of the colormap. If ``None``, the max of the image is used.
    Passed to :func:`matplotlib.pyplot.imshow`.
"""

# bg_vmin
docdict['bg_vmin'] = """
bg_vmin : :obj:`float`, optional
    vmin for ``bg_img``."""

# bg_vmax
docdict['bg_vmax'] = """
bg_vmin : :obj:`float`, optional
    vmax for ``bg_img``."""

# resampling_interpolation
docdict['resampling_interpolation'] = """
resampling_interpolation : :obj:`str`, optional
    Interpolation to use when resampling the image to
    the destination space. Can be:

        - "continuous": use 3rd-order spline interpolation
        - "nearest": use nearest-neighbor mapping.

            .. note::

                "nearest" is faster but can be noisier in some cases.

"""

# cmap
docdict['cmap'] = """
cmap : :class:`matplotlib.colors.Colormap`, or :obj:`str`, optional
    The colormap to use. Either a string which is a name of
    a matplotlib colormap, or a matplotlib colormap object."""

# Dimming factor
docdict['dim'] = """
dim : :obj:`float`, or 'auto', optional
    Dimming factor applied to background image. By default, automatic
    heuristics are applied based upon the background image intensity.
    Accepted float values, where a typical span is between -2 and 2
    (-2 = increase contrast; 2 = decrease contrast), but larger values
    can be used for a more pronounced effect. 0 means no dimming."""

# avg_method
docdict['avg_method'] = """
avg_method : {'mean', 'median', 'min', 'max', custom function}, optional
    How to average vertex values to derive the face value:

        - ``mean``: results in smooth boundaries
        - ``median``: results in sharp boundaries
        - ``min`` or ``max``: for sparse matrices
        - ``custom function``: You can also pass a custom function
          which will be executed though :func:`numpy.apply_along_axis`.
          Here is an example of a custom function:

            .. code-block:: python

                def custom_function(vertices):
                    return vertices[0] * vertices[1] * vertices[2]

"""

# hemi
docdict['hemi'] = """
hemi : {'left', 'right'}, optional
    Hemisphere to display. Default='left'."""

# hemispheres
docdict['hemispheres'] = """
hemispheres : list of :obj:`str`, optional
    Hemispheres to display. Default=['left', 'right']."""

# view
docdict['view'] = """
view : {'lateral', 'medial', 'dorsal', 'ventral',\
        'anterior', 'posterior'}, optional
    View of the surface that is rendered.
    Default='lateral'.
"""

# bg_on_data
docdict['bg_on_data'] = """
bg_on_data : :obj:`bool`, optional
    If ``True``, and a ``bg_map`` is specified,
    the ``surf_data`` data is multiplied by the background
    image, so that e.g. sulcal depth is visible beneath
    the ``surf_data``.

        .. note::
            This non-uniformly changes the surf_data values according
            to e.g the sulcal depth.

"""

# darkness
docdict['darkness'] = """
darkness : :obj:`float` between 0 and 1, optional
    Specifying the darkness of the background image:

        - '1' indicates that the original values of the background are used
        - '.5' indicates that the background values are reduced by half
          before being applied.

"""

# linewidth
docdict['linewidths'] = """
linewidths : :obj:`float`, optional
    Set the boundary thickness of the contours.
    Only reflects when ``view_type=contours``."""

# fsaverage options
docdict['fsaverage_options'] = """

        - 'fsaverage3': the low-resolution fsaverage3 mesh (642 nodes)
        - 'fsaverage4': the low-resolution fsaverage4 mesh (2562 nodes)
        - 'fsaverage5': the low-resolution fsaverage5 mesh (10242 nodes)
        - 'fsaverage5_sphere': the low-resolution fsaverage5 spheres

            .. deprecated:: 0.8.0
                This option has been deprecated and will be removed in v0.9.0.
                fsaverage5 sphere coordinates can now be accessed through
                attributes sphere_{left, right} using mesh='fsaverage5'

        - 'fsaverage6': the medium-resolution fsaverage6 mesh (40962 nodes)
        - 'fsaverage7': same as 'fsaverage'
        - 'fsaverage': the high-resolution fsaverage mesh (163842 nodes)

            .. note::
                The high-resolution fsaverage will result in more computation
                time and memory usage

"""

docdict_indented = {}


def _indentcount_lines(lines):
    """Minimum indent for all lines in line list.

    >>> lines = [' one', '  two', '   three']
    >>> _indentcount_lines(lines)
    1
    >>> lines = []
    >>> _indentcount_lines(lines)
    0
    >>> lines = [' one']
    >>> _indentcount_lines(lines)
    1
    >>> _indentcount_lines(['    '])
    0

    """
    indentno = sys.maxsize
    for line in lines:
        stripped = line.lstrip()
        if stripped:
            indentno = min(indentno, len(line) - len(stripped))
    if indentno == sys.maxsize:
        return 0
    return indentno


def fill_doc(f):
    """Fill a docstring with docdict entries.

    Parameters
    ----------
    f : callable
        The function to fill the docstring of. Will be modified in place.

    Returns
    -------
    f : callable
        The function, potentially with an updated ``__doc__``.

    """
    docstring = f.__doc__
    if not docstring:
        return f
    lines = docstring.splitlines()
    # Find the minimum indent of the main docstring, after first line
    if len(lines) < 2:
        icount = 0
    else:
        icount = _indentcount_lines(lines[1:])
    # Insert this indent to dictionary docstrings
    try:
        indented = docdict_indented[icount]
    except KeyError:
        indent = ' ' * icount
        docdict_indented[icount] = indented = {}
        for name, dstr in docdict.items():
            lines = dstr.splitlines()
            try:
                newlines = [lines[0]]
                for line in lines[1:]:
                    newlines.append(indent + line)
                indented[name] = '\n'.join(newlines)
            except IndexError:
                indented[name] = dstr
    try:
        f.__doc__ = docstring % indented
    except (TypeError, ValueError, KeyError) as exp:
        funcname = f.__name__
        funcname = docstring.split('\n')[0] if funcname is None else funcname
        raise RuntimeError('Error documenting %s:\n%s'
                           % (funcname, str(exp)))
    return f
# -*- coding: utf-8 -*- Functions related to the documentation.  @author: maedbhking  docdict contains the standard documentation entries used across SUITPy (source: Nilearn and Eric Larson and MNE-python team. https://github.com/mne-tools/mne-python/blob/main/mne/utils/docs.py ################################### # Standard documentation entries # # Verbose verbose : :obj:`int`, optional     Verbosity level (0 means no message).     Default={}. # Resume resume : :obj:`bool`, optional     Whether to resume download of a partly-downloaded file.     Default=True. # Data_dir data_dir : :obj:`pathlib.Path` or :obj:`str`, optional     Path where data should be downloaded. By default,     files are downloaded in home directory. # URL url : :obj:`str`, optional     URL of file to download.     Override download URL. Used for test only (or if you     setup a mirror of the data).     Default=None. # Smoothing_fwhm smoothing_fwhm : :obj:`float`, optional.     If ``smoothing_fwhm`` is not ``None``, it gives     the full-width at half maximum in millimeters     of the spatial smoothing to apply to the signal. # Standardize standardize : :obj:`bool`, optional.     If ``standardize`` is True, the data are centered and normed:     their mean is put to 0 and their variance is put to 1 in the     time dimension.     Default={}. # detrend detrend : :obj:`bool`, optional     Whether to detrend signals or not. # Target_affine target_affine : :class:`numpy.ndarray`, optional.     If specified, the image is resampled corresponding to this new affine.     ``target_affine`` can be a 3x3 or a 4x4 matrix.     Default=None. # Target_shape target_shape : :obj:`tuple` or :obj:`list`, optional.     If specified, the image will be resized to match this new shape.     ``len(target_shape)`` must be equal to 3.      .. note::         If ``target_shape`` is specified, a ``target_affine`` of shape         ``(4, 4)`` must also be given.      Default=None. # Low_pass low_pass : :obj:`float` or None, optional     Low cutoff frequency in Hertz.     If None, no low-pass filtering will be performed.     Default=None. # High pass high_pass : :obj:`float`, optional     High cutoff frequency in Hertz.     Default=None. # t_r t_r : :obj:`float` or None, optional     Repetition time, in seconds (sampling period).     Set to ``None`` if not provided.     Default=None. # mask_img mask_img : Niimg-like object     Object used for masking the data. # Memory memory : instance of :class:`joblib.Memory` or :obj:`str`     Used to cache the masking process.     By default, no caching is done. If a :obj:`str` is given, it is the     path to the caching directory. # n_parcels n_parcels : :obj:`int`, optional     Number of parcels to divide the data into.     Default=50. # random_state random_state : :obj:`int` or RandomState, optional     Pseudo-random number generator state used for random sampling. # Memory_level memory_level : :obj:`int`, optional.     Rough estimator of the amount of memory used by caching. Higher value     means more memory for caching.     Default={}. # n_jobs n_jobs : :obj:`int`, optional.     The number of CPUs to use to do the computation. -1 means 'all CPUs'.     Default={}. # img img : Niimg-like object     See `input-output <%(input_output)s>`_. # imgs imgs : :obj:`list` of Niimg-like objects     See `input-output <%(input_output)s>`_. # cut_coords cut_coords : None, a :obj:`tuple` of :obj:`float`, or :obj:`int`, optional     The MNI coordinates of the point where the cut is performed.          - If ``display_mode`` is 'ortho' or 'tiled', this should           be a 3-tuple: ``(x, y, z)``         - For ``display_mode == 'x'``, 'y', or 'z', then these are           the coordinates of each cut in the corresponding direction.         - If ``None`` is given, the cuts are calculated automaticaly.         - If ``display_mode`` is 'mosaic', and the number of cuts is the same           for all directions, ``cut_coords`` can be specified as an integer.           It can also be a length 3 tuple specifying the number of cuts for           every direction if these are different.          .. note::              If ``display_mode`` is 'x', 'y' or 'z', ``cut_coords`` can be             an integer, in which case it specifies the number of             cuts to perform. # output_file output_file : :obj:`str`, or None, optional     The name of an image file to export the plot to. Valid extensions     are .png, .pdf, .svg. If ``output_file`` is not None, the plot     is saved to a file, and the display is closed. # display_mode display_mode : {'ortho', 'tiled', 'mosaic','x',\ 'y', 'z', 'yx', 'xz', 'yz'}, optional     Choose the direction of the cuts:          - 'x': sagittal         - 'y': coronal         - 'z': axial         - 'ortho': three cuts are performed in orthogonal           directions         - 'tiled': three cuts are performed and arranged           in a 2x2 grid         - 'mosaic': three cuts are performed along           multiple rows and columns      Default='ortho'. # figure figure : :obj:`int`, or :class:`matplotlib.figure.Figure`, or None,  optional     Matplotlib figure used or its number. If ``None`` is given, a     new figure is created. # axes axes : :class:`matplotlib.axes.Axes`, or 4 tuple\ of :obj:`float`: (xmin, ymin, width, height), optional     The axes, or the coordinates, in matplotlib figure     space, of the axes used to display the plot.     If ``None``, the complete figure is used. # title title : :obj:`str`, or None, optional     The title displayed on the figure.     Default=None. # threshold threshold : a number, None, or 'auto', optional     If ``None`` is given, the image is not thresholded.     If a number is given, it is used to threshold the image:     values below the threshold (in absolute value) are plotted     as transparent. If 'auto' is given, the threshold is determined     magically by analysis of the image. # annotate annotate : :obj:`bool`, optional     If ``annotate`` is ``True``, positions and left/right annotation     are added to the plot. Default=True. # draw_cross draw_cross : :obj:`bool`, optional     If ``draw_cross`` is ``True``, a cross is drawn on the plot to indicate     the cut position. Default=True. # black_bg black_bg : :obj:`bool`, or 'auto', optional     If ``True``, the background of the image is set to be black.     If you wish to save figures with a black background, you     will need to pass facecolor='k', edgecolor='k'     to :func:`matplotlib.pyplot.savefig`. # colorbar colorbar : :obj:`bool`, optional     If ``True``, display a colorbar on the right of the plots. # symmetric_cbar symmetric_cbar : :obj:`bool`, or 'auto', optional     Specifies whether the colorbar should range from ``-vmax`` to ``vmax``     or from ``vmin`` to ``vmax``. Setting to 'auto' will select the latter     if the range of the whole image is either positive or negative.      .. note::          The colormap will always range from ``-vmax`` to ``vmax``. # cbar_tick_format cbar_tick_format : :obj:`str`, optional     Controls how to format the tick labels of the colorbar.     Ex: use "%%.2g" to display using scientific notation. # bg_img bg_img : Niimg-like object, optional     See `input_output <%(input_output)s>`_.     The background image to plot on top of. # vmin vmin : :obj:`float`, optional     Lower bound of the colormap. If ``None``, the min of the image is used.     Passed to :func:`matplotlib.pyplot.imshow`. # vmax vmax : :obj:`float`, optional     Upper bound of the colormap. If ``None``, the max of the image is used.     Passed to :func:`matplotlib.pyplot.imshow`. # bg_vmin bg_vmin : :obj:`float`, optional     vmin for ``bg_img``. # bg_vmax bg_vmin : :obj:`float`, optional     vmax for ``bg_img``. # resampling_interpolation resampling_interpolation : :obj:`str`, optional     Interpolation to use when resampling the image to     the destination space. Can be:          - "continuous": use 3rd-order spline interpolation         - "nearest": use nearest-neighbor mapping.              .. note::                  "nearest" is faster but can be noisier in some cases. # cmap cmap : :class:`matplotlib.colors.Colormap`, or :obj:`str`, optional     The colormap to use. Either a string which is a name of     a matplotlib colormap, or a matplotlib colormap object. # Dimming factor dim : :obj:`float`, or 'auto', optional     Dimming factor applied to background image. By default, automatic     heuristics are applied based upon the background image intensity.     Accepted float values, where a typical span is between -2 and 2     (-2 = increase contrast; 2 = decrease contrast), but larger values     can be used for a more pronounced effect. 0 means no dimming. # avg_method avg_method : {'mean', 'median', 'min', 'max', custom function}, optional     How to average vertex values to derive the face value:          - ``mean``: results in smooth boundaries         - ``median``: results in sharp boundaries         - ``min`` or ``max``: for sparse matrices         - ``custom function``: You can also pass a custom function           which will be executed though :func:`numpy.apply_along_axis`.           Here is an example of a custom function:              .. code-block:: python                  def custom_function(vertices):                     return vertices[0] * vertices[1] * vertices[2] # hemi hemi : {'left', 'right'}, optional     Hemisphere to display. Default='left'. # hemispheres hemispheres : list of :obj:`str`, optional     Hemispheres to display. Default=['left', 'right']. # view view : {'lateral', 'medial', 'dorsal', 'ventral',\         'anterior', 'posterior'}, optional     View of the surface that is rendered.     Default='lateral'. # bg_on_data bg_on_data : :obj:`bool`, optional     If ``True``, and a ``bg_map`` is specified,     the ``surf_data`` data is multiplied by the background     image, so that e.g. sulcal depth is visible beneath     the ``surf_data``.          .. note::             This non-uniformly changes the surf_data values according             to e.g the sulcal depth. # darkness darkness : :obj:`float` between 0 and 1, optional     Specifying the darkness of the background image:          - '1' indicates that the original values of the background are used         - '.5' indicates that the background values are reduced by half           before being applied. # linewidth linewidths : :obj:`float`, optional     Set the boundary thickness of the contours.     Only reflects when ``view_type=contours``. # fsaverage options - 'fsaverage3': the low-resolution fsaverage3 mesh (642 nodes)         - 'fsaverage4': the low-resolution fsaverage4 mesh (2562 nodes)         - 'fsaverage5': the low-resolution fsaverage5 mesh (10242 nodes)         - 'fsaverage5_sphere': the low-resolution fsaverage5 spheres              .. deprecated:: 0.8.0                 This option has been deprecated and will be removed in v0.9.0.                 fsaverage5 sphere coordinates can now be accessed through                 attributes sphere_{left, right} using mesh='fsaverage5'          - 'fsaverage6': the medium-resolution fsaverage6 mesh (40962 nodes)         - 'fsaverage7': same as 'fsaverage'         - 'fsaverage': the high-resolution fsaverage mesh (163842 nodes)              .. note::                 The high-resolution fsaverage will result in more computation                 time and memory usage Minimum indent for all lines in line list.      >>> lines = [' one', '  two', '   three']     >>> _indentcount_lines(lines)     1     >>> lines = []     >>> _indentcount_lines(lines)     0     >>> lines = [' one']     >>> _indentcount_lines(lines)     1     >>> _indentcount_lines(['    '])     0 Fill a docstring with docdict entries.      Parameters     ----------     f : callable         The function to fill the docstring of. Will be modified in place.      Returns     -------     f : callable         The function, potentially with an updated ``__doc__``. # Find the minimum indent of the main docstring, after first line # Insert this indent to dictionary docstrings