/*******************************************************************************
 *  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.BasicPixelSpacingCalibrationMacro;
import cdac.medinfo.sdk.dcm30_04.baselibs.ImagePixelMacro;
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.enums.EnumBurnedInAnnotation;
import cdac.medinfo.sdk.dcm30_04.enums.EnumConversionType;
import cdac.medinfo.sdk.dcm30_04.enums.EnumDigitizingDeviceTransportDirection;
import cdac.medinfo.sdk.dcm30_04.enums.EnumPhotometricInterpretation;
import cdac.medinfo.sdk.dcm30_04.enums.EnumPresentationLUTShape;
import cdac.medinfo.sdk.dcm30_04.enums.EnumRecognizableVisualFeatures;
import cdac.medinfo.sdk.dcm30_04.enums.EnumRescaleIntercept;
import cdac.medinfo.sdk.dcm30_04.enums.EnumRescaleSlope;
import cdac.medinfo.sdk.dcm30_04.enums.EnumRescaleType;

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

//TODO: Auto-generated Javadoc
/**
\* This module describes SC Multi-Frame Image Module.
*/

public class SCMultiFrameImageModule extends Module  
{


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


	/**
	 * Instantiates a new sC multi frame image module.
	 * 
	 * @param objIDataSet the obj i data set
	 */
	
	
	
	BasicPixelSpacingCalibrationMacro objBasicPixelSpacingCalibrationMacro = null;
	public SCMultiFrameImageModule(IDataSet objIDataSet)
	{
		super(objIDataSet);
	}

	static
	{
		objSingleCollection = new SingleCollection<ITag>();
		objSingleCollection.add(new Tag(0x0018,0x2010));
		objSingleCollection.add(new Tag(0x0018,0x2020));
		objSingleCollection.add(new Tag(0x0018,0x2030));
		objSingleCollection.add(new Tag(0x0028,0x0009));
		objSingleCollection.add(new Tag(0x0028,0x0301));
		objSingleCollection.add(new Tag(0x0028,0x1052));
		objSingleCollection.add(new Tag(0x0028,0x1053));
		objSingleCollection.add(new Tag(0x0028,0x1054));
		objSingleCollection.add(new Tag(0x2010,0x015E));
		objSingleCollection.add(new Tag(0x2010,0x0160));
		objSingleCollection.add(new Tag(0x2050,0x0020));
	}

	/**
	 * 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
		{			
			if(!isPresent(0x0028,0x0301))
				throw new MandatoryMissingException("0028,0301");

			ImagePixelMacro objImagePixelMacro = new ImagePixelMacro(objIDataSet);
			if(objImagePixelMacro != null)
			{

				String strPhotometricInterpretation = objImagePixelMacro.getPhotometricInterpretation();
				Short shBitsStored  = objImagePixelMacro.getBitsStored();
				if(strPhotometricInterpretation != null)
				{
					if((strPhotometricInterpretation.equalsIgnoreCase(EnumPhotometricInterpretation.MONOCHROME2.getValue())) && (shBitsStored > 1) && (!isPresent(0x2050,0x0020)))
						throw new ConditionFailedException("If element 0028,0004 is MONOCHROME2 and element 0028,0101 is greater than 1 then elemnt 2050,0020 must be present");
					if((strPhotometricInterpretation.equalsIgnoreCase(EnumPhotometricInterpretation.MONOCHROME2.getValue())) && (shBitsStored > 1) && (!isPresent(0x0028,0x1052)))
						throw new ConditionFailedException("If element 0028,0004 is MONOCHROME2 and element 0028,0101 is greater than 1 then elemnt 0028,1052 must be present");
					if((strPhotometricInterpretation.equalsIgnoreCase(EnumPhotometricInterpretation.MONOCHROME2.getValue())) && (shBitsStored > 1) && (!isPresent(0x0028,0x1053)))
						throw new ConditionFailedException("If element 0028,0004 is MONOCHROME2 and element 0028,0101 is greater than 1 then elemnt 0028,1053 must be present");
					if((strPhotometricInterpretation.equalsIgnoreCase(EnumPhotometricInterpretation.MONOCHROME2.getValue())) && (shBitsStored > 1) && (!isPresent(0x0028,0x1054)))
						throw new ConditionFailedException("If element 0028,0004 is MONOCHROME2 and element 0028,0101 is greater than 1 then elemnt 0028,1054 must be present");
				}
			}

			MultiFrameModule objMultiFrameModule = new MultiFrameModule(objIDataSet);
			if(objMultiFrameModule != null)
			{
				String strNumberOfFrames = objMultiFrameModule.getNumberOfFrames();
				if(strNumberOfFrames != null)
				{
					if((Integer.parseInt(strNumberOfFrames) > 1) && (!isPresent(0x0028,0x0009)))
						throw new ConditionFailedException("If Number of Frames is greater than 1 then element 0028,0009 shall be present");
				}
			}
			SCEquipmentModule objSCEquipmentModule = new SCEquipmentModule(objIDataSet);
			if(objSCEquipmentModule != null)
			{
				String strConversionType = objSCEquipmentModule.getConversionType();
				if(strConversionType != null)
				{
					if((strConversionType.equalsIgnoreCase(EnumConversionType.DIGITIZED_FILM.getValue())) && (!isPresent(0x0018,0x2010)))
						throw new ConditionFailedException("If Conversion Type is Digitized Film then element 0018,2010 must be present");
				}
			}
		}
		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;
	}


	/**
	 * Sets Burned In Annotation.
	 * Indicates whether or not image contains sufficient
	 * burned in annotation to identify the patient and date the image was acquired.
	 * 
	 * @param enumBurnedInAnnotation - Enum value of Burned In Annotation.
	 */
	public void setBurnedInAnnotation(EnumBurnedInAnnotation enumBurnedInAnnotation)
	{
		/*Accept Indicates whether or not image contains 
		 * sufficient burned in annotation to identify the patient and date the image was acquired 
		 * 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(0x0028, 0x0301, DicomConstants.DCM_DT_CS, enumBurnedInAnnotation.getValue());
	}

	/**
	 * Retrieves Burned In Annotation.
	 * 
	 * @return the burned in annotation
	 */
	public String getBurnedInAnnotation()
	{Object objectValue = getValue(0x0028,0x0301);
		if(objectValue!=null)
			return objectValue.toString();
		return null;
	}


	/**
	 * Sets indicates whether or not the image contains sufficiently recognizable visual features to allow the image or a reconstruction from a set of images to identifythe patient.
	 *Enumerated Values:
	 *YES
	 *NO
	 * @param enumRecognizableVisualFeatures  enum value of Recognizable Visual Features .
	 */
	public void setRecognizableVisualFeatures (EnumRecognizableVisualFeatures enumRecognizableVisualFeatures)
	{
		/* Accept the RecognizableVisualFeatures  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
		 */
		addDataElement(0x0028, 0x0302, DicomConstants.DCM_DT_CS,enumRecognizableVisualFeatures);
	}
	/**
	 * Return tRecognizable Visual Features
	 * @return String - value of Recognizable Visual Features
	 */
	public String getRecognizableVisualFeatures()
	{
		Object objValue = getValue(0x0028,0x0302);
		if(objValue != null)
			return objValue.toString();
		return null;
	}

	/**
	 * Specifies an identity transformation for the Presentation LUT.
	 * 
	 * @param enumPresentationLUTShape - Enum value of Presentation LUT Shape.
	 */
	public void setPresentationLUTShape(EnumPresentationLUTShape enumPresentationLUTShape)
	{
		/*Accept identity transformation for the Presentation LUT 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
		 * Enumerated Values are:
			IDENTITY - output is in P-Values - shall
			be used if Photometric Interpretation
			(0028,0004) is MONOCHROME2 or any
			color photometric interpretation.
			INVERSE - output after inversion is in PValues
			- shall be used if Photometric
			Interpretation (0028,0004) is
			MONOCHROME1.
		 */
		addDataElement(0x2050, 0x0020, DicomConstants.DCM_DT_CS, enumPresentationLUTShape.getValue());
	}

	/**
	 * Retrieves Presentation LUT Shape.
	 * 
	 * @return the presentation lut shape
	 */
	public String getPresentationLUTShape()
	{Object objectValue = getValue(0x2050,0x0020);
		if(objectValue!=null)
			return objectValue.toString();
		return null;
	}

	/**
	 * Sets Illumination.
	 * Luminance of a hypothetical viewing device illuminating a piece of monochrome transmissive film
	 * or for the case of reflective media, luminance obtainable from diffuse reflection of the illumination present.
	 * 
	 * @param shIllumination - short value of Illumination.
	 */
	public void setIllumination(short shIllumination)
	{
		/* Accept Illumination in String 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(0x2010,0x015E, DicomConstants.DCM_DT_US, shIllumination);
	}

	/**
	 * Retrieves Illumination.
	 * 
	 * @return the illumination
	 */
	public short getIllumination()
	{
		Object objectValue = getValue(0x2010,0x015E);
		if(objectValue!=null)
			return Short.parseShort(objectValue.toString());
		return DicomConstants.DCM_NO_VALUE;
	}


	/**
	 * Sets Reflected Ambient Light.
	 * For scanned monochrome transmissive film viewed on a hypothetical viewing device,
	 * the luminance contribution due to reflected ambient light.
	 * 
	 * @param shReflectedAmbientLight - short value of Reflected Ambient Light.
	 */
	public void setReflectedAmbientLight(short shReflectedAmbientLight)
	{
		/* Accept Structure Set Label in String 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
		 */
		addDataElement(0x2010,0x0160, DicomConstants.DCM_DT_US, shReflectedAmbientLight);
	}

	/**
	 * Retrieves Reflected Ambient Light.
	 * 
	 * @return the reflected ambient light
	 */
	public short getReflectedAmbientLight()
	{Object objectValue = getValue(0x2010,0x0160);
		if(objectValue!=null)
			return Short.parseShort(objectValue.toString());
		return DicomConstants.DCM_NO_VALUE;
	}


	/**
	 * Sets Rescale Intercept.
	 * 
	 * @param enumRescaleIntercept - Enum value of Rescale Intercept.
	 */
	public void setRescaleIntercept(EnumRescaleIntercept enumRescaleIntercept)
	{
		/*Accept Rescale Intercept 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 Hash Map
		 */
		addDataElement(0x0028, 0x1052, DicomConstants.DCM_DT_DS, enumRescaleIntercept.getValue());
	}

	/**
	 * Retrieves Rescale Intercept.
	 * 
	 * @return the rescale intercept
	 */
	public String getRescaleIntercept()
	{Object objectValue = getValue(0x0028,0x1052);
		if(objectValue!=null)
			return objectValue.toString();
		return null;
	}


	/**
	 * Sets Rescale Slope.
	 * 
	 * @param enumRescaleSlope - Enum value of Rescale Slope.
	 */
	public void setRescaleSlope(EnumRescaleSlope enumRescaleSlope)
	{
		/*Accept Rescale Slope 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 Hash Map
		 */
		addDataElement(0x0028, 0x1053, DicomConstants.DCM_DT_DS, enumRescaleSlope.getValue());
	}

	/**
	 * Retrieves Rescale Slope.
	 * 
	 * @return the rescale slope
	 */
	public String getRescaleSlope()
	{Object objectValue = getValue(0x0028,0x1053);
		if(objectValue!=null)
			return objectValue.toString();
		return null;
	}


	/**
	 * Sets Rescale Type.
	 * 
	 * @param enumRescaleType - Enum value of Rescale Type.
	 */
	public void setRescaleType(EnumRescaleType enumRescaleType)
	{
		/*Accept Rescale Type in String format of type LO.
		 * 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(0x0028, 0x1054, DicomConstants.DCM_DT_LO, enumRescaleType.getValue());
	}

	/**
	 * Retrieves Rescale Type.
	 * 
	 * @return the rescale type
	 */
	public String getRescaleType()
	{Object objectValue = getValue(0x0028,0x1054);
		if(objectValue!=null)
			return objectValue.toString();
		return null;
	}

	/**
	 * Sets value that is Used to specify multiple values for Frame Increment Pointer.
	 * 
	 * @param objSingleCollection - Collection of Tags (GroupNumber and ElementNumbers)
	 */
	/*
	 * This method is given since the user can specify multiple GroupNumber and ElementNumbers
	 */
	public void setFrameIncrementPointer(ISingleCollection<ITag> objSingleCollection)
	{
		/*Accept Frame Increment Pointer in String format of type short array.
		 * 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(0x0028, 0x0009, DicomConstants.DCM_DT_AT, objSingleCollection);
	}

	/**
	 * Retrieves the collections of Frame Increment Pointers.
	 * 
	 * @return ISingleCollection<ITag> - collection of Tag objects
	 */
	@SuppressWarnings("unchecked")
	public ISingleCollection<ITag> getFrameIncrementPointer()
	{
		Object objValue = getValue(0x0028,0x0009);
		if(objValue != null)
			return (ISingleCollection<ITag>)objValue;
		return null;
	}


	/**
	 * Sets Nominal Scanned Pixel Spacing.
	 * 
	 * @param arrstrNominalScannedPixelSpacing - String value of Nominal Scanned Pixel Spacing.
	 */
	public void setNominalScannedPixelSpacing(String[] arrstrNominalScannedPixelSpacing)
	{
		/* Accept Structure Set Label 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,0x2010, DicomConstants.DCM_DT_DS, arrstrNominalScannedPixelSpacing);
	}

	/**
	 * Retrieves Nominal Scanned Pixel Spacing.
	 * 
	 * @return the nominal scanned pixel spacing
	 */
	public String[] getNominalScannedPixelSpacing()
	{Object objectValue = getValue(0x0018,0x2010);
		if(objectValue!=null)
			return (String[])objectValue;
		return null;
	}

	/**
	 * Returns instance of Basic Pixel Spacing Calibration Macro.
	 * 
	 * @return Instance of Basic Pixel Spacing Calibration Macro
	 */
	public BasicPixelSpacingCalibrationMacro getBasicPixelSpacingCalibrationMacro()
	{
		if(objBasicPixelSpacingCalibrationMacro == null)
		{
			objBasicPixelSpacingCalibrationMacro = new BasicPixelSpacingCalibrationMacro(objIDataSet,objIDictionaryReader);
		}
		return objBasicPixelSpacingCalibrationMacro;
	}
	
	/**
	 * Sets Digitizing Device Transport Direction.
	 * 
	 * @param enumDigitizingDeviceTransportDirection the enum digitizing device transport direction
	 */
	public void setDigitizingDeviceTransportDirection(EnumDigitizingDeviceTransportDirection enumDigitizingDeviceTransportDirection)
	{
		/* Accept Structure Set Label 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
		 */
		String strDigitizingDeviceTransportDirection = enumDigitizingDeviceTransportDirection.getValue();
		addDataElement(0x0018,0x2020, DicomConstants.DCM_DT_CS, strDigitizingDeviceTransportDirection);
	}

	/**
	 * Retrieves Digitizing Device Transport Direction.
	 * 
	 * @return the digitizing device transport direction
	 */
	public String getDigitizingDeviceTransportDirection()
	{Object objectValue = getValue(0x0018,0x2020);
		if(objectValue!=null)
			return objectValue.toString();
		return null;
	}


	/**
	 * Sets Rotation of Scanned Film.
	 * 
	 * @param strRotationOfScannedFilm - String value of Rotation of Scanned Film.
	 */
	public void setRotationOfScannedFilm(String strRotationOfScannedFilm)
	{
		/* Accept Structure Set Label 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,0x2030, DicomConstants.DCM_DT_DS, strRotationOfScannedFilm);
	}

	/**
	 * Retrieves Rotation of Scanned Film.
	 * 
	 * @return the rotation of scanned film
	 */
	public String getRotationOfScannedFilm()
	{Object objectValue = getValue(0x0018,0x2030);
		if(objectValue!=null)
			return objectValue.toString();
		return null;
	}

	/**
	 * 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;
	}
}
