# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.

"""
Morphology computation module
-----------------------------

This module provides morphological operations for image processing, such as
dilation, erosion, opening, and closing. These operations are useful for
removing noise, filling gaps, and extracting structures in binary and grayscale
images.
"""

# pylint: disable=invalid-name  # Allows short reference names like x, y, ...

# Note:
# ----
# - All `guidata.dataset.DataSet` parameter classes must also be imported
#   in the `sigima.params` module.
# - All functions decorated by `computation_function` must be imported in the upper
#   level `sigima.proc.image` module.

from __future__ import annotations

import guidata.dataset as gds
from skimage import morphology

from sigima.config import _
from sigima.objects.image import ImageObj
from sigima.proc.decorator import computation_function
from sigima.proc.image.base import dst_1_to_1, restore_data_outside_roi

# NOTE: Only parameter classes DEFINED in this module should be included in __all__.
# Parameter classes imported from other modules (like sigima.proc.base) should NOT
# be re-exported to avoid Sphinx cross-reference conflicts. The sigima.params module
# serves as the central API point that imports and re-exports all parameter classes.
__all__ = [
    "MorphologyParam",
    "black_tophat",
    "closing",
    "dilation",
    "erosion",
    "opening",
    "white_tophat",
]


class MorphologyParam(gds.DataSet):
    """White Top-Hat parameters"""

    radius = gds.IntItem(
        _("Radius"), default=1, min=1, help=_("Footprint (disk) radius.")
    )


@computation_function()
def white_tophat(src: ImageObj, p: MorphologyParam) -> ImageObj:
    """Compute White Top-Hat with :py:func:`skimage.morphology.white_tophat`

    Args:
        src: input image object
        p: parameters

    Returns:
        Output image object
    """
    dst = dst_1_to_1(src, "white_tophat", f"radius={p.radius}")
    dst.data = morphology.white_tophat(src.data, morphology.disk(p.radius))
    restore_data_outside_roi(dst, src)
    return dst


@computation_function()
def black_tophat(src: ImageObj, p: MorphologyParam) -> ImageObj:
    """Compute Black Top-Hat with :py:func:`skimage.morphology.black_tophat`

    Args:
        src: input image object
        p: parameters

    Returns:
        Output image object
    """
    dst = dst_1_to_1(src, "black_tophat", f"radius={p.radius}")
    dst.data = morphology.black_tophat(src.data, morphology.disk(p.radius))
    restore_data_outside_roi(dst, src)
    return dst


@computation_function()
def erosion(src: ImageObj, p: MorphologyParam) -> ImageObj:
    """Compute Erosion with :py:func:`skimage.morphology.erosion`

    Args:
        src: input image object
        p: parameters

    Returns:
        Output image object
    """
    dst = dst_1_to_1(src, "erosion", f"radius={p.radius}")
    dst.data = morphology.erosion(src.data, morphology.disk(p.radius))
    restore_data_outside_roi(dst, src)
    return dst


@computation_function()
def dilation(src: ImageObj, p: MorphologyParam) -> ImageObj:
    """Compute Dilation with :py:func:`skimage.morphology.dilation`

    Args:
        src: input image object
        p: parameters

    Returns:
        Output image object
    """
    dst = dst_1_to_1(src, "dilation", f"radius={p.radius}")
    dst.data = morphology.dilation(src.data, morphology.disk(p.radius))
    restore_data_outside_roi(dst, src)
    return dst


@computation_function()
def opening(src: ImageObj, p: MorphologyParam) -> ImageObj:
    """Compute morphological opening with :py:func:`skimage.morphology.opening`

    Args:
        src: input image object
        p: parameters

    Returns:
        Output image object
    """
    dst = dst_1_to_1(src, "opening", f"radius={p.radius}")
    dst.data = morphology.opening(src.data, morphology.disk(p.radius))
    restore_data_outside_roi(dst, src)
    return dst


@computation_function()
def closing(src: ImageObj, p: MorphologyParam) -> ImageObj:
    """Compute morphological closing with :py:func:`skimage.morphology.closing`

    Args:
        src: input image object
        p: parameters

    Returns:
        Output image object
    """
    dst = dst_1_to_1(src, "closing", f"radius={p.radius}")
    dst.data = morphology.closing(src.data, morphology.disk(p.radius))
    restore_data_outside_roi(dst, src)
    return dst
