/*******************************************************************************
 *  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.dicommodel.modules;



import cdac.medinfo.sdk.dcm30_04.baselibs.CodeSequenceMacro;
import cdac.medinfo.sdk.dcm30_04.baselibs.DicomV3Factory;
import cdac.medinfo.sdk.dcm30_04.baselibs.MultiplexedAudioChannelsDescriptionCodeSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.Tag;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataSet;
import cdac.medinfo.sdk.dcm30_04.commoninterface.ITag;

import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidMacroException;
import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidModuleException;
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.utils.ISingleCollection;
import cdac.medinfo.sdk.dcm30_04.utils.SingleCollection;

// TODO: Auto-generated Javadoc
/**
 \* This module specifies the Attributes of a Multi-frame Cine Image.
 */
public class CineModule extends Module  
{


	/**
	 * The obj single collection.
	 */
	public static ISingleCollection<ITag> objSingleCollection = null;


	/**
	 * Instantiates a new cine module.
	 * 
	 * @param objIDataSet the obj i data set
	 */
	public CineModule(IDataSet objIDataSet)
	{
		super(objIDataSet);
	}

	/**
	 * The obj code sequence macro.
	 */
	CodeSequenceMacro objCodeSequenceMacro = null;

	static
	{
		objSingleCollection = new SingleCollection<ITag>();
		objSingleCollection.add(new Tag(0x0008,0x2142));
		objSingleCollection.add(new Tag(0x0008,0x2143));
		objSingleCollection.add(new Tag(0x0008,0x2144));
		objSingleCollection.add(new Tag(0x0018,0x0040));
		objSingleCollection.add(new Tag(0x0018,0x0072));
		objSingleCollection.add(new Tag(0x0018,0x1063));
		objSingleCollection.add(new Tag(0x0018,0x1065));
		objSingleCollection.add(new Tag(0x0018,0x1066));
		objSingleCollection.add(new Tag(0x0018,0x1242));
		objSingleCollection.add(new Tag(0x0018,0x1244));		
		objSingleCollection.add(new Tag(0x003A,0x0300));
	}

	/**
	 * This method validates each Attribute in the Module according to the Type
	 * of the Attribute.Attributes are of 5 types.
	 * Type 1,Type 2 are Mandatory:
	 * Mandatory Attributes have to present in the Module.
	 * Type 1C,2C are Conditional:
	 * Conditional Attributes' presence depend on a condition.
	 * If the condition is satisfied the Attribute has to be present.
	 * Type 3 are Optional
	 * Optional Attributes can be added as and when required.
	 * 
	 * @return true, if validate module
	 * 
	 * @throws InvalidModuleException - If validation of Module fails
	 */
	public boolean validateModule() throws InvalidModuleException 
	{
		//Conditon for (0018,1063), (0018,1065) not understood. Pg. No. 286 PDF-3.
		//Condition for (003A,0300) not understood. Pg. No.287 PDF-3
		//validate the Macro

		try
		{
			getCodeSequenceMacro().validate();
		}
		catch(InvalidMacroException ve)
		{
			if(DicomConfig.createInstance().isLoggingOn())
				DicomLogger.getLogger().log(ve.getMessage());

			
				throw new InvalidModuleException(this.getClass().getSimpleName(),ve);

		}
		return true;
	}

	/**
	 * Sets the preferred playback sequencing for a multi-frame image.
	 * Enumerated Values:
	 * 0 = Looping (1,2...n,1,2,...n,1,2,....n,...)
	 * 1 = Sweeping (1,2,...n,n-1,...2,1,2,...n,...).
	 * 
	 * @param sPreferredPlaybackSequencing short value of Preferred Playback Sequencing.
	 */
	public void setPreferredPlaybackSequencing(short sPreferredPlaybackSequencing)
	{
		/*Accept Preferred Playback Sequencing 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 objet 
		 * Set the iVRType
		 * Set length from the element
		 * Add this element to the Data Set
		 */
		addDataElement(0x0018,0x1244, DicomConstants.DCM_DT_US, sPreferredPlaybackSequencing);
	}

	/**
	 * Sets Nominal time (in msec) per individual frame.Required if Frame Increment
	 * Pointer (0028,0009) points to Frame Time.
	 * 
	 * @param strFrameTime String value of Frame Time.
	 */
	public void setFrameTime(String strFrameTime)
	{
		/*Accept Frame Time 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
		 */
		addDataElement(0x0018, 0x1063, DicomConstants.DCM_DT_CS, strFrameTime);
	}


	/**
	 * Sets an array that contains the real time increments (in msec) between frames for
	 * a Multi-frame image. Required if Frame Increment Pointer (0028,0009) points to
	 * Frame Time Vector.
	 * 
	 * @param arrStrFrameTimeVector String array of Frame Time values.
	 */
	public void setFrameTimeVector(String[] arrStrFrameTimeVector)
	{
		/*Accept Frame Time Vector in Array of 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
		 */
		addDataElement(0x0018,0x1065, DicomConstants.DCM_DT_DS, arrStrFrameTimeVector);
	}

	/**
	 * Sets the frame number of the first frame of the Multi-frame image to be displayed.
	 * 
	 * @param strStartTrim String value of Start Trim.
	 */
	public void setStartTrim(String strStartTrim)
	{
		/* Accept Start Trim of 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
		 */
		addDataElement(0x0008,0x2142, DicomConstants.DCM_DT_DS, strStartTrim);
	}

	/**
	 * Sets the Frame Number of the last frame of a Multi-frame image to be displayed.
	 * 
	 * @param strStopTrim String value of Stop Trim.
	 */
	public void setStopTrim(String strStopTrim)
	{
		/* Accept Stop Trim of 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
		 */
		addDataElement(0x0008,0x2143, DicomConstants.DCM_DT_IS, strStopTrim);
	}

	/**
	 * Sets recommended rate at which the frames of a Multi-frame image should be displayed in frames/second.
	 * 
	 * @param strRecommendedDisplayFrameRate String value of Recommended Display Frame Rate.
	 */
	public void setRecommendedDisplayFrameRate(String strRecommendedDisplayFrameRate)
	{
		/* Accept Recommended Display Frame Rate of 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
		 */
		addDataElement(0x0008,0x2144, DicomConstants.DCM_DT_IS, strRecommendedDisplayFrameRate);
	}

	/**
	 * Sets number of frames per second.
	 * 
	 * @param strCineRate String value of Cine Rate.
	 */
	public void setCineRate(String strCineRate)
	{
		/* Accept Cine Rate of 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
		 */
		addDataElement(0x00018,0x0040, DicomConstants.DCM_DT_IS, strCineRate);
	}

	/**
	 * Sets time (in msec) from Content Time (0008,0033) to the start of the first frame
	 * in a Multi-frame image.
	 * 
	 * @param strFrameDelay String value of Frame Delay.
	 */
	public void setFrameDelay(String strFrameDelay)
	{
		/* Accept Frame Delay of 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
		 */
		addDataElement(0x00018,0x1066, DicomConstants.DCM_DT_DS, strFrameDelay);
	}

	/**
	 * Sets delay time in milliseconds from trigger (e.g., X-ray on pulse) to the first frame of
	 * a Multi-frame image.
	 * 
	 * @param strImageTriggerDelay String value of Image Trigger Delay.
	 */
	public void setImageTriggerDelay(String strImageTriggerDelay)
	{
		/* Accept Image Trigger Delay of 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
		 */
		addDataElement(0x00018,0x1067, DicomConstants.DCM_DT_DS, strImageTriggerDelay);
	}

	/**
	 * Sets total time in seconds that data was actually taken for the entire Multi-frame image.
	 * 
	 * @param strEffectiveDuration String value of Effective Duration.
	 */
	public void setEffectiveDuration(String strEffectiveDuration)
	{
		/* Accept Effective Duration of 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
		 */
		addDataElement(0x00018,0x0072, DicomConstants.DCM_DT_DS, strEffectiveDuration);
	}

	/**
	 * Sets elapsed time of data acquisition in msec per each frame.
	 * 
	 * @param strActualFrameDuration String value of Actual Frame Duration.
	 */
	public void setActualFrameDuration(String strActualFrameDuration)
	{
		/* Accept Actual Frame Duration of 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
		 */
		addDataElement(0x00018,0x1242, DicomConstants.DCM_DT_IS, strActualFrameDuration);
	}

	/**
	 * Retrieves the preferred playback sequencing for a multi-frame image.
	 * Enumerated Values:
	 * 0 = Looping (1,2...n,1,2,...n,1,2,....n,...)
	 * 1 = Sweeping (1,2,...n,n-1,...2,1,2,...n,...)
	 * 
	 * @return short - short value of Preferred Playback Sequencing.
	 */
	public short getPreferredPlaybackSequencing()
	{
		Object objValue = getValue(0x0018,0x1244);
		if(objValue != null)
			return Short.parseShort(objValue.toString());
		return DicomConstants.DCM_NO_VALUE;
	}

	/**
	 * Retrieves Nominal time (in msec) per individual frame.
	 * 
	 * @return String - String value of Frame Time.
	 */ 
	public String getFrameTime()
	{
		Object objValue = getValue(0x0018,0x1063);
		if(objValue != null)
			return objValue.toString();
		return null;
	}

	/**
	 * Retrieves An array that contains the real time increments (in msec) between frames for
	 * a Multi-frame image.
	 * 
	 * @return String[] - String array of Frame Time values.
	 */
	public String[] getFrameTimeVector()
	{
		Object objValue = getValue(0x0018,0x1065);
		if(objValue != null)
			return (String[])objValue;
		return null;
	}

	/**
	 * Retrieves the frame number of the first frame of the Multi-frame image to be displayed.
	 * 
	 * @return String - String value of Start Trim.
	 */
	public String getStartTrim()
	{
		Object objValue = getValue(0x0008,0x2142);
		if(objValue != null)
			return objValue.toString();
		return null;
	}

	/**
	 * Retrieves the Frame Number of the last frame of a Multi-frame image to be displayed.
	 * 
	 * @return String - String value of Stop Trim.
	 */
	public String getStopTrim()
	{
		Object objValue = getValue(0x0008,0x2143);
		if(objValue != null)
			return objValue.toString();
		return null;
	}

	/**
	 * Retrieves recommended rate at which the frames of a Multi-frame image should be displayed in frames/second.
	 * 
	 * @return String - String value of Recommended Display Frame Rate.
	 */
	public String getRecommendedDisplayFrameRate()
	{
		Object objValue = getValue(0x0008,0x2144);
		if(objValue != null)
			return objValue.toString();
		return null;
	}

	/**
	 * Retrieves Number of frames per second.
	 * 
	 * @return String - String value of  Cine Rate.
	 */
	public String getCineRate()
	{
		Object objValue = getValue(0x0018,0x0040);
		if(objValue != null)
			return objValue.toString();
		return null;
	}

	/**
	 * Retrieves Time (in msec) from Content Time (0008,0033) to the start of the first frame
	 * in a Multi-frame image.
	 * 
	 * @return String - String value of  Frame Delay.
	 */
	public String getFrameDelay()
	{
		Object objValue = getValue(0x0018,0x1066);
		if(objValue != null)
			return objValue.toString();
		return null;
	}

	/**
	 * Retrieves Delay time in milliseconds from trigger (e.g., X-ray on pulse) to the first frame of
	 * a Multi-frame image.
	 * 
	 * @return String - String value of Image Trigger Delay.
	 */
	public String getImageTriggerDelay()
	{
		Object objValue = getValue(0x0018,0x1067);
		if(objValue != null)
			return objValue.toString();
		return null;
	}

	/**
	 * Retrieves Total time in seconds that data was actually taken for the entire Multi-frame image.
	 * 
	 * @return String - String value of Effective Duration.
	 */
	public String getEffectiveDuration()
	{
		Object objValue = getValue(0x0018,0x0072);
		if(objValue != null)
			return objValue.toString();
		return null;
	}

	/**
	 * Retrieves Elapsed time of data acquisition in msec per each frame.
	 * 
	 * @return String - String value of Actual Frame Duration.
	 */
	public String getActualFrameDuration()
	{
		Object objValue = getValue(0x0018,0x1242);
		if(objValue != null)
			return objValue.toString();
		return null;
	}

	/**
	 * Provides the pre-initialized instance of Multiplexed Audio Channels Description Code 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 - instance of Multiplexed Audio Channels Description Code Sequence.
	 */
	public MultiplexedAudioChannelsDescriptionCodeSequence getMultiplexedAudioChannelsDescriptionCodeSequence()
	{
		MultiplexedAudioChannelsDescriptionCodeSequence objMultiplexedAudioChannelsDescriptionCodeSequence = (MultiplexedAudioChannelsDescriptionCodeSequence)getDicomValue(0x003A, 0x0300);
		if(objMultiplexedAudioChannelsDescriptionCodeSequence == null)
		{
			/*
			 * If the sequence object is not present in the DataSet, a new sequence object is created and
			 * the TransferSyntax and DictionaryReader is set into it as it is set in the Module. This ensures that 
			 * the DataElements getting added in the DataSet follow the same TransferSyntax and use the same
			 * DictionaryReader object as that of the module.
			 */
			DicomV3Factory objDicomV3Factory = DicomV3Factory.createInstance();
			objMultiplexedAudioChannelsDescriptionCodeSequence = (MultiplexedAudioChannelsDescriptionCodeSequence)objDicomV3Factory.createSequence(0x003A, 0x0300, objITransferSyntax, objIDictionaryReader);
			
			// This will add Data Element of SQ to the DataSet.
			addDataElement(0x003A, 0x0300, DicomConstants.DCM_DT_SQ, objMultiplexedAudioChannelsDescriptionCodeSequence.getValue());
		}
		return objMultiplexedAudioChannelsDescriptionCodeSequence;
	}

	/**
	 * Get the Instance of Code Sequence Macro.Attributes related to Code Sequence Macro will be set in Macro
	 * using the setter methods provided in the Macro.Calling this method multiple times will return the same Marco
	 * Instance that was created at first. Hence, any attributes in this Macro,if set multiple times will contain the value that was
	 * set in the last call to its setter method.
	 * 
	 * @return - object of code sequence macro which the user programmer can populate
	 */
	public CodeSequenceMacro getCodeSequenceMacro()
	{
		if(objCodeSequenceMacro == null)
			objCodeSequenceMacro = new CodeSequenceMacro(objIDataSet);
		return objCodeSequenceMacro; 
	}

	/**
	 * This method returns the collection of keys representing the DataElements that belong to this module.
	 * 
	 * @return Collection of Tags - returns the collection containing the tags
	 */
	public ISingleCollection<ITag> getVectorOfKeys()
	{
		return objSingleCollection;
	}


}
