/**
 * 
 */
package plugins.kernel.roi.descriptor.measure;

import icy.math.UnitUtil.UnitPrefix;
import icy.roi.ROI;
import icy.roi.ROI3D;
import icy.roi.ROIDescriptor;
import icy.sequence.Sequence;
import icy.sequence.SequenceEvent;
import icy.sequence.SequenceEvent.SequenceEventSourceType;
import icy.util.StringUtil;

/**
 * Volume ROI descriptor class (see {@link ROIDescriptor})
 * 
 * @author Stephane
 */
public class ROIVolumeDescriptor extends ROIDescriptor
{
    public static final String ID = "Volume";

    public ROIVolumeDescriptor()
    {
        super(ID, "Volume", Double.class);
    }

    @Override
    public String getDescription()
    {
        return "Volume";
    }

    @Override
    public String getUnit(Sequence sequence)
    {
        if (sequence != null)
            return sequence.getBestPixelSizeUnit(3, 3).toString() + "m3";

        return UnitPrefix.MICRO.toString() + "m3";
    }

    @Override
    public boolean needRecompute(SequenceEvent change)
    {
        final SequenceEventSourceType sourceType = change.getSourceType();

        if (sourceType == SequenceEventSourceType.SEQUENCE_DATA)
            return true;
        if (sourceType == SequenceEventSourceType.SEQUENCE_META)
        {
            final String metaName = (String) change.getSource();

            return StringUtil.isEmpty(metaName) || StringUtil.equals(metaName, Sequence.ID_PIXEL_SIZE_X)
                    || StringUtil.equals(metaName, Sequence.ID_PIXEL_SIZE_Y)
                    || StringUtil.equals(metaName, Sequence.ID_PIXEL_SIZE_Z);
        }

        return false;
    }

    @Override
    public Object compute(ROI roi, Sequence sequence) throws UnsupportedOperationException
    {
        return Double.valueOf(computeVolume(roi, sequence));
    }

    /**
     * Computes and returns the volume expressed in the unit of the descriptor (see {@link #getUnit(Sequence)}) for the
     * specified ROI.<br>
     * It may thrown an <code>UnsupportedOperationException</code> if the operation is not supported for that ROI.
     * 
     * @param roi
     *        the ROI on which we want to compute the volume
     * @param sequence
     *        an optional sequence where the pixel size can be retrieved
     * @return the volume expressed in the unit of the descriptor (see {@link #getUnit(Sequence)})
     * @throws UnsupportedOperationException
     *         if the operation is not supported for this ROI
     */
    public static double computeVolume(ROI roi, Sequence sequence) throws UnsupportedOperationException
    {
        return computeVolume(ROIInteriorDescriptor.computeInterior(roi), roi, sequence);
    }

    /**
     * Computes and returns the volume from a given number of interior points expressed in the
     * unit of the descriptor (see {@link #getUnit(Sequence)}) for the specified sequence and ROI.<br>
     * It may returns <code>Double.Nan</code> if the operation is not supported for that ROI.
     * 
     * @param interiorPoints
     *        the number of interior points (override the ROI value)
     * @param roi
     *        the ROI we want to compute the volume
     * @param sequence
     *        the input sequence used to retrieve operation unit by using pixel size
     *        information.
     * @return the volume expressed in the unit of the descriptor (see {@link #getUnit(Sequence)})
     * @throws UnsupportedOperationException
     *         if the operation is not supported for this ROI
     */
    public static double computeVolume(double interiorPoints, ROI roi, Sequence sequence)
            throws UnsupportedOperationException
    {
        try
        {
            // we restrict to ROI3D only
            if (!(roi instanceof ROI3D))
                throw new UnsupportedOperationException();

            return ROIInteriorDescriptor.computeInterior(interiorPoints, roi, sequence, 3);
        }
        catch (UnsupportedOperationException e)
        {
            throw new UnsupportedOperationException("Can't process " + ID + " calculation for ROI: '" + roi.getName()
                    + "'");
        }
    }
}