import numpy as np

from dipy.denoise.denspeed import nlmeans_3d
from dipy.testing.decorators import warning_for_keywords

# from warnings import warn
# import warnings

# warnings.simplefilter('always', DeprecationWarning)
# warn(DeprecationWarning("Module 'dipy.denoise.nlmeans' is deprecated,"
#                         " use module 'dipy.denoise.non_local_means' instead"))


@warning_for_keywords()
def nlmeans(
    arr,
    sigma,
    *,
    mask=None,
    patch_radius=1,
    block_radius=5,
    rician=True,
    num_threads=None,
):
    r"""Non-local means for denoising 3D and 4D images.

    See :footcite:p:Descoteaux2008a` for further details about the method.

    Parameters
    ----------
    arr : 3D or 4D ndarray
        The array to be denoised
    mask : 3D ndarray
    sigma : float or 3D array
        standard deviation of the noise estimated from the data
    patch_radius : int
        patch size is ``2 x patch_radius + 1``. Default is 1.
    block_radius : int
        block size is ``2 x block_radius + 1``. Default is 5.
    rician : boolean
        If True the noise is estimated as Rician, otherwise Gaussian noise
        is assumed.
    num_threads : int, optional
        Number of threads to be used for OpenMP parallelization. If None
        (default) the value of OMP_NUM_THREADS environment variable is used
        if it is set, otherwise all available threads are used. If < 0 the
        maximal number of threads minus $|num_threads + 1|$ is used (enter -1 to
        use as many threads as possible). 0 raises an error.

    Returns
    -------
    denoised_arr : ndarray
        the denoised ``arr`` which has the same shape as ``arr``.

    References
    ----------
    .. footbibliography::

    """

    # warn(DeprecationWarning("function 'dipy.denoise.nlmeans'"
    #                        " is deprecated, use module "
    #                        "'dipy.denoise.non_local_means'"
    #                        " instead"))

    if arr.ndim == 3:
        sigma = np.ones(arr.shape, dtype=np.float64) * sigma
        return nlmeans_3d(
            arr,
            mask=mask,
            sigma=sigma,
            patch_radius=patch_radius,
            block_radius=block_radius,
            rician=rician,
            num_threads=num_threads,
        ).astype(arr.dtype)

    elif arr.ndim == 4:
        denoised_arr = np.zeros_like(arr)

        if isinstance(sigma, np.ndarray) and sigma.ndim == 3:
            sigma = np.ones(arr.shape, dtype=np.float64) * sigma[..., np.newaxis]
        else:
            sigma = np.ones(arr.shape, dtype=np.float64) * sigma

        for i in range(arr.shape[-1]):
            denoised_arr[..., i] = nlmeans_3d(
                arr[..., i],
                mask=mask,
                sigma=sigma[..., i],
                patch_radius=patch_radius,
                block_radius=block_radius,
                rician=rician,
                num_threads=num_threads,
            ).astype(arr.dtype)

        return denoised_arr

    else:
        raise ValueError("Only 3D or 4D array are supported!", arr.shape)
