/*******************************************************************************
 *  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.AcquisitionDeviceTypeCodeSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.ChannelDescriptionCodeSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.DicomV3Factory;
import cdac.medinfo.sdk.dcm30_04.baselibs.IlluminationTypeCodeSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.ImagePathFilterTypeStackCodeSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.LensesCodeSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.LightPathFilterTypeStackCodeSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.Tag;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataSet;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IItem;
import cdac.medinfo.sdk.dcm30_04.commoninterface.ITag;
import cdac.medinfo.sdk.dcm30_04.enums.EnumDetectorType;

import cdac.medinfo.sdk.dcm30_04.exceptions.ConditionFailedException;
import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidModuleException;
import cdac.medinfo.sdk.dcm30_04.exceptions.MandatoryMissingException;
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.ICollectionIterator;
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 that describe an Ophthalmic Photographic Parameters Module
 \* This module describes equipment used to create original images.
 */
public class OphthalmicPhotographicParametersModule extends Module  
{


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


	/**
	 * Instantiates a new ophthalmic photographic parameters module.
	 * 
	 * @param objIDataSet the obj i data set
	 */
	public OphthalmicPhotographicParametersModule(IDataSet objIDataSet)
	{
		super(objIDataSet);
	}

	static
	{
		objSingleCollection = new SingleCollection<ITag>();
		objSingleCollection.add(new Tag(0x0018,0x7004));
		objSingleCollection.add(new Tag(0x0022,0x0001));
		objSingleCollection.add(new Tag(0x0022,0x0002));
		objSingleCollection.add(new Tag(0x0022,0x0003));
		objSingleCollection.add(new Tag(0x0022,0x0004));
		objSingleCollection.add(new Tag(0x0022,0x0015));
		objSingleCollection.add(new Tag(0x0022,0x0016));
		objSingleCollection.add(new Tag(0x0022,0x0017));
		objSingleCollection.add(new Tag(0x0022,0x0018));
		objSingleCollection.add(new Tag(0x0022,0x0019));
		objSingleCollection.add(new Tag(0x0022,0x001A));
		objSingleCollection.add(new Tag(0x0022,0x001E));
	}

	/**
	 * 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 boolean - returns validation status as success or failure
	 * 
	 * @throws InvalidModuleException - If validation of Module fails
	 */	
	public  boolean validateModule() throws InvalidModuleException
	{

		try
		{			/*
		 * Validation Rules for Mandatory Elements
		 */
			if(!isPresent(0x0022,0x0015))
				throw new MandatoryMissingException("0022,0015");
			if(!isPresent(0x0022,0x0016))
				throw new MandatoryMissingException("0022,0016");
			if(!isPresent(0x0022,0x0017))
				throw new MandatoryMissingException("0022,0017");
			if(!isPresent(0x0022,0x0018))
				throw new MandatoryMissingException("0022,0018");
			if(!isPresent(0x0022,0x0019))
				throw new MandatoryMissingException("0022,0019");
			if(!isPresent(0x0018,0x7004))
				throw new MandatoryMissingException("0018,7004");

			if(isPresent(0x0028,0x002A))
			{
				ICollectionIterator<IItem> objChannelDescriptionCodeSequenceIterator = getChannelDescriptionCodeSequence().iterator();
				int iNoOfItem = 0;
				while(objChannelDescriptionCodeSequenceIterator.hasNext())
					iNoOfItem++;
				OphthalmicPhotographyImageModule objOphthalmicPhotographyImageModule = new OphthalmicPhotographyImageModule(objIDataSet);
				if(objOphthalmicPhotographyImageModule.isPresent(0x0028,0x0003))
				{
					short shSamplesPerPixelUsed = objOphthalmicPhotographyImageModule.getSamplesPerPixelUsed();
					if(shSamplesPerPixelUsed != iNoOfItem)
						throw new ConditionFailedException(" (0022,001A)Channel Description Code Sequence Shall have the same number of items as the Value of Samples per Pixel Used (0028,0003) if present");
				}
				else if(objOphthalmicPhotographyImageModule.isPresent(0x0028, 0x0002))
				{
					short shSamplesPerPixel = objOphthalmicPhotographyImageModule.getSamplesPerPixel();
					if(shSamplesPerPixel != iNoOfItem)
						throw new ConditionFailedException(" (0022,001A)Channel Description Code Sequence Shall have the same number of items as the Value of Samples per Pixel (0028,0003) "+
						"if present or otherwise the value of Samples per Pixel (0028,0002).");
				}
				else
					throw new ConditionFailedException(" (0022,001A)Channel Description Code Sequence Shall have the same number of items as the Value of Samples per Pixel (0028,0003) "+
					"if present or otherwise the value of Samples per Pixel (0028,0002).");
			}
		}
		catch(MandatoryMissingException mme)
		{
			if(DicomConfig.createInstance().isLoggingOn())
				DicomLogger.getLogger().log(mme.getMessage());

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

		}
		catch(ConditionFailedException cfe)
		{
			if(DicomConfig.createInstance().isLoggingOn())
				DicomLogger.getLogger().log(cfe.getMessage());

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

		}
		return true;
	}

	/**
	 * This method Returns provides the pre-initialized object of Acquisition Device Type Code Sequence
	 * return - object of Acquisition Device Type Code Sequence. If the module contans 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 SourceImageSequence - the Patient Eye Movement Command Code Sequence
	 */
	public AcquisitionDeviceTypeCodeSequence getAcquisitionDeviceTypeCodeSequence()
	{
		AcquisitionDeviceTypeCodeSequence objAcquisitionDeviceTypeCodeSequence = (AcquisitionDeviceTypeCodeSequence)getDicomValue(0x0022, 0x0015);
		if(objAcquisitionDeviceTypeCodeSequence == 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();
			objAcquisitionDeviceTypeCodeSequence = (AcquisitionDeviceTypeCodeSequence)objDicomV3Factory.createSequence(0x0022, 0x0015, objITransferSyntax, objIDictionaryReader);
			
			// This will add Data Element of SQ to the DataSet.
			addDataElement(0x0022, 0x0015, DicomConstants.DCM_DT_SQ, objAcquisitionDeviceTypeCodeSequence.getValue());
		}
		return objAcquisitionDeviceTypeCodeSequence;
	}

	/**
	 * This method Returns provides the pre-initialized object of Illumination Type Code Sequence
	 * return - object of Illumination Type Code Sequence. If the module contans 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 SourceImageSequence - the Illumination Type Code Sequence
	 */
	public IlluminationTypeCodeSequence getIlluminationTypeCodeSequence()
	{
		IlluminationTypeCodeSequence objIlluminationTypeCodeSequence = (IlluminationTypeCodeSequence)getDicomValue(0x0022, 0x0016);
		if(objIlluminationTypeCodeSequence == 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();
			objIlluminationTypeCodeSequence = (IlluminationTypeCodeSequence)objDicomV3Factory.createSequence(0x0022, 0x0016, objITransferSyntax, objIDictionaryReader);
			
			// This will add Data Element of SQ to the DataSet.
			addDataElement(0x0022, 0x0016, DicomConstants.DCM_DT_SQ, objIlluminationTypeCodeSequence.getValue());
		}
		return objIlluminationTypeCodeSequence;
	}

	/**
	 * This method Returns provides the pre-initialized object of Light Path Filter Type Stack Code Sequence
	 * return - object of Light Path Filter Type Stack Code Sequence. If the module contans 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 SourceImageSequence - the Light Path Filter Type Stack Code Sequence
	 */
	public LightPathFilterTypeStackCodeSequence getLightPathFilterTypeStackCodeSequence()
	{
		LightPathFilterTypeStackCodeSequence objLightPathFilterTypeStackCodeSequence = (LightPathFilterTypeStackCodeSequence)getDicomValue(0x0022, 0x0017);
		if(objLightPathFilterTypeStackCodeSequence == 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();
			objLightPathFilterTypeStackCodeSequence = (LightPathFilterTypeStackCodeSequence)objDicomV3Factory.createSequence(0x0022, 0x0017, objITransferSyntax, objIDictionaryReader);
			
			// This will add Data Element of SQ to the DataSet.
			addDataElement(0x0022, 0x0017, DicomConstants.DCM_DT_SQ, objLightPathFilterTypeStackCodeSequence.getValue());
		}
		return objLightPathFilterTypeStackCodeSequence;
	}

	/**
	 * Sets the value of Nominal pass-through wavelength of light path filter in nm.
	 * 
	 * @param shLightPathFilterPassThroughWavelength the sh light path filter pass through wavelength
	 */	
	public void setLightPathFilterPassThroughWavelength(short shLightPathFilterPassThroughWavelength)
	{
		/*Accept the value of Light Path Filter Pass Through Wavelengthin 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 Hash Map
		 */
		addDataElement(0x0022, 0x0001, DicomConstants.DCM_DT_US, shLightPathFilterPassThroughWavelength);
	}

	/**
	 * Retrieves the value of Nominal pass-through wavelength of light path filter in nm.
	 * 
	 * @return the patient eye movement commanded
	 */
	public short getPatientEyeMovementCommanded()
	{
		Object objValue = getValue(0x0022,0x0001);
		if(objValue != null)
			return Short.parseShort(objValue.toString());
		return DicomConstants.DCM_NO_VALUE;
	}

	/**
	 * Sets the value of Pass band of light path filter in nm.
	 * 
	 * @param shLightPathFilterPassBand - value of Pass band of light path filter in nm.
	 */	
	public void setLightPathFilterPassBand(short[] shLightPathFilterPassBand)
	{
		/*Accept the value of Light Path Filter Pass Band 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 Hash Map
		 */
		addDataElement(0x0022, 0x0002, DicomConstants.DCM_DT_US, shLightPathFilterPassBand);
	}

	/**
	 * Retrieves the value of Pass band of light path filter in nm.
	 * 
	 * @return the light path filter pass band
	 */
	public short[] getLightPathFilterPassBand()
	{
		Object objValue = getValue(0x0022,0x0002);
		if(objValue != null)
			return (short[])objValue;
		return null;
	}

	/**
	 * This method Returns provides the pre-initialized object of Image Path Filter Type Stack Code Sequence
	 * return - object of Image Path Filter Type Stack Code Sequence. If the module contans 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 SourceImageSequence - the Image Path Filter Type Stack Code Sequence
	 */
	public ImagePathFilterTypeStackCodeSequence getImagePathFilterTypeStackCodeSequence()
	{
		ImagePathFilterTypeStackCodeSequence objImagePathFilterTypeStackCodeSequence = (ImagePathFilterTypeStackCodeSequence)getDicomValue(0x0022, 0x0018);
		if(objImagePathFilterTypeStackCodeSequence == 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();
			objImagePathFilterTypeStackCodeSequence = (ImagePathFilterTypeStackCodeSequence)objDicomV3Factory.createSequence(0x0022, 0x0018, objITransferSyntax, objIDictionaryReader);
			
			// This will add Data Element of SQ to the DataSet.
			addDataElement(0x0022, 0x0018, DicomConstants.DCM_DT_SQ, objImagePathFilterTypeStackCodeSequence.getValue());
		}
		return objImagePathFilterTypeStackCodeSequence;
	}

	/**
	 * Sets the value of Nominal pass-through wavelength of image path filter in nm.
	 * 
	 * @param shImagePathFilterPassThroughWavelength the sh image path filter pass through wavelength
	 */	
	public void setImagePathFilterPassThroughWavelength(short shImagePathFilterPassThroughWavelength)
	{
		/*Accept the value of Image Path Filter Pass Through Wavelength 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 Hash Map
		 */
		addDataElement(0x0022, 0x0003, DicomConstants.DCM_DT_US, shImagePathFilterPassThroughWavelength);
	}

	/**
	 * Retrieves the value of Nominal pass-through wavelength of image path filter in nm.
	 * 
	 * @return the image path filter pass through wavelength
	 */
	public short getImagePathFilterPassThroughWavelength()
	{
		Object objValue = getValue(0x0022,0x0003);
		if(objValue != null)
			return Short.parseShort(objValue.toString());
		return DicomConstants.DCM_NO_VALUE;
	}

	/**
	 * Sets the value of Pass band of image path filter in nm.
	 * 
	 * @param shImagePathFilterPassBand the sh image path filter pass band
	 */	
	public void setImagePathFilterPassBand(short[] shImagePathFilterPassBand)
	{
		/*Accept the value of Image Path Filter Pass Band 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 Hash Map
		 */
		addDataElement(0x0022, 0x0003, DicomConstants.DCM_DT_US, shImagePathFilterPassBand);
	}

	/**
	 * Retrieves the value of Pass band of image path filter in nm.
	 * 
	 * @return the image path filter pass band
	 */
	public short[] getImagePathFilterPassBand()
	{
		Object objValue = getValue(0x0022,0x0003);
		if(objValue != null)
			return (short[])objValue;
		return null;
	}

	/**
	 * This method Returns provides the pre-initialized object of Lenses Code Sequence
	 * return - object of Image Lenses Code Sequence. If the module contans 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 SourceImageSequence - the Lenses Code Sequence
	 */
	public LensesCodeSequence getLensesCodeSequence()
	{
		LensesCodeSequence objLensesCodeSequence = (LensesCodeSequence)getDicomValue(0x0022, 0x0019);
		if(objLensesCodeSequence == 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();
			objLensesCodeSequence = (LensesCodeSequence)objDicomV3Factory.createSequence(0x0022, 0x0019, objITransferSyntax, objIDictionaryReader);
			
			// This will add Data Element of SQ to the DataSet.
			addDataElement(0x0022, 0x0019, DicomConstants.DCM_DT_SQ, objLensesCodeSequence.getValue());
		}
		return objLensesCodeSequence;
	}

	/**
	 * Sets the type of detector used to acquire this image. Defined Terms:
	 * DIRECT = X-Ray photoconductor, SCINTILLATOR = Phosphor used, STORAGE = Storage phosphor,
	 * FILM = Scanned film/screen.
	 * 
	 * @param enumDetectorType the enum detector type
	 */
	public void setDetectorType(EnumDetectorType enumDetectorType)
	{
		/* Accept Detector 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 Hash Map
		 */
		addDataElement(0x0018, 0x7004, DicomConstants.DCM_DT_CS, enumDetectorType.getValue());
	}

	/**
	 * Return the type of detector used to acquire this image.
	 * 
	 * @return  - String value of Detector Type
	 */
	public String getDetectorType()
	{
		Object objValue = getValue(0x0018, 0x7004);
		if(objValue != null)
			return objValue.toString();
		return null;
	}

	/**
	 * This method Returns provides the pre-initialized object of Channel Description Code Sequence
	 * return - object of Channel Description Code Sequence. If the module contans 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 SourceImageSequence - the Channel Description Code Sequence
	 */
	public ChannelDescriptionCodeSequence getChannelDescriptionCodeSequence()
	{
		ChannelDescriptionCodeSequence objChannelDescriptionCodeSequence = (ChannelDescriptionCodeSequence)getDicomValue(0x0022, 0x001A);
		if(objChannelDescriptionCodeSequence == 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();
			objChannelDescriptionCodeSequence = (ChannelDescriptionCodeSequence)objDicomV3Factory.createSequence(0x0022, 0x001A, objITransferSyntax, objIDictionaryReader);
			
			// This will add Data Element of SQ to the DataSet.
			addDataElement(0x0022, 0x001A, DicomConstants.DCM_DT_SQ, objChannelDescriptionCodeSequence.getValue());
		}
		return objChannelDescriptionCodeSequence;
	}

	/**
	 * Sets CameraAngleofView
	 * @param fCameraAngleofView Float value of the CameraAngleofView
	 */
	public void setCameraAngleofView( float fCameraAngleofView)
	{
		/*
		 * Accept the CameraAngleofView in String 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
		 */
		addDataElement(0x0022,0x001E, DicomConstants.DCM_DT_FL,fCameraAngleofView);
	}
	/**
	 * Retrieves CameraAngleofView
	 * @return float - Float value of the CameraAngleofView
	 */
	public float getCameraAngleofView()
	{
		Object objValue = getValue(0x0022,0x001E);
		if(objValue != null)
			return Float.parseFloat(objValue.toString());
		return DicomConstants.DCM_NO_VALUE;
	}
	
	/**
	 * This method returns the vector of keys representing the DataElements that belong to this module.
	 * 
	 * @return vector of Tags - returns the vector containing the collection of tags
	 */
	public ISingleCollection<ITag> getVectorOfKeys()
	{
		return objSingleCollection;
	}
}
