/*******************************************************************************
 *  Copyright 2017-2018 Centre for Development of Advanced Computing(C-DAC), Pune
 *  
 * 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.
 *******************************************************************************/
package cdac.medinfo.sdk.dcm30_04.baselibs;



import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataElement;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDictionaryReader;
import cdac.medinfo.sdk.dcm30_04.commoninterface.ITransferSyntax;
import cdac.medinfo.sdk.dcm30_04.enums.EnumMaskOperation;
import cdac.medinfo.sdk.dcm30_04.enums.EnumMaskSelectionMode;
import cdac.medinfo.sdk.dcm30_04.exceptions.ConditionFailedException;
import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidItemException;
import cdac.medinfo.sdk.dcm30_04.exceptions.MandatoryMissingException;
import cdac.medinfo.sdk.dcm30_04.exceptions.ValidationException;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConfig;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConstants;
import cdac.medinfo.sdk.dcm30_04.utils.DicomLogger;




/**
* This item class contains methods to set,get and validate attributes of Mask Subtraction Sequence.
 */
public class MaskSubtractionSequenceItem extends Item  
{
	

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * This constructor creates the item and internal code sequence macro with default TransferSyntax 
	 */
	MaskSubtractionSequenceItem()
	{
		super();
	}

	/*
	 * This constructor calls the respective superclass constructor
	 * This constructor should throw exception if null TS is tried to be set
	 */
	/**
	 * This constructor creates the item and internal code sequence macro with default TransferSyntax 
	 * and default DictionaryReader
	 * @param objITransferSyntax specifies the DICOM Standard defined TransfeSyntax
	 */
	MaskSubtractionSequenceItem(ITransferSyntax objITransferSyntax)
	{
		super(objITransferSyntax);
	}

	/**
	 * This constructor creates MaskSubtractionSequenceItem with specified TransferSyntax and
	 * specified DictionaryReader
	 * @param objTransferSyntax specifies the DICOM Standard defined TransfeSyntax
	 * @param objDictionaryReader specifies the DICOM Dictionary reader to be used 
	 */
	MaskSubtractionSequenceItem(ITransferSyntax objTransferSyntax, IDictionaryReader objDictionaryReader)
	{
		super(objTransferSyntax,objDictionaryReader);
	}

	/*
	 * (non-Javadoc) This mathod validates the contents of the Item
	 * @see cdac.medinfo.sdk.dcm30_04.commoninterface.IItem#validate()
	 */
	/**
	 * Validates all the attributes in Mask Subtraction Sequence Item that are present in the DataSet
	 * @return boolean - Success -true, Failure - false
	 * @throws InvalidItemException throws InvalidItemException If Mandatory or Conditional element fails.
	 */
	public boolean validateForModule()  throws InvalidItemException
	{
		try{
			if(!contains(0x0028,0x6101))
				throw new MandatoryMissingException("0028,6101");
			else
			{
				String strValue = getMaskOperation();
				if(strValue != null)
				{
					if(strValue.equalsIgnoreCase(EnumMaskOperation.AVERAGE_SUBTRACTION.getValue()) && (!contains(0x0028,0x6110)))
						throw new ConditionFailedException("value for conditional Element 0x0028,0x6110 required if value of Mask Operation 0x0028,0x6101 is AVG_SUB");
					if(strValue.equalsIgnoreCase(EnumMaskOperation.TIME_INTERVAL_DIFFERENCING.getValue()) && (!contains(0x0028,0x6120)))
						throw new ConditionFailedException("value for conditional Element 0x0028,0x6120 required if value of Mask Operation 0x0028,0x6101 is TID");
				}
			}
			
			if(contains(0x0028,0x6101) && getMaskOperation().equalsIgnoreCase(EnumMaskOperation.REVERSE_TIME_INTERVAL_DIFFERENCING.getValue()) )
					if(!contains(0x0028,0x6102))
						throw new ConditionFailedException("0x0028,0x6102");
		}
		catch(ConditionFailedException ce)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger.getLogger().log(ce.getMessage());
			}
			throw new ValidationException(this.getClass().getSimpleName(),ce); 
		}
		catch( MandatoryMissingException me)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger.getLogger().log(me.getMessage());
			}
			throw new ValidationException(this.getClass().getSimpleName(),me);
		}
		return true;
	}

	/**
	 * Sets the type of mask operation to be performed.
	 * @param enumEnumMaskOperation Specifies Enum value of Mask Operation
	 */
	public void setMaskOperation(EnumMaskOperation enumEnumMaskOperation)
	{
		/*Accept the Mask Operation in String format of type CS.
		 * Read the Transfer Syntax and check if ImplicitDE or ExplicitDE to create
		 * 
		 * Set the iGroupNumber and iElementNumber to ImplicitDE object 
		 * Set the iVRType
		 * Set length from the element
		 * Add this element to the Data Set
		 */
		IDataElement objIDataElement = null;
		if(objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_IMPLICIT)
			objIDataElement = new ImplicitDE(0x0028,0x6101, enumEnumMaskOperation.getValue(),getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0028,0x6101,DicomConstants.DCM_DT_CS, enumEnumMaskOperation.getValue());

		addDataElement(objIDataElement);
	}

	/**
	 * Return type of mask operation to be performed.
	 * @return String -  Specifies String value of Mask Operation
	 */
	public String getMaskOperation()
	{
		Object objValue = getValue(0x0028,0x6101);
		if(objValue != null)
			return objValue.toString();
		return null;
	}

	/**
	 * Sets Identification of the Subtraction Item used to associate a certain Mask Sub-PixelShift (0028,6114)
	 * @param shSubtractionItemID short value of Number of significant bits
	 */
	public void setSubtractionItemID(short shSubtractionItemID)
	{
		/* Accept the WaveformBitsStored of type US.
		 * Identification of the Subtraction Item used to associate a certain Mask Sub-PixelShift (0028,6114).
		 * Add this element to the Data Set
		 */
		IDataElement objIDataElement = null;
		if(objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_IMPLICIT)	
			objIDataElement = new ImplicitDE(0x0028,0x9416,shSubtractionItemID,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0028,0x9416,DicomConstants.DCM_DT_US, shSubtractionItemID);

		addDataElement(objIDataElement);
	}

	/**
	 * Sets Identification of the Subtraction Item used to associate a certain Mask Sub-PixelShift (0028,6114)
	 * @param shSubtractionItemID short value of Number of significant bits
	 */
	public short getSubtractionItemID()
	{
		Object objValue = getValue(0x0028,0x9416);
		if(objValue != null)
			return Short.parseShort(objValue.toString());
		return DicomConstants.DCM_NO_VALUE;
	}
	
	/**
	 *  Sets Applicable Frame Range.
	 *  Each pair of numbers in this multivalued attribute specify a beginning and ending frame number inclusive of a
	 *  range where this particular mask operation is valid. Discontinuous ranges are represented by multiple
	 *  pairs of numbers. Frames in a Multiframe Image are specified by sequentially increasing number values
	 *  beginning with 1. If this Attribute is missing in this particular sequence item, then the mask operation is
	 *  applicable throughout the entire Multiframe Image, subject to certain limits
	 * @param arrShMaskOperation short array of frame numbers 
	 */
	public void setApplicableFrameRange(short[] arrShMaskOperation)
	{
		/*Accept the Applicable Frame Range in short Array format of type US.
		 * Read the Transfer Syntax and check if ImplicitDE or ExplicitDE to create
		 * Set the iGroupNumber and iElementNumber to ImplicitDE object 
		 * Set the iVRType
		 * Set length from the element
		 * Add this element to the Data Set
		 */
		IDataElement objIDataElement = null;
		if(objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_IMPLICIT)
			objIDataElement = new ImplicitDE(0x0028,0x6102, arrShMaskOperation,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0028,0x6102,DicomConstants.DCM_DT_US, arrShMaskOperation);

		addDataElement(objIDataElement);
	}

	/**
	 * Return Applicable Frame Range.
	 * @return short [] - short array of Applicable Frames.
	 */
	public short[] getApplicableFrameRange()
	{
		Object objValue = getValue(0x0028,0x6102);
		if(objValue != null)
			return (short[])objValue;
		return null;
	}

	/**
	 * Sets the frame numbers of the pixel data used to generate this mask. Frames in a Multi-frame Image are
	 * specified by sequentially increasing number values beginning with 1. Required if the Mask Operation
	 * (0028,6101) is AVG_SUB.
	 * @param arrMaskFrameNumbers short array of Mask Frame Numbers
	 */
	public void setMaskFrameNumbers(short[] arrMaskFrameNumbers)
	{
		/*Accept the Mask Frame Numbers in short Array format of type US.
		 * Read the Transfer Syntax and check if ImplicitDE or ExplicitDE to create
		 * Set the iGroupNumber and iElementNumber to ImplicitDE object 
		 * Set the iVRType
		 * Set length from the element
		 * Add this element to the Data Set
		 */
		IDataElement objIDataElement = null;
		if(objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_IMPLICIT) 
			objIDataElement = new ImplicitDE(0x0028,0x6110, arrMaskFrameNumbers,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0028,0x6110,DicomConstants.DCM_DT_US, arrMaskFrameNumbers);

		addDataElement(objIDataElement);
	}

	/**
	 * Return frame numbers of the pixel data used to generate this mask.
	 * @return short [] - short array of Mask Frame Numbers.
	 */
	public short[] getMaskFrameNumbers()
	{
		Object objValue = getValue(0x0028,0x6110);
		if(objValue != null)
			return (short[])objValue;
		return null;
	}

	/**
	 * Sets the number of contrast frames to average together before performing the mask operation. 
	 * If the Attribute is missing, no averaging is performed.
	 * @param shContrastFrameAveraging short value of Contrast Frame Averaging
	 */
	public void setContrastFrameAveraging(short shContrastFrameAveraging)
	{
		/*Accept the Contrast Frame Averaging in short format of type US.
		 * Read the Transfer Syntax and check if ImplicitDE or ExplicitDE to create
		 * Set the iGroupNumber and iElementNumber to ImplicitDE object 
		 * Set the iVRType
		 * Set length from the element
		 * Add this element to the Data Set
		 */
		IDataElement objIDataElement = null;
		if(objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_IMPLICIT)
			objIDataElement = new ImplicitDE(0x0028,0x6112, shContrastFrameAveraging,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0028,0x6112,DicomConstants.DCM_DT_US, shContrastFrameAveraging);

		addDataElement(objIDataElement);
	}

	/**
	 * Return number of contrast frames to average together before performing the mask operation..
	 * @return short - short value of Contrast Frame Averaging
	 */
	public short getContrastFrameAveraging()
	{
		Object objValue = getValue(0x0028,0x6112);
		if(objValue != null)
			return Short.parseShort(objValue.toString());
		return DicomConstants.DCM_NO_VALUE;
	}

	/**
	 * Sets a pair of floating point numbers specifying the fractional vertical [adjacent row spacing] 
	 * and horizontal [adjacent column spacing] pixel shift applied to the mask before subtracting
	 * it from the contrast frame.
	 * @param arrFMaskSubPixelShift pair of floating point numbers
	 */
	public void setMaskSubPixelShift(float[] arrFMaskSubPixelShift)
	{
		/* Accept the Mask Sub-pixel Shift in short format of type FL.
		 * Read the Transfer Syntax and check if ImplicitDE or ExplicitDE to create
		 * Set the iGroupNumber and iElementNumber to ImplicitDE object 
		 * Set the iVRType
		 * Set length from the element
		 * Add this element to the Data Set
		 */
		IDataElement objIDataElement = null;
		if(objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_IMPLICIT)
			objIDataElement = new ImplicitDE(0x0028,0x6114, arrFMaskSubPixelShift,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0028,0x6114,DicomConstants.DCM_DT_FL, arrFMaskSubPixelShift);

		addDataElement(objIDataElement);
	}

	/**
	 * Retrieves Mask Sub Pixel Shift.
	 * @return float[] - Float array of Mask Sub Pixel Shift.
	 */
	public float[] getMaskSubPixelShift()
	{
		Object objValue = getValue(0x0028,0x6114);
		if(objValue != null)
			return (float[])objValue;
		return null;
	}

	/**
	 * Sets the offset to be subtracted from the current frame number in order to locate the mask frame in TID mode.
	 * If zero length, TID Offset defaults to 1. Required if Mask Operation (0028,6101) is TID.
	 * @param shTIDOffset short value of TID Offset.
	 */
	public void setTIDOffset(short shTIDOffset)
	{
		/* Accept the TID Offset in short format of type short.
		 * Read the Transfer Syntax and check if ImplicitDE or ExplicitDE to create
		 * Set the iGroupNumber and iElementNumber to ImplicitDE object 
		 * Set the iVRType
		 * Set length from the element
		 * Add this element to the Data Set
		 */
		IDataElement objIDataElement = null;
		if(objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_IMPLICIT)
			objIDataElement = new ImplicitDE(0x0028,0x6120, shTIDOffset,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0028,0x6120,DicomConstants.DCM_DT_US, shTIDOffset);

		addDataElement(objIDataElement);
	}

	/**
	 * Return offset to be subtracted from the current frame number in order to locate the mask frame in TID mode.
	 * @return short - short value of TID Offset
	 */
	public short getTIDOffset()
	{
		Object objValue = getValue(0x0028,0x6120);
		if(objValue != null)
			return Short.parseShort(objValue.toString());
		return DicomConstants.DCM_NO_VALUE;
	}

	/**
	 * Sets Free form explanation of this particular mask operation.
	 * @param strMaskOperationExplanation String value of Mask Operation Explanation
	 */
	public void setMaskOperationExplanation(String strMaskOperationExplanation)
	{
		/* Accept the Mask Operation Explanation in String format of type ST.
		 * Read the Transfer Syntax and check if ImplicitDE or ExplicitDE to create
		 * Set the iGroupNumber and iElementNumber to ImplicitDE object 
		 * Set the iVRType
		 * Set length from the element
		 * Add this element to the Data Set
		 */
		IDataElement objIDataElement = null;
		if(objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_IMPLICIT)
			objIDataElement = new ImplicitDE(0x0028,0x6190, strMaskOperationExplanation,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0028,0x6190,DicomConstants.DCM_DT_ST, strMaskOperationExplanation);

		addDataElement(objIDataElement);
	}

	/**
	 * Return Free form explanation of this particular mask operation.
	 * @return String -  Specifies String value of Mask Operation Explanation
	 */
	public String getMaskOperationExplanation()
	{
		Object objValue = getValue(0x0028,0x6190);
		if(objValue != null)
			return objValue.toString();
		return null;
	}
	
	/**
	 * Sets the Specifies the method of selection of the mask operations of this item.
	 * Enumerated Values:
	 * SYSTEM	
	 * USER	
	 * @param enumMaskSelectionMode Specifies type of the value encoded in this Item
	 */
	public void setMaskSelectionMode(EnumMaskSelectionMode enumMaskSelectionMode)
	{
		/* Accept the Value Type in String format of type CS.
		 * Sets the Specifies the method of selection of the mask operations of this item.
		 * Add this element to the Data Set
		 */
		IDataElement objIDataElement = null;
		if(objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_IMPLICIT)
			objIDataElement = new ImplicitDE(0x0028, 0x9454, enumMaskSelectionMode,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0028, 0x9454,DicomConstants.DCM_DT_CS, enumMaskSelectionMode);

		addDataElement(objIDataElement);
	}
	/**
	 * Retrieves type of the MaskSelectionMode encoded in this Item
	 * @return String - Type of the Mask Selection Mode encoded in this Item
	 */
	public String getMaskSelectionMode()
	{
		Object objValue = getValue(0x0028, 0x9454);
		if(objValue != null)
			return objValue.toString();
		return null;
	}


}
