/*******************************************************************************
 *  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.ContentIdentificationMacro;
import cdac.medinfo.sdk.dcm30_04.baselibs.DicomV3Factory;
import cdac.medinfo.sdk.dcm30_04.baselibs.SegmentSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.Tag;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataElement;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataSet;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDicomValue;
import cdac.medinfo.sdk.dcm30_04.commoninterface.ITag;
import cdac.medinfo.sdk.dcm30_04.enums.EnumBitsAllocated;
import cdac.medinfo.sdk.dcm30_04.enums.EnumBitsStored;
import cdac.medinfo.sdk.dcm30_04.enums.EnumSegmentationType;
import cdac.medinfo.sdk.dcm30_04.enums.EnumHighBit;
import cdac.medinfo.sdk.dcm30_04.enums.EnumLossyImageCompression;
import cdac.medinfo.sdk.dcm30_04.enums.EnumPhotometricInterpretation;
import cdac.medinfo.sdk.dcm30_04.enums.EnumPixelRepresentation;

import cdac.medinfo.sdk.dcm30_04.exceptions.ConditionFailedException;
import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidItemException;
import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidMacroException;
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 Segmentation Image Module.
*/
public class SegmentationImageModule extends Module
{
	 /**
	  * The obj single collection.
	  */
	 public static ISingleCollection<ITag> objSingleCollection = null;
	 
	 /**
		 * The obj common ct or mr image description macro.
		 */
	 ContentIdentificationMacro objContentIdentificationMacro = null;

		/**
		 * Creates SegmentationImageModule.
		 * 
		 * @param objIDataSet the obj i data set
		 */
		public SegmentationImageModule(IDataSet objIDataSet)
		{
			super(objIDataSet);
		}

		static
		{

			objSingleCollection = new SingleCollection<ITag>();
			objSingleCollection.add(new Tag(0x0008,0x0008));
			objSingleCollection.add(new Tag(0x0028,0x0002));
			objSingleCollection.add(new Tag(0x0028,0x0004));
			objSingleCollection.add(new Tag(0x0028,0x0103));
			objSingleCollection.add(new Tag(0x0028,0x0100));
			objSingleCollection.add(new Tag(0x0028,0x0101));
			objSingleCollection.add(new Tag(0x0028,0x0102));
			objSingleCollection.add(new Tag(0x0028,0x2110));
			objSingleCollection.add(new Tag(0x0028,0x2112));
			objSingleCollection.add(new Tag(0x0028,0x2114));			
			objSingleCollection.add(new Tag(0x0062,0x0001));
			objSingleCollection.add(new Tag(0x0062,0x0010));
			objSingleCollection.add(new Tag(0x0062,0x000E));			
			objSingleCollection.add(new Tag(0x0062,0x0002));
		
		}
		/**
		 * 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
			{		
				getContentIdentificationMacro().validate();
				if(!isPresent(0x0008,0x0008))
					throw new MandatoryMissingException("0008,0008");
				if(!isPresent(0x0028,0x0002))
					throw new MandatoryMissingException("0028,0002");
				if(!isPresent(0x0028,0x0004))
					throw new MandatoryMissingException("0028,0004");
				if(!isPresent(0x0028,0x0103))
					throw new MandatoryMissingException("0028,0103");
				if(!isPresent(0x0028,0x0100))
					throw new MandatoryMissingException("0028,0100");
				if(!isPresent(0x0028,0x0101))
					throw new MandatoryMissingException("0028,0101");
				if(!isPresent(0x0028,0x0102))
					throw new MandatoryMissingException("0028,0102");
				if(!isPresent(0x0028,0x2110))
					throw new MandatoryMissingException("0028,2110");
				if(!isPresent(0x0028,0x2112))
					throw new ConditionFailedException("0028,2112");
				if(!isPresent(0x0028,0x2114))
					throw new ConditionFailedException("0028,2114");				
				if(!isPresent(0x0062,0x0001))
					throw new MandatoryMissingException("0062,0001");
				if(!isPresent(0x0062,0x0002))
					throw new MandatoryMissingException("0062,0002");
				
				String strSegmentationType=getSegmentationType();
				if(strSegmentationType.equalsIgnoreCase(EnumSegmentationType.FRACTIONAL.getValue()))
					if(!isPresent(0x0062,0x0010))
						throw new ConditionFailedException("0062,0010 Required if Segmentation Type (0062,0001) is FRACTIONAL");
				
			if(strSegmentationType.equalsIgnoreCase(EnumSegmentationType.FRACTIONAL.getValue()))
					if(!isPresent(0x0062,0x000E))
						throw new ConditionFailedException("0062,000E Required if Segmentation Type (0062,0001) is FRACTIONAL");
			
			
			}
			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);

			}
			catch(InvalidItemException iie)
			{
				if(DicomConfig.createInstance().isLoggingOn())
					DicomLogger.getLogger().log(iie.getMessage());

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

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

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

			}
			return true;
		}
		
		
		/**
		 * Retrieves pre-initialized instance of Content Identification Macro Attributes.
		 * 
		 * @return ContentIdentificationMacro - instance of {@link ContentIdentificationMacro}.
		 */
		public ContentIdentificationMacro getContentIdentificationMacro()
		{

			if(objContentIdentificationMacro == null)
			{
				objContentIdentificationMacro = new ContentIdentificationMacro(objIDataSet,objIDictionaryReader);
			}
			return objContentIdentificationMacro;
		}
		/**
		 * Sets Image identification characteristics.
		 * 
		 * @param arrStrImageType - String array of Image identification characteristics.
		 */	
		public void setImageType(String[] arrStrImageType)
		{
			/*Accept Image identification characteristics in String Array format of type CS.
			 * Read the Transfer Syntax and check to create ImplicitDE or ExplicitDE .
			 * Set the iGroupNumber and iElementNumber to ImplicitDE object 
			 * Set the iVRType
			 * Set length from the element
			 * Add this element to the Hash Map
			 */
			addDataElement(0x0008, 0x0008, DicomConstants.DCM_DT_CS, arrStrImageType);
		}

		/**
		 * Return Image Type.
		 * 
		 * @return String[]- String array of Image identification characteristics.
		 */
		public String[] getImageType()
		{
			if (objIDataSet == null)
				return null;
			IDataElement objIDataElement =  objIDataSet.getDataForTag(0x0008, 0x0008);
			if(objIDataElement == null)
				return null;
			IDicomValue objIDicomValue = objIDataElement.getDicomValue();
			if(objIDicomValue == null)
				return null;
			if(objIDicomValue.getValue()==null)
				return null;
			return (String[])objIDicomValue.getValue();
		}
		/**
		 * Method is used to set Number of samples (planes) in this image
		 * The function accepts a String Value and creates the
		 * DE based on the transfer syntax and populates the DE with right values.
		 * This De is then added to the Module HashMap
		 * 
		 * @param sSamplesPerPixel the s samples per pixel
		 */
		public void setSamplesPerPixel(short sSamplesPerPixel)
		{
			/*Accept Number of samples (planes) in this image 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 Hash Map
			 */
			addDataElement(0x0028, 0x0002, DicomConstants.DCM_DT_US, sSamplesPerPixel);
		}
		/**
		 * Return the short value of the attribute Samples Per Pixel.
		 * 
		 * @return short - returns the Samples per pixel
		 */
		public short getSamplesPerPixel()
		{Object objectValue = getValue(0x0028, 0x0002);
			if(objectValue!=null)
				return Short.parseShort(objectValue.toString());
			return DicomConstants.DCM_NO_VALUE;
		}

		/**
		 * Sets Specifies the intended interpretation of the pixel data.
		 * 
		 * @param enumPhotometricInterpretation - Enum value Photometric Interpretation
		 */
		public void setPhotometricInterpretation(EnumPhotometricInterpretation enumPhotometricInterpretation)
		{
			/*Accept Specifies the intended interpretation of the pixel data 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
			 */
			addDataElement(0x0028, 0x0004, DicomConstants.DCM_DT_CS, enumPhotometricInterpretation.getValue());
		}

		/**
		 * Retrieves the photometric interpretation.
		 * 
		 * @return String - returns the photometric interpretation
		 */
		public String getPhotometricInterpretation()
		{
			Object objectValue = getValue(0x0028, 0x0004);
			if(objectValue!=null)
				return objectValue.toString();
			return null;
		}
		/**
		 * Sets Pixel Representation
		 * 
		 * @param enumPixelRepresentation - Enum value Pixel Representation
		 */
		public void setPixelRepresentation(EnumPixelRepresentation enumPixelRepresentation)
		{
			/*Accept Pixel Representation  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, 0x0103, DicomConstants.DCM_DT_CS, enumPixelRepresentation.getValue());
		}

		/**
		 * Retrieves the PixelRepresentation  
		 * @return String - returns the PixelRepresentation 
		 */
		public String getPixelRepresentation()
		{
			Object objectValue = getValue(0x0028, 0x0103);
			if(objectValue!=null)
				return objectValue.toString();
			return null;
		}	
		/**
		 * Sets Number of bits allocated for each pixel sample. Each sample shall have the same
		 * number of bits allocated.
		 * Enumerated Values: 8, 16
		 * 
		 * @param enumBitsAllocated the enum bits allocated
		 */
		public void setBitsAllocated(EnumBitsAllocated enumBitsAllocated)
		{
			/*Accept bits allocated for each pixel in this image 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
			 */
			addDataElement(0x0028, 0x0100, DicomConstants.DCM_DT_US, enumBitsAllocated.getValue());
		}

		/**
		 * Retrieves the number of bits allocated.
		 * 
		 * @return - returns the number of bits allocated
		 */
		public short getBitsAllocated()
		{
			Object objectValue = getValue(0x0028, 0x0100);
			if(objectValue!=null)
				return Short.parseShort(objectValue.toString());
			return DicomConstants.DCM_NO_VALUE;
		}
		/**
		 * Sets Number of bits stored for each pixel sample. Each sample shall have the same
		 * number of bits stored.
		 * Enumerated Values: 6 to 16
		 * 
		 * @param enumBitsStored the enum bits stored
		 */
		public void setBitsStored(EnumBitsStored enumBitsStored)
		{
			/*Accept bits stored for each pixel in this image 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
			 */
			addDataElement(0x0028, 0x0101, DicomConstants.DCM_DT_US, enumBitsStored.getValue());
		}

		/**
		 * Retrieves the number of bits stored.
		 * 
		 * @return - returns the number of bits stored
		 */
		public short getBitsStored()
		{
			Object objectValue = getValue(0x0028, 0x0101);
			if(objectValue!=null)
				return Short.parseShort(objectValue.toString());
			return DicomConstants.DCM_NO_VALUE;
		}
		/**
		 * Sets Most significant bit for pixel sample data. Each sample shall have the same high bit.
		 * 
		 * @param enumHighBit the enum high bit
		 */
		public void setHighBit(EnumHighBit enumHighBit)
		{
			/*Accept Most significant bit for pixel in this image 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
			 */
			addDataElement(0x0028, 0x0102, DicomConstants.DCM_DT_US, enumHighBit.getValue());
		}

		/**
		 * Retrieves the high bit.
		 * 
		 * @return - returns the high bit
		 */
		public short getHighBit()
		{
			Object objectValue = getValue(0x0028, 0x0102);
			if(objectValue!=null)
				return Short.parseShort(objectValue.toString());
			return DicomConstants.DCM_NO_VALUE;
		}
		/**
		 * Sets a value that specifies whether an Image has undergone lossy compression.
		 * Enumerated Values:
		 * 00 = Image has NOT been subjected to lossy compression,
		 * 01 = Image has been subjected to lossy compression.
		 * 
		 * @param enumLossyImageCompression the enum lossy image compression
		 */
		public void setLossyImageCompression(EnumLossyImageCompression enumLossyImageCompression)
		{
			/*Accept Specifies whether an Image has undergone lossy compression.
			 * 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, 0x2110, DicomConstants.DCM_DT_CS, enumLossyImageCompression.getValue());
		}

		/**
		 * Retrieves the Lossy Image Compression.
		 * 
		 * @return - returns the Lossy Image Compression
		 */
		public String getLossyImageCompression()
		{
			Object objectValue = getValue(0x0028, 0x2110);
			if(objectValue!=null)
				return objectValue.toString();
			return null;
		}

		/**
		 * Sets the approximate lossy compression ratio(s) that have been applied to this image.
		 * 
		 * @param arrStrLossyImageCompressionRatio - String array of Lossy Image Compression Ratio
		 */
		public void setLossyImageCompressionRatio(String[] arrStrLossyImageCompressionRatio)
		{
			/*Accept the approximate lossy compression ratio(s) that have been applied to this image.
			 * 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, 0x2112, DicomConstants.DCM_DT_DS, arrStrLossyImageCompressionRatio);
		}

		/**
		 * Retrieves the Lossy Image Compression.
		 * 
		 * @return - returns the Lossy Image Compression
		 */
		public String[] getLossyImageCompressionRatio()
		{
			Object objValue = getValue(0x0028,0x2112);
			if(objValue!=null)
				return (String[])objValue;
			return null;

		}
		/**
		 * The method is used to set lossy compression method(s) that have been applied to this image.
		 * The function accepts a String Value and creates the
		 * DE based on the transfer syntax and populates the DE with right values.
		 * This De is then added to the Module HashMap
		 * 
		 * @param arrStrLossyImageCompressionMethod - Lossy Image Compression Method
		 */
		public void setLossyImageCompressionMethod(String[] arrStrLossyImageCompressionMethod)
		{
			/*Accept lossy compression method(s) that have been applied to this image
			 * 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, 0x2114, DicomConstants.DCM_DT_CS, arrStrLossyImageCompressionMethod);
		}

		/**
		 * Retrieves the Lossy Image Compression Method.
		 * 
		 * @return String[] - the Lossy Image Compression Method
		 */
		public String[] getLossyImageCompressionMethod()
		{
			Object objValue = objIDataSet.getValue(0x0028, 0x2114);
			if(objValue != null)
				return (String[])objValue;
			return null;
		}
		/**
		 * Sets SegmentationType
		 * @param enumSegmentationType the enum SegmentationType
		 */
		public void setSegmentationType(EnumSegmentationType enumSegmentationType)
		{
			/*Accept SegmentationType 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(0x0062, 0x0001, DicomConstants.DCM_DT_CS, enumSegmentationType.getValue());
		}
		/**
		 * Retrieves SegmentationType
		 * 
		 * @return - returns the SegmentationType
		 */
		public String getSegmentationType()
		{
			Object objectValue = getValue(0x0062, 0x0001);
			if(objectValue!=null)
				return objectValue.toString();
			return null;
		}
		/**
		 * Sets SegmentationFractionalType
		 * @param strSegmentationFractionalType Specifies SegmentationFractionalType
		 */
		public void setSegmentationFractionalType(String strSegmentationFractionalType)
		{
			/*Accept the SegmentationFractionalType 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(0x0062, 0x0010, DicomConstants.DCM_DT_CS,strSegmentationFractionalType);
		}
		/**
		 * Retrieves SegmentationFractionalType
		 * @return String - SegmentationFractionalType
		 */
		public String getSegmentationFractionalType()
		{
			Object objValue = getValue(0x0062, 0x0010);
			if(objValue != null)
				return objValue.toString();
			return null;
		}
		/**
		 * Sets MaximumFractionalValue
		 * @param shMaximumFractionalValue short value of MaximumFractionalValue
		 */
		public void setMaximumFractionalValue(short shMaximumFractionalValue)
		{
			/* Accept the MaximumFractionalValue 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(0x0062, 0x000E, DicomConstants.DCM_DT_US,shMaximumFractionalValue);
		}

		/**
		 * Retrieves MaximumFractionalValue
		 * @return short - short value of MaximumFractionalValue
		 */
		public short getMaximumFractionalValue()
		{
			Object objValue = getValue(0x0062, 0x000E);
			if(objValue != null)
				return Short.parseShort(objValue.toString());

			return DicomConstants.DCM_NO_VALUE;
		}
		
		/**
		 * This method provides the pre-initialized object of SegmentSequence.
		 * 
		 * @return - object of SegmentSequence. 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
		 */
		public SegmentSequence getSegmentSequence()
		{
			SegmentSequence objSegmentSequence= (SegmentSequence)getDicomValue(0x0008, 0x0301);
			if(objSegmentSequence == 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();
				objSegmentSequence = (SegmentSequence)objDicomV3Factory.createSequence(0x0008, 0x0301, objITransferSyntax, objIDictionaryReader);
				
				// This will add Data Element of SQ to the DataSet.
				addDataElement(0x0062, 0x0002, DicomConstants.DCM_DT_SQ, objSegmentSequence.getValue());
			}
			return objSegmentSequence;
		}
}
