# Copyright (c) MONAI Consortium
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#     http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Callable, Sequence

from monai.apps.deepgrow.transforms import (
    AddGuidanceFromPointsd,
    AddGuidanceSignald,
    Fetch2DSliced,
    ResizeGuidanced,
    RestoreLabeld,
    SpatialCropGuidanced,
)
from monai.inferers import Inferer, SimpleInferer
from monai.transforms import (
    Activationsd,
    AddChanneld,
    AsChannelFirstd,
    AsChannelLastd,
    AsDiscreted,
    EnsureTyped,
    LoadImaged,
    NormalizeIntensityd,
    Resized,
    Spacingd,
    ToNumpyd,
)

from monailabel.interfaces.tasks.infer_v2 import InferType
from monailabel.tasks.infer.basic_infer import BasicInferTask


class Deepgrow(BasicInferTask):
    """
    This provides Inference Engine for Deepgrow 2D/3D pre-trained model.
    For More Details, Refer https://github.com/Project-MONAI/tutorials/tree/master/deepgrow/ignite
    """

    def __init__(
        self,
        path,
        network=None,
        type=InferType.DEEPGROW,
        labels=None,
        dimension=2,
        description="A pre-trained DeepGrow model based on UNET",
        spatial_size=(256, 256),
        model_size=(256, 256),
        **kwargs,
    ):
        super().__init__(
            path=path,
            network=network,
            type=type,
            labels=labels,
            dimension=dimension,
            description=description,
            load_strict=False,
            **kwargs,
        )

        self.spatial_size = spatial_size
        self.model_size = model_size

    def pre_transforms(self, data=None) -> Sequence[Callable]:
        t = [
            LoadImaged(keys="image"),
            AsChannelFirstd(keys="image"),
            Spacingd(keys="image", pixdim=[1.0] * self.dimension, mode="bilinear"),
        ]

        self.add_cache_transform(t, data)
        t.append(AddGuidanceFromPointsd(ref_image="image", guidance="guidance", spatial_dims=self.dimension))

        if self.dimension == 2:
            t.append(Fetch2DSliced(keys="image", guidance="guidance"))
        t.extend(
            [
                AddChanneld(keys="image"),
                SpatialCropGuidanced(keys="image", guidance="guidance", spatial_size=self.spatial_size),
                Resized(keys="image", spatial_size=self.model_size, mode="area"),
                ResizeGuidanced(guidance="guidance", ref_image="image"),
                NormalizeIntensityd(keys="image", subtrahend=208, divisor=388),  # type: ignore
                AddGuidanceSignald(image="image", guidance="guidance"),
                EnsureTyped(keys="image", device=data.get("device") if data else None),
            ]
        )
        return t

    def inferer(self, data=None) -> Inferer:
        return SimpleInferer()

    def post_transforms(self, data=None) -> Sequence[Callable]:
        return [
            EnsureTyped(keys="pred", device=data.get("device") if data else None),
            Activationsd(keys="pred", sigmoid=True),
            AsDiscreted(keys="pred", threshold=0.5),
            ToNumpyd(keys="pred"),
            RestoreLabeld(keys="pred", ref_image="image", mode="nearest"),
            AsChannelLastd(keys="pred"),
        ]
