/*******************************************************************************
 *  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.IDicomValue;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDictionaryReader;
import cdac.medinfo.sdk.dcm30_04.commoninterface.ITransferSyntax;
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;

import cdac.medinfo.sdk.dcm30_04.enums.EnumGradientOutputType;

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

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

	/**
	 * This constructor creates the item and internal code sequence macro with default TransferSyntax 
	 */
	MRTimingAndRelatedParametersSequenceItem()
	{
		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
	 */
	MRTimingAndRelatedParametersSequenceItem(ITransferSyntax objITransferSyntax)
	{
		super(objITransferSyntax);
	}

	/**
	 * This constructor creates MRTimingAndRelatedParametersSequenceItem with specified TransferSyntax and
	 * specified DictionaryReader
	 * @param objTransferSyntax specifies the DICOM Standard defined TransfeSyntax
	 * @param objDictionaryReader specifies the DICOM Dictionary reader to be used 
	 */
	MRTimingAndRelatedParametersSequenceItem(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
		{
			/*
			 * Since this class is implemented while implementing MACRO, Validations required for modules
			 * is not cleared that is modules using this item is not known now.
			 *  Validation conditions for attributes of these Item is not cleared.  
			 *  Refer p.d.f Chapter 3
			 *  Page No :- 603
			 */
			
		}
		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;
	}

	/**
	 * The time in ms between two successive excitations of the same volume. Shall be 0 (zero) if there is a single excitation per
	 * volume.
	 * @param strRepetitionTime String value of Source Axis Distance.
	 */
	public void setRepetitionTime(String strRepetitionTime)
	{
		/*Accept the RepetitionTime in String format of type DS.
		 * 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(0x0018,0x0080,strRepetitionTime,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0018,0x0080,DicomConstants.DCM_DT_DS, strRepetitionTime);

		addDataElement(objIDataElement);
	}

	/**
	 * Retrieves The time in ms between two successive excitations of the same volume. Shall be 0 (zero) if there is a single excitation per
	 * volume.
	 * @return String - value of Source Axis Distance.
	 */
	public String getRepetitionTime()
	{
		Object objValue = getValue(0x0018,0x0080);
		if(objValue != null)
			return objValue.toString();
		return null;
	}
	
	/**
	 * Sets Steady state angle in degrees to which the magnetic vector is flipped from the magnetic vector of the primary field.
	 * @param strFlipAngle String value of Source Axis Distance.
	 */
	public void setFlipAngle(String strFlipAngle)
	{
		/*Accept the FlipAngle in String format of type DS.
		 * 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(0x0018,0x1314,strFlipAngle,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0018,0x1314,DicomConstants.DCM_DT_DS, strFlipAngle);

		addDataElement(objIDataElement);
	}

	/**
	 * Retrieves Steady state angle in degrees to which the magnetic vector is flipped from the magnetic vector of the primary field.
	 * @return String - value of Source Axis Distance.
	 */
	public String getFlipAngle()
	{
		Object objValue = getValue(0x0018,0x1314);
		if(objValue != null)
			return objValue.toString();
		return null;
	}
	
	/**
	 * Sets Number of lines in k-space acquired per excitation of the same volume regardless of the type of echo or the number of
	 * frames derived from them.
	 * @param strEchoTrainLength String value of Application Setup Number.
	 */
	public void setEchoTrainLength(String strEchoTrainLength)
	{
		/*Accept the EchoTrainLength in String format of type IS.
		 * 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(0x0018,0x0091, strEchoTrainLength,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0018,0x0091,DicomConstants.DCM_DT_IS, strEchoTrainLength);

		addDataElement(objIDataElement);
	}
	/**
	 * Retrieves Number of lines in k-space acquired per excitation of the same volume regardless of the type of echo or the number of
	 * frames derived from them.
	 * @return String - value of EchoTrainLength.
	 */
	public String strEchoTrainLength()
	{
		Object objValue = getValue(0x0018,0x0091);
		if(objValue != null)
			return objValue.toString();
		return null;
	}
	
	/**
	 * Sets Number of RF echoes collected per RF shot (or excitation) per frame. 
	 * @param shRFEchoTrainLength short value of RF Echo Train Length.
	 */
	public void setRFEchoTrainLength(short shRFEchoTrainLength)
	{
		/* Accept the RF Echo Train Length 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(0x0018,0x9240,shRFEchoTrainLength,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0018,0x9240,DicomConstants.DCM_DT_US, shRFEchoTrainLength);

		addDataElement(objIDataElement);
	}

	/**
	 * Retrieves Number of RF echoes collected per RF shot (or excitation) per frame. 
	 * @return short - value of RF Echo Train Length.
	 */
	public short getRFEchoTrainLength()
	{
		Object objValue = getValue(0x0018,0x9240);
		if(objValue != null)
			return Short.parseShort(objValue.toString());
		return DicomConstants.DCM_NO_VALUE;
	}
	
	/**
	 * Sets The position of the annotation box in the parent film box. 
	 * Required if the Annotation Content Sequence is sent.
	 * @param shGradientEchoTrainLength short value of Gradient Echo Train Length.
	 */
	public void setGradientEchoTrainLength(short shGradientEchoTrainLength)
	{
		/* Accept the Gradient Echo Train Length 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(0x0018,0x9241,shGradientEchoTrainLength,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0018,0x9241,DicomConstants.DCM_DT_US, shGradientEchoTrainLength);

		addDataElement(objIDataElement);
	}

	/**
	 * Retrieves The position of the annotation box in the parent film box. 
	 * @return short - value of Gradient Echo Train Length.
	 */
	public short getGradientEchoTrainLength()
	{
		Object objValue = getValue(0x0018,0x9241);
		if(objValue != null)
			return Short.parseShort(objValue.toString());
		return DicomConstants.DCM_NO_VALUE;
	}
	
	/**
	 * Sets Definition of gradient output unit, for which the value is stored in Gradient Output (0018,9182).
	 * @param enumGradientOutputType Enum value for geometric interpretation of the Contour Data
	 */
	public void setGradientOutputType(EnumGradientOutputType enumGradientOutputType)
	{
		/*Accept the Gradient Output Type 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(0x0018,0x9180, enumGradientOutputType.getValue(),getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0018,0x9180,DicomConstants.DCM_DT_CS, enumGradientOutputType.getValue());

		addDataElement(objIDataElement);
	}
	/**
	 * Retrieves Definition of gradient output unit, for which the value is stored in Gradient Output (0018,9182).
	 * @return String - value for geometric interpretation of the Contour Data
	 */
	public String getGradientOutputType()
	{
		Object objValue = getValue(0x0018,0x9180);
		if(objValue != null)
			return objValue.toString();
		return null;
	}
	
	/**
	 * Sets Unit is defined by Gradient Output Type (0018,9180).
	 * @param dblGradientOutput String array of coordinates
	 */
	public void setGradientOutput(double dblGradientOutput)
	{
		/*Accept the Gradient Output in double format of type FD.
		 * 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(0x0018,0x9182, dblGradientOutput,getDictionaryReader());
		else
			objIDataElement = new ExplicitDE(0x0018,0x9182,DicomConstants.DCM_DT_FD, dblGradientOutput);

		addDataElement(objIDataElement);
	}
	/**
	 * Retrieves Unit is defined by Gradient Output Type (0018,9180).
	 * @return double - double value of coordinates
	 */
	public double getGradientOutput()
	{
		Object objValue = getValue(0x0018,0x9182);
		if(objValue != null)
			return Double.parseDouble(objValue.toString());
		return DicomConstants.DCM_NO_VALUE;
	}
	
	/**
	 * Retrieves pre-initialized object of Specific Absorption Rate Sequence.If the module contains the sequence object
	 * it returns the same otherwise it creates the sequence adds to the module with proper initialization
	 * and then returns the object of sequence.
	 * @return SpecificAbsorptionRateSequence - instance of Specific Absorption Rate Sequence. 
	 */
	public SpecificAbsorptionRateSequence getSpecificAbsorptionRateSequence()
	{
		SpecificAbsorptionRateSequence objSpecificAbsorptionRateSequence = null;
		IDataElement objIDataElement =  getDataForTag(0x0018,0x9239);
		if(objIDataElement != null)
		{
			IDicomValue objIDicomValue = objIDataElement.getDicomValue();
			if(objIDicomValue != null)
				objSpecificAbsorptionRateSequence = (SpecificAbsorptionRateSequence)objIDicomValue;
		}
		else
		{
			DicomV3Factory objDicomV3Factory = DicomV3Factory.createInstance();
			int iVRTransferSyntax = objITransferSyntax.getVRTransferSyntax();
			objIDataElement = null;
			objSpecificAbsorptionRateSequence = (SpecificAbsorptionRateSequence)objDicomV3Factory.createSequence(0x0018,0x9239, objITransferSyntax, objIDictionaryReader);

			if(iVRTransferSyntax == DicomConstants.DCM_VR_IMPLICIT)
				objIDataElement = new ImplicitDE(0x0018,0x9239, objSpecificAbsorptionRateSequence.getValue(),objIDictionaryReader);
			else
				objIDataElement = new ExplicitDE(0x0018,0x9239, DicomConstants.DCM_DT_SQ, objSpecificAbsorptionRateSequence.getValue());
			objIDataElement.setTransferSyntax(objITransferSyntax);
			if(objIDataElement != null)
				addDataElement(objIDataElement);
		}
		return objSpecificAbsorptionRateSequence;
	}
	
	/**
	 * Retrieves pre-initialized object of Operating Mode Sequence.If the module contains the sequence object
	 * it returns the same otherwise it creates the sequence adds to the module with proper initialization
	 * and then returns the object of sequence.
	 * @return OperatingModeSequence - instance of Operating Mode Sequence. 
	 */
	public OperatingModeSequence getOperatingModeSequence()
	{
		OperatingModeSequence objOperatingModeSequence = null;
		IDataElement objIDataElement =  getDataForTag(0x0018,0x9176);
		if(objIDataElement != null)
		{
			IDicomValue objIDicomValue = objIDataElement.getDicomValue();
			if(objIDicomValue != null)
				objOperatingModeSequence = (OperatingModeSequence)objIDicomValue;
		}
		else
		{
			DicomV3Factory objDicomV3Factory = DicomV3Factory.createInstance();
			int iVRTransferSyntax = objITransferSyntax.getVRTransferSyntax();
			objIDataElement = null;
			objOperatingModeSequence = (OperatingModeSequence)objDicomV3Factory.createSequence(0x0018,0x9176, objITransferSyntax, objIDictionaryReader);

			if(iVRTransferSyntax == DicomConstants.DCM_VR_IMPLICIT)
				objIDataElement = new ImplicitDE(0x0018,0x9176, objOperatingModeSequence.getValue(),objIDictionaryReader);
			else
				objIDataElement = new ExplicitDE(0x0018,0x9176, DicomConstants.DCM_DT_SQ, objOperatingModeSequence.getValue());
			objIDataElement.setTransferSyntax(objITransferSyntax);
			if(objIDataElement != null)
				addDataElement(objIDataElement);
		}
		return objOperatingModeSequence;
	}
}
