# Test python camitk ImageComponent
import numpy as np
import camitk

def print_numpy_array_info(msg, image_data, threshold):
    # number of voxels below threshold but over 0
    count = np.sum((image_data < threshold) & (image_data > image_data.min()))
    # Mask values in range [0, threshold)
    masked = image_data[(image_data > image_data.min()) & (image_data < threshold)]
    # Get the number of unique values between 0 and threshold
    num_unique = np.unique(masked).size
    # full info
    camitk.info(msg + ":\n- size: " +str(image_data.shape) 
        + "\n- dim:" + str(image_data.ndim) + " of type " + str(type(image_data.ndim)) 
        + "\n- values: [" + str(image_data.min()) + ".." + str(image_data.max()) 
        + "]\n- nb of voxels < " + str(threshold) + ": " + str(count) + " voxels with " + str(num_unique) + " different values")

def print_parameters(self):
    value = self.getParameterValue("My Int Value")
    camitk.info("My Int Value: " + str(type(value)) + " = " + str(value))
    
    value = self.getParameterValue("My Enum")
    enumValues = self.getProperty("My Enum").getAttribute("enumNames")
    camitk.info("My Enum: " + str(type(value)) + " = '" + enumValues[value] + "' (" + str(value) + ")")

    value = self.getParameterValue("My Boolean")
    camitk.info("My Boolean: " + str(type(value)) + " = " + str(value))

    value = self.getParameterValue("My QString")
    camitk.info("My QString: " + str(type(value)) + " = " + str(value))

    value = self.getParameterValue("My Double")
    camitk.info("My Double: " + str(type(value)) + " = " + str(value))

def init(self:camitk.Action):
    camitk.info("CamiTK version: " + camitk.__version__)
    camitk.info("Opened components: " + str(len(camitk.Application.getTopLevelComponents())))
    camitk.Application.open(camitk.Core.getTestDataDir() + "/amos_0336/amos_0336.mhd")
    camitk.info("Opened components: " + str(len(camitk.Application.getTopLevelComponents())))
    for c in camitk.Application.getTopLevelComponents():
        print(c.getName()) # print will print to CamiTK console without timestamp, level and location in code

def process(self:camitk.Action):
    camitk.info("From Python action: " + str(self.getName()))
    camitk.info("Opened components: " + str(len(camitk.Application.getTopLevelComponents())))
    print_parameters(self)
    camitk.info("Nb of targets: " + str(len(self.getTargets())))

    targets = self.getTargets()
    lastTarget = targets[-1]
    msg = "Last target:" + str(lastTarget.getName())
    camitk.info(msg)

    if isinstance(lastTarget, camitk.Component) and not isinstance(lastTarget, camitk.ImageComponent):
        camitk.warning("lastTarget is instance of Component, but dynamic cast not working\n" + str(lastTarget.__class__)) 
        return False

    image_component = lastTarget
    camitk.info("After python cast: " + str(type(image_component)))

    image_data = image_component.getImageDataAsNumpy()
    
    # use numpy to threshold (set values that are below the threshold to 0)
    threshold = self.getParameterValue("My Int Value")

    print_numpy_array_info("Initial data", image_data, threshold)
    image_data[image_data<threshold] = image_data.min()
    camitk.info("Image data is: " + str(type(image_data)))
    camitk.info("Spacing: " + str(image_component.getSpacing()))
    print_numpy_array_info("Threshold data", image_data, threshold)

    # Create image from data with the same spacing and frame
    new_image_component = camitk.newImageComponentFromNumpy(image_data, "Image From Python", image_component.getSpacing())
    # other ways to call
    #camitk.newImageComponentFromNumpy(image_data) # default name="image", spacing=(1,1,1)
    #camitk.newImageComponentFromNumpy(image_data, spacing=image_data_info["spacing"]) # default name="image", spacing=(1,1,1)
    #camitk.newImageComponentFromNumpy(image_data, "label_map", spacing=(0.5, 0.5, 1.0))
    #camitk.newImageComponentFromNumpy(image_data, spacing=(0.5, 0.5, 1.0), name="threshold image")
    new_image_component.setFrameFrom(image_component)

    # Test replace_image_data
    # image_component.replace_image_data(image_data)

    camitk.info("New image:" + str(new_image_component.getName()) + " "  + str(type(new_image_component)))

    # check new image
    new_image_data = new_image_component.getImageDataAsNumpy()
    print_numpy_array_info("Threshold data in new image component", new_image_data, threshold)
    # number of voxels below threshold but over 0
    count = np.sum((new_image_data < threshold) & (new_image_data > image_data.min()))
    # Mask values in range [0, threshold)
    masked = new_image_data[(new_image_data > image_data.min()) & (new_image_data < threshold)]
    # Get the number of unique values between 0 and threshold
    num_unique = np.unique(masked).size
    if count > 0 or num_unique > 0:
        return False
    
    self.refreshApplication() # similar to what would be done in C++
    # or camitk.refresh()

    return True

def targetDefined(self:camitk.Action):
    targets = self.getTargets()    
    camitk.info("New target: " + targets[0].getName())

def parameterChanged(self:camitk.Action, name:str):
    print_parameters(self)
    camitk.info("Parameter " + name + " changed, new value: " + str(self.getParameterValue(name)))