package plugins.kernel.roi.descriptor.measure;

import icy.plugin.abstract_.Plugin;
import icy.plugin.interface_.PluginROIDescriptor;
import icy.roi.ROI;
import icy.roi.ROIDescriptor;
import icy.sequence.Sequence;
import icy.type.rectangle.Rectangle5D;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * This {@link PluginROIDescriptor} implements the following basic measures ROI descriptors:<br/>
 * <li>Contour (in pixel)</li><br/>
 * <li>Interior (in pixel)</li><br/>
 * <li>Perimeter (pixel size unit - 2D ROI only)</li><br/>
 * <li>Surface Area (pixel size unit - 3D ROI only)</li><br/>
 * <li>Area (pixel size unit - 2D ROI only)</li><br/>
 * <li>Volume (pixel size unit - 3D ROI only)</li><br/>
 * 
 * @author Stephane
 */
public class ROIBasicMeasureDescriptorsPlugin extends Plugin implements PluginROIDescriptor
{
    public static final String ID_CONTOUR = ROIContourDescriptor.ID;
    public static final String ID_INTERIOR = ROIInteriorDescriptor.ID;
    public static final String ID_PERIMETER = ROIPerimeterDescriptor.ID;
    public static final String ID_AREA = ROIAreaDescriptor.ID;
    public static final String ID_SURFACE_AREA = ROISurfaceAreaDescriptor.ID;
    public static final String ID_VOLUME = ROIVolumeDescriptor.ID;

    public static final ROIContourDescriptor contourDescriptor = new ROIContourDescriptor();
    public static final ROIInteriorDescriptor interiorDescriptor = new ROIInteriorDescriptor();
    public static final ROIPerimeterDescriptor perimeterDescriptor = new ROIPerimeterDescriptor();
    public static final ROIAreaDescriptor areaDescriptor = new ROIAreaDescriptor();
    public static final ROISurfaceAreaDescriptor surfaceAreaDescriptor = new ROISurfaceAreaDescriptor();
    public static final ROIVolumeDescriptor volumeDescriptor = new ROIVolumeDescriptor();

    /**
     * Calculate the multiplier factor depending the wanted dimension information.
     */
    public static double getMultiplierFactor(Sequence sequence, ROI roi, int dim)
    {
        final int dimRoi = roi.getDimension();

        // cannot give this information for this roi
        if (dimRoi > dim)
            return 0d;

        final Rectangle5D boundsRoi = roi.getBounds5D();
        double mul = 1d;

        switch (dim)
        {
            case 5:
                if (dimRoi == 4)
                {
                    final int sizeC = sequence.getSizeC();

                    if ((boundsRoi.getSizeC() == Double.POSITIVE_INFINITY) && (sizeC > 1))
                        mul *= sizeC;
                    // cannot give this information for this roi
                    else
                        mul = 0d;
                }
            case 4:
                if (dimRoi == 3)
                {
                    final int sizeT = sequence.getSizeT();

                    if ((boundsRoi.getSizeT() == Double.POSITIVE_INFINITY) && (sizeT > 1))
                        mul *= sizeT;
                    // cannot give this information for this roi
                    else
                        mul = 0d;
                }
            case 3:
                if (dimRoi == 2)
                {
                    final int sizeZ = sequence.getSizeZ();

                    if ((boundsRoi.getSizeZ() == Double.POSITIVE_INFINITY) && (sizeZ > 1))
                        mul *= sizeZ;
                    // cannot give this information for this roi
                    else
                        mul = 0d;
                }
            case 2:
                if (dimRoi == 1)
                {
                    final int sizeY = sequence.getSizeY();

                    if ((boundsRoi.getSizeY() == Double.POSITIVE_INFINITY) && (sizeY > 1))
                        mul *= sizeY;
                    // cannot give this information for this roi
                    else
                        mul = 0d;
                }
        }

        return mul;
    }

    @Override
    public List<ROIDescriptor> getDescriptors()
    {
        final List<ROIDescriptor> result = new ArrayList<ROIDescriptor>();

        result.add(contourDescriptor);
        result.add(interiorDescriptor);
        result.add(perimeterDescriptor);
        result.add(areaDescriptor);
        result.add(surfaceAreaDescriptor);
        result.add(volumeDescriptor);

        return result;
    }

    @Override
    public Map<ROIDescriptor, Object> compute(ROI roi, Sequence sequence) throws UnsupportedOperationException
    {
        final Map<ROIDescriptor, Object> result = new HashMap<ROIDescriptor, Object>();

        // use the contour and interior to compute others descriptors
        final double contour = ROIContourDescriptor.computeContour(roi);
        final double interior = ROIInteriorDescriptor.computeInterior(roi);

        result.put(contourDescriptor, Double.valueOf(contour));
        result.put(interiorDescriptor, Double.valueOf(interior));

        int notComputed = 0;

        try
        {
            result.put(perimeterDescriptor, Double.valueOf(ROIPerimeterDescriptor.computePerimeter(roi, sequence)));
        }
        catch (UnsupportedOperationException e)
        {
            result.put(perimeterDescriptor, null);
            notComputed++;
        }
        try
        {
            result.put(areaDescriptor, Double.valueOf(ROIAreaDescriptor.computeArea(interior, roi, sequence)));
        }
        catch (UnsupportedOperationException e)
        {
            result.put(areaDescriptor, null);
            notComputed++;
        }
        try
        {
            result.put(surfaceAreaDescriptor,
                    Double.valueOf(ROISurfaceAreaDescriptor.computeSurfaceArea(roi, sequence)));
        }
        catch (UnsupportedOperationException e)
        {
            result.put(surfaceAreaDescriptor, null);
            notComputed++;
        }
        try
        {
            result.put(volumeDescriptor, Double.valueOf(ROIVolumeDescriptor.computeVolume(interior, roi, sequence)));
        }
        catch (UnsupportedOperationException e)
        {
            result.put(volumeDescriptor, null);
            notComputed++;
        }

        if (notComputed == 4)
        {
            throw new UnsupportedOperationException(getClass().getSimpleName()
                    + ": cannot compute any of the descriptors for '" + roi.getName() + "'");
        }

        return result;
    }
}