"""
This is a demonstration file to explain how to
use an image filter.

This is an implementation of the Roberts' cross operator, from the scikit-image package.

#. Import a dataset;
#. Go to *Tools/Image Processing Toolbox* to start the Image Processing;
#. In the combo box of operations, go to the section *Demos* to select *DemoImageFilterRoberts*;
#. Press the button *Compute Selected Preview* to execute the filter on the dataset.

:author: ORS Team
:contact: http://theobjects.com
:email: info@theobjects.com
:organization: Object Research Systems (ORS), Inc.
:address: 760 St-Paul West, suite 101, Montréal, Québec, Canada, H3C 1M4
:copyright: Object Research Systems (ORS), Inc. All rights reserved 2020.
:date: Sep 29 2017 15:41
:dragonflyVersion: 3.1.0.307 (D)
:UUID: 2047c500a54e11e797f9448a5b5d70c0
"""

__version__ = '1.0.0'

from skimage.filters import roberts

from ORSModel import orsObj
from COMWrapper.ORS_def import CxvChannel_Data_Type
from OrsPythonPlugins.OrsSimpleFilters.filters.filterabstract import FilterAbstract


class DemoImageFilterRoberts_2047c500a54e11e797f9448a5b5d70c0(FilterAbstract):

    @classmethod
    def getFilterName(cls):
        """
        Method called to get the full name of the filter.
        This name should be unique among all filters.
        :return: string
            Full name of the filter
        """
        return 'DemoImageFilterRoberts'
    
    @classmethod
    def getAbbreviatedOutputName(cls, outputIndex):
        """
        Method called to get an abbreviated name for an output.
        By default, returns the full name of the filter with the index.
        :return: string
            Abbreviated name of the filter
        """
        if outputIndex == 0:
            return 'DemoFiltRob'
        else:
            return 'DemoFiltRob/' + str(outputIndex)
    
    @classmethod
    def getFilterCategory(cls):
        """
        Method called to get a category for the filter.
        :return: string
            Category of the filter (ex: Smoothing, Edge detection, ...)
        """
        return 'Demo'
    
    @classmethod
    def getFilterInfo(cls):
        """
        Method called to get an information string about the filter.
        This string is displayed in the UI.
        This string could serve as a general description or to display a warning.
        :return: str
        """
        return 'This is a demonstration filter.'
    
    @classmethod
    def getInputCount(cls):
        """
        Method called to know how many inputs (datasets) are required.
        :return: int
        """
        return 1
    
    @classmethod
    def getOutputCount(cls):
        """
        Method called to know how many outputs (datasets) are required.
        :return: int
        """
        return 1

    # The methods "apply" and "_apply" are NOT overloaded,
    # so the methods "applyOnPatch" and "patchJob" are the ones that will be called
    # when the filter is executed.
    # This is done so that multithreading will be active.

    @classmethod
    def applyOnPatch(cls, dataInputTSlice, dataOutputTSlice, isOnFullSpatialChannel, dictBooleanArguments,
                     dictNumericArguments, dictStringArguments, progressId):
        IProgress = orsObj(progressId)
        
        extra_keywords = {'progressId': progressId,
                          'resultDType': dataOutputTSlice.dtype}
        
        result = cls._apply_parallel(cls.patchJob, dataInputTSlice, isOnFullSpatialChannel=isOnFullSpatialChannel,
                                     extra_keywords=extra_keywords)
        if IProgress is not None and IProgress.getIsCancelled():
            return None
        if isOnFullSpatialChannel:
            dataOutputTSlice[:] = result[:]
            return None
        else:
            return result
    
    @classmethod
    def patchJob(cls, dataInputTSlice, progressId, resultDType):
        zI = dataInputTSlice.shape[0]
        result = dataInputTSlice.astype(resultDType)  # Making a copy (for the shape), with the output type
        IProgress = orsObj(progressId)
        for zindex in range(0, zI):
            if IProgress is not None and IProgress.getIsCancelled():
                return dataInputTSlice.copy()
            result[zindex, ...] = roberts(dataInputTSlice[zindex])  # Calling the Roberts filter on the image
        return result

    @classmethod
    def getSuggestedOutputDataType(cls, listInputChannelId, outputChannelIndex):
        IChannelInput = orsObj(listInputChannelId[0])
        iDataTypeInput = IChannelInput.getDataType()

        # The roberts filter produces floating-point result, often (always?) between 0 and 1.
        # Therefore, the suggested output data type is float.
        # Using an integer data type will result in casting these values to 0.
        return CxvChannel_Data_Type.CXVCHANNEL_DATA_TYPE_FLOAT

    @classmethod
    def getLengthDependenceX(cls, inputChannelIndex, numpyKernel, dictBooleanArguments, dictNumericArguments, dictStringArguments):
        """
        Method to ask the filter for the extent required (in X) to perform computations on a subset of the dataset
        :param inputChannelIndex: int
            Channel index for which the size dependence is requested
        :return: int
            Largest number of pixels required in X from the pixel of computation (either side).
            If all the pixels are required, return -1.
        """
        return 1  # The filter requires only the immediate pixels of the given pixel to be computed
    
    @classmethod
    def getLengthDependenceY(cls, inputChannelIndex, numpyKernel, dictBooleanArguments, dictNumericArguments, dictStringArguments):
        """
        Method to ask the filter for the extent required (in Y) to perform computations on a subset of the dataset
        :param inputChannelIndex: int
            Channel index for which the size dependence is requested
        :return: int
            Largest number of pixels required in Y from the pixel of computation (either side).
            If all the pixels are required, return -1.
        """
        return 1  # The filter requires only the immediate pixels of the given pixel to be computed

    @classmethod
    def getLengthDependenceZ(cls, inputChannelIndex, numpyKernel, dictBooleanArguments, dictNumericArguments,
                             dictStringArguments):
        return 0  # 2D filter. Adjacent slices are not required to compute the output of the filter at a given pixel.
    
    @classmethod
    def getShowKernelShape(cls):
        """
        :return: bool
            Return True if the kernel shape (square, circle, ...) should be visible in the UI, False otherwise.
        """

        return False
    
    @classmethod
    def getShowKernelDim(cls):
        """
        :return: bool
            Return True if the kernel dimensionality (2D, 3D) should be visible in the UI, False otherwise.
        """

        return False
    
    @classmethod
    def getShowKernelSize(cls):
        """
        :return: bool
            Return True if the kernel size (3, 5, 7, ...) should be visible in the UI, False otherwise.
        """

        return False

    @classmethod
    def getListAvailableKernelSize(cls):
        """
        :return: list
            List of the available kernel sizes
        """

        return [3]  # The only kernel size is to cover the current pixel and the immediate neighbors
