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


import cdac.medinfo.sdk.dcm30_04.baselibs.DataSet;
import cdac.medinfo.sdk.dcm30_04.baselibs.ExplicitDE;
import cdac.medinfo.sdk.dcm30_04.baselibs.ImageElement;
import cdac.medinfo.sdk.dcm30_04.baselibs.ImplicitDE;
import cdac.medinfo.sdk.dcm30_04.baselibs.OBImageObject;
import cdac.medinfo.sdk.dcm30_04.baselibs.OWImageObject;
import cdac.medinfo.sdk.dcm30_04.baselibs.TransferSyntax;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataElement;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataSet;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataTypeSQ;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDicomValue;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDictionaryReader;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IItem;
import cdac.medinfo.sdk.dcm30_04.commoninterface.ITag;
import cdac.medinfo.sdk.dcm30_04.commoninterface.ITransferSyntax;
import cdac.medinfo.sdk.dcm30_04.dictionaryreaderwriters.DictionaryReader;
import cdac.medinfo.sdk.dcm30_04.exceptions.ConversionFailedException;
import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidDataElementException;
import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidDataSetException;
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.IPairedCollection;
import cdac.medinfo.sdk.dcm30_04.utils.ISingleCollection;
import cdac.medinfo.sdk.dcm30_04.utils.PairedCollection;
import cdac.medinfo.sdk.dcm30_04.utils.StandardMethods;

/**
 * This class converts the DataElement into the desired TransferSyntax. During conversion, it converts
 * the DataElement to its corresponding default TransferSyntax.
 * 
 */
public class ConverterCodec  
{
	
	
	IDictionaryReader objIDictionaryReader = null;

	/**
	 * This method converts the DataElement into the specified TransferSyntax and uses Default DictionaryReader if needed.
	 * @param objIDataElement - DataElement object which is to be converted into the specified TransferSyntax.
	 * @param strTSUID - TransferSyntax UID specified by DICOM Standard.
	 * @return DataElement - object converted into the desired Transfer Syntax.
	 */
	public IDataElement recursiveConvert(IDataElement objIDataElement, String strTSUID)
	{
		return recursiveConvert(objIDataElement, strTSUID, null);
	}

	/**
	 * This method converts the DataElement to specified TransferSyntax and uses the specified Dictionary
	 * reader. If the DataElement contains the data of type SQ, then it applies the conversion to all the 
	 * nesting levels using recursion. After conversion, SQ and all its nesting SQs and Items get converted
	 * into default TransferSyntax.
	 * @param objIDataElement - DataElement object which is to be converted into the specified TransferSyntax
	 * @param strTSUID - TransferSyntax UID specified by DICOM Standard
	 * @param objIDictionaryReader - Dictionary Reader specified by the user
	 * @return DataElement - object converted into the desired Transfer Syntax
	 */
	@SuppressWarnings("unchecked")
	public IDataElement recursiveConvert(IDataElement objIDataElement, String strTSUID, IDictionaryReader objIDictionaryReader)
	{
		ImageElement objImageElement = null;

		if(strTSUID == null)
		{
			return null;
			/*
			 * Here exception needs to be thrown if somebody tries to convert DataElement to null TransferSyntax
			 */
		}
		if(strTSUID.equals(objIDataElement.getTransferSyntax().getTransferSyntaxUID()))
		{
			return objIDataElement;
		}

		ITransferSyntax objITransferSyntax = new TransferSyntax(strTSUID);
		String strVRType = null;

		int iGroupNumber = objIDataElement.getTag().getGroupNumber();
		int iElementNumber = objIDataElement.getTag().getElementNumber();
		int iVRType = 0;

		IDicomValue objIDicomValue = objIDataElement.getDicomValue();
		Object objValue = null;

		if(objIDicomValue != null)
		{
			if(!(iGroupNumber == 0x7fe0 && iElementNumber == 0x0010 && DicomConfig.createInstance().isTempStorageInUse()))
				objValue = objIDicomValue.getValue();
			if(objIDicomValue instanceof IDataTypeSQ)
			{
				IItem objIItem = null;
				IPairedCollection<ITag, IDataElement> objPairedCollection = new PairedCollection<ITag, IDataElement>();

//				HashMap<ITag, IDataElement> objHashMap = new HashMap<ITag,IDataElement>();
				ISingleCollection<IItem> objVectOfItems = (ISingleCollection<IItem>) objValue;
				for(int index = 0; index < objVectOfItems.size(); index++)
				{
					objIItem = objVectOfItems.get(index);
					ICollectionIterator<IDataElement> objIterator = objIItem.iterator();
					IDataElement objTempIDataElement = null;
					while(objIterator.hasNext())
					{
						objTempIDataElement = objIterator.next();
						objTempIDataElement = recursiveConvert(objTempIDataElement, strTSUID, objIDictionaryReader);
						objPairedCollection.put(objTempIDataElement.getTag(),objTempIDataElement);
					}
					objIItem.setTransferSyntax(objITransferSyntax);
					objIItem.setValue(objPairedCollection);
				}
			}
		}

		if(objIDataElement  instanceof ImplicitDE && objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_EXPLICIT)
		{
			strVRType = objIDictionaryReader.getVR(iGroupNumber, iElementNumber);
			/*
			 * Whent the VR read from Dictionary will be "OB/OW" or "OW/OB" we would use VR as "OW". 
			 * If the TransferSyntax is LittleIndianImplicit then VR will be considered as OW
			   else TransferSyntax would be considered as OB.
			 */
			if(strVRType!= null)
			{
				if(strVRType.equalsIgnoreCase("OW/OB") || strVRType.equalsIgnoreCase("OB/OW"))
				{
					if(objITransferSyntax.getEndianNess() == DicomConstants.DCM_ENDIANNESS_LITTLE_ENDIAN 
							&& objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_IMPLICIT)
						strVRType = "OW";
					else
						strVRType = "OB";
				}
			}
			iVRType = StandardMethods.getiVRtype(strVRType);

			//if 7FE0,0010 occurs and temp storage is in use then make Image Element.
			if(iGroupNumber == 0x7fe0 && iElementNumber == 0x0010 && DicomConfig.createInstance().isTempStorageInUse() && strVRType.equals("OW"))
			{
				objImageElement = new ImageElement(objITransferSyntax, (OWImageObject)objIDataElement.getDicomValue());
				return objImageElement;
			}
			//if 7FE0,0010 occurs and temp storage is in use then make Image Element.
			else if(iGroupNumber == 0x7fe0 && iElementNumber == 0x0010 && DicomConfig.createInstance().isTempStorageInUse() && strVRType.equals("OB"))
			{
				objImageElement = new ImageElement(objITransferSyntax, (OBImageObject)objIDataElement.getDicomValue());
				return objImageElement;
			}
			else
				objIDataElement = new ExplicitDE(iGroupNumber, iElementNumber, iVRType, objValue, objITransferSyntax);
		}
		else if(objIDataElement  instanceof ImplicitDE && objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_IMPLICIT)
			objIDataElement.setTransferSyntax(objITransferSyntax);
		//if 7FE0,0010 occurs and temp storage is in use then make Image Element.
		else if(iGroupNumber == 0x7fe0 && iElementNumber == 0x0010 && DicomConfig.createInstance().isTempStorageInUse())
		{
			if(objIDicomValue instanceof OWImageObject)
				objImageElement = new ImageElement(objITransferSyntax, (OWImageObject)objIDataElement.getDicomValue());
			else if(objIDicomValue instanceof OBImageObject)
				objImageElement = new ImageElement(objITransferSyntax, (OBImageObject)objIDataElement.getDicomValue());
			return objImageElement;
		}
		else if(objIDataElement instanceof ExplicitDE && objITransferSyntax.getVRTransferSyntax() == DicomConstants.DCM_VR_IMPLICIT)
			objIDataElement = new ImplicitDE(iGroupNumber, iElementNumber, objValue, objITransferSyntax, objIDictionaryReader);

		return objIDataElement;
	}

	/**
	 * 
	 * @param objIDataSet The DataSet which is to be converted .
	 * @param asIs the boolean parameter which decides whether data set should be converted to default or not.
	 * @return IDataSet - object converted into the desired Transfer Syntax.
	 */
	public IDataSet Convert(IDataSet objIDataSet, boolean asIs)
	{
		IDataSet objTempIDataSet = Convert(objIDataSet, asIs ,null);
		return objTempIDataSet;
	}

	/**
	 * 
	 * @param objIDataSet The DataSet which is to be converted .
	 * @param asIs the boolean parameter which decides whether data set should be converted to default or not.
	 * @param objIDictionaryReader  The DictionaryReader which is given by the user.
	 * @return IDataSet - object converted into the desired Transfer Syntax.
	 */
	public IDataSet Convert(IDataSet objIDataSet, boolean asIs ,IDictionaryReader objIDictionaryReader )
	{
		try
		{
			objIDataSet.validate();
			if(asIs == true)
			{
				return objIDataSet;
			}
			else
			{
				ITransferSyntax objITransferSyntax = objIDataSet.getTransferSyntax();
				TransferSyntax objITSyntax = new TransferSyntax(objITransferSyntax.getTransferSyntaxUID());
				objITSyntax.changeToDefault();

				/*
				 * If asIs is false and if after conversion of transfer syntax to default, it is same as of the transfer syntax of data set 
				 * then there is no need of conversion. the function should return data set.
				 */
				if(objITSyntax.equals(objITransferSyntax))
					return objIDataSet;

				/*
				 * If dictionary reader is null, then Parser assumes default dictionary reader  
				 */
				if(objIDictionaryReader == null )
				{
					objIDictionaryReader = DictionaryReader.createInstance();
					objIDictionaryReader.populate();
				}
				IDataSet objITempDataSet = new DataSet(objITSyntax,objIDictionaryReader);
				IDataElement objIDataElement = null;
				ICollectionIterator<IDataElement> objDataSetIterator = objIDataSet.iterator();
				while(objDataSetIterator.hasNext())
				{
					objIDataElement = objDataSetIterator.next();
					objIDataElement = recursiveConvert(objIDataElement, objITSyntax.getTransferSyntaxUID(), objIDictionaryReader);
					if(objIDataElement.validate())
						objITempDataSet.addDataElement(objIDataElement);
				}
				return objITempDataSet;
			}
		}
		catch(InvalidDataElementException e)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger.getLogger().log(e.getMessage());
			}
			throw new ConversionFailedException(e.getMessage(),e);
		}
		catch(InvalidDataSetException e)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger.getLogger().log(e.getMessage());
			}
			throw new ConversionFailedException(e.getMessage(),e);
		}
	}
}
