/*******************************************************************************
 *  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.services.storagecommitment.storagecommitmentsourceimpldb;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Locale;

import cdac.medinfo.sdk.dcm30_04.baselibs.DataSet;
import cdac.medinfo.sdk.dcm30_04.baselibs.FailedSOPSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.ImplicitDE;
import cdac.medinfo.sdk.dcm30_04.baselibs.ReferencedSOPSequence;
import cdac.medinfo.sdk.dcm30_04.baselibs.ReferencedSOPSequenceItem;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataElement;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataSet;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IItem;
import cdac.medinfo.sdk.dcm30_04.dicommodel.modules.StorageCommitmentModule;
import cdac.medinfo.sdk.dcm30_04.dicommodel.storagecommitment.StorageCommitmentIOD;
import cdac.medinfo.sdk.dcm30_04.dicomparserserializers.DicomParser;
import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidDataElementException;
import cdac.medinfo.sdk.dcm30_04.services.dicomjavadb.DCMDBAgent;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumImplOpsStatus;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.SourceException;
import cdac.medinfo.sdk.dcm30_04.services.dimsecommons.DicomMessage;
import cdac.medinfo.sdk.dcm30_04.services.dimsecommons.IDataSource;
import cdac.medinfo.sdk.dcm30_04.services.dimsecommons.IQueryModel;
import cdac.medinfo.sdk.dcm30_04.services.javadbhelper.CServices_QueryModel;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConfig;
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.ResourceBundleWrapper;
import cdac.medinfo.sdk.dcm30_04.utils.SingleCollection;

/**
 * This class class provides functionality to query the data base and 
 * find the resultant matches.This class works as Source Implementation for Storage Commitment.
 * This class accepts Dicom Message received in Storage Commitment Request.
 * Storage Commitment Request contains Reference SOP Sequence which contains the 
 * SOP Class UID and SOP Instance UID for which Storage Commitment needs to be provided.
 * The Search functionality in this class accepts DataSet arrives in Storage Commitment Request,
 * Searches for the SOP Class UID and SOP Instance UID for which Storage Commitment Needs to be provided.
 * Creates Data Set containing Referenced SOP Sequence and Failed SOP Sequence.
 * Referenced SOP Sequence contains information for SOP Class UID for which Storage Commitment can be provided.
 * Failed SOP Sequence contains information for which Storage Commitment Cannot be provided. 
 */
public class StorageCommitmentSourceImplDB implements IDataSource  
{
	private DCMDBAgent objDBAgent = null;

	/**
	 * Instantiates a new Storage Commitment Source Implementation.
	 * @param strConnectionString  connection string.
	 * @param strDriverName  driver name.
	 * @throws SQLException Signals if SQL exception occurs.
	 * @throws IllegalAccessException Signals if illegal access exception occurs.
	 * @throws InstantiationException Signals if instantiation exception occurs.
	 * @throws ClassNotFoundException the class not found exception occurs.
	 */
	public StorageCommitmentSourceImplDB(String strConnectionString, String strDriverName) throws SQLException, IllegalAccessException,InstantiationException,ClassNotFoundException
	{
		objDBAgent = DCMDBAgent.createInstance(strConnectionString,strDriverName);
	}

	/**
	 * This function finds DataSet arrives in Storage Commitment Request,
	 * Searches for the SOP Class UID and SOP Instance UID for which Storage Commitment Needs to be provided.
	 * Creates Data Set containing Referenced SOP Sequence and Failed SOP Sequence.
	 * Referenced SOP Sequence contains information for SOP Class UID for which Storage Commitment can be provided.
	 *  Failed SOP Sequence contains information for which Storage Commitment Cannot be provided. 
	 * @param objDicomMessage object of Dicom Message.
	 * @param objRstDataCollection the matched data set collection
	 * @return short - shows status value which will be used in generation of response
	 * 					Refer EnumImplOpsStatus for supported response status values.
	 * @throws SourceException Signals if search operation fails.
	 */
	public short find(DicomMessage objDicomMessage, ISingleCollection<IDataSet> objRstDataCollection)
	{
		try
		{
			IDataSet objNActionDataSet = objDicomMessage.getDataSet();

			/*
			 * Populate Storage Commitment IOD using N-Action DataSet.
			 * Get the Referenced SOP sequence . This sequence will contain the SOP Class UID and SOP Instnace UID
			 * for which Storage Commitment is required.
			 */
			StorageCommitmentIOD objStorageCommitmentIOD = new StorageCommitmentIOD(objNActionDataSet);
			StorageCommitmentModule objStorageCommitmentModule = objStorageCommitmentIOD.getStorageCommitmentModule();
			ReferencedSOPSequence objReferencedSOPSequence = objStorageCommitmentModule.getReferencedSOPSequence();

			/*
			 * Create Storage Commitment IOD , whose data set needs to be set in N-EventReprt RQ.
			 */
			StorageCommitmentIOD objRSPStorageCommitmentIOD = new StorageCommitmentIOD();
			StorageCommitmentModule objRSPStorageCommitmentModule = objRSPStorageCommitmentIOD.getStorageCommitmentModule();
			objRSPStorageCommitmentModule.setTransactionUID(objStorageCommitmentModule.getTransactionUID());
			ReferencedSOPSequence objRSPReferencedSOPSequence = objRSPStorageCommitmentModule.getReferencedSOPSequence();
			FailedSOPSequence objRSPFailedSOPSequence = objRSPStorageCommitmentModule.getFailedSOPSequence();

			/*
			 * This function gets each item of Referenced SOP Sequence.
			 * Creates A data Set from each item.
			 * Seach it using IDataSource. If the SOP class UID and SOP Instance UID is found in 
			 * DataSource then add it to Referenced SOP sequence else add it to 
			 * Failed SOP Sequence.
			 */
			for(IItem objItem : objReferencedSOPSequence)
			{
				/*
				 * Query Model Does not has the capability to search for Sequence
				 * Thus creating DataSet from Each Item and finding them.
				 * Also the sequence contains Reference SOP Class UID and Referenced SOP Instance UID
				 * while Query Model Knows about SOP Class UID and SOP Instance UID.
				 * Thus creating these data elements and adding to temporary data set to search.
				 */
				IDataSet objTempIDataSet = new DataSet();
				ReferencedSOPSequenceItem objReferencedSOPSequenceItem = (ReferencedSOPSequenceItem)objItem;
				IDataElement objIDataElement = new ImplicitDE(0x0008,0x0016,objReferencedSOPSequenceItem.getDataForTag(0x0008,0x1150).getValue(),objNActionDataSet.getDictionaryReader());
				objTempIDataSet.addDataElement(objIDataElement);
				objIDataElement = new ImplicitDE(0x0008,0x0018,objReferencedSOPSequenceItem.getDataForTag(0x0008,0x1155).getValue(),objNActionDataSet.getDictionaryReader());
				objTempIDataSet.addDataElement(objIDataElement);
				ISingleCollection<IDataSet> objSingleCollectionDataSet = new SingleCollection<IDataSet>();

				CServices_QueryModel objDCMQuery = new CServices_QueryModel(); 
				CServices_QueryModel objRstQueryModel = new CServices_QueryModel();
				objDCMQuery.decode(objTempIDataSet);

				if(objDBAgent.find(objDCMQuery, objRstQueryModel))
					objRstQueryModel.encode(objTempIDataSet.getTransferSyntax(), objSingleCollectionDataSet);

				/*
				 * If the SOP Class UID and SOP Instance UID is find from them then create Referenced SOP Sequence
				 * other wise create Failed SOP Sequence.
				 */
				if(objSingleCollectionDataSet.size() != 0)
				{
					if(DicomConfig.createInstance().isLoggingOn())
					{
						DicomLogger objDicomLogger = DicomLogger.getLogger();
						objDicomLogger.log(ResourceBundleWrapper.getValue("status", "StorageCommitment.SuccessSOPClass", new Locale(DicomConfig.createInstance().getLocale())) + objReferencedSOPSequenceItem.getDataForTag(0x0008,0x1150).getValue());
						objDicomLogger.log(ResourceBundleWrapper.getValue("status", "StorageCommitment.SuccessSOPInstance", new Locale(DicomConfig.createInstance().getLocale())) + objReferencedSOPSequenceItem.getDataForTag(0x0008,0x1155).getValue());
					}
					IItem objRSPReferencedSOPSequenceItem = objRSPReferencedSOPSequence.getNewItem();
					objIDataElement = new ImplicitDE(0x0008,0x1155,objReferencedSOPSequenceItem.getDataForTag(0x0008,0x1155).getValue(),objNActionDataSet.getDictionaryReader());
					objRSPReferencedSOPSequenceItem.addDataElement(objIDataElement);
					objIDataElement = new ImplicitDE(0x0008,0x1150,objReferencedSOPSequenceItem.getDataForTag(0x0008,0x1150).getValue(),objNActionDataSet.getDictionaryReader());
					objRSPReferencedSOPSequenceItem.addDataElement(objIDataElement);
					objRSPReferencedSOPSequence.addItem(objRSPReferencedSOPSequenceItem);
				}
				else
				{
					if(DicomConfig.createInstance().isLoggingOn())
					{
						DicomLogger objDicomLogger = DicomLogger.getLogger();
						objDicomLogger.log(ResourceBundleWrapper.getValue("status", "StorageCommitment.FailedSOPClass", new Locale(DicomConfig.createInstance().getLocale())) + objReferencedSOPSequenceItem.getDataForTag(0x0008,0x1150).getValue());
						objDicomLogger.log(ResourceBundleWrapper.getValue("status", "StorageCommitment.FailedSOPInstance", new Locale(DicomConfig.createInstance().getLocale())) + objReferencedSOPSequenceItem.getDataForTag(0x0008,0x1155).getValue());
					}
					IItem objRSPFailedSOPSequenceItem = objRSPFailedSOPSequence.getNewItem();
					objIDataElement = new ImplicitDE(0x0008,0x1155,objReferencedSOPSequenceItem.getDataForTag(0x0008,0x1155).getValue(),objNActionDataSet.getDictionaryReader());
					objRSPFailedSOPSequenceItem.addDataElement(objIDataElement);
					objIDataElement = new ImplicitDE(0x0008,0x1150,objReferencedSOPSequenceItem.getDataForTag(0x0008,0x1150).getValue(),objNActionDataSet.getDictionaryReader());
					objRSPFailedSOPSequenceItem.addDataElement(objIDataElement);
					objRSPFailedSOPSequence.addItem(objRSPFailedSOPSequenceItem);
				}
			}
			/*
			 * create N-Action RSP DataSet.
			 */
			IDataSet objIDataSet = objRSPStorageCommitmentIOD.getDataSet();
			IDataElement objIDataElement = new ImplicitDE(0x0008,0x1199,objRSPReferencedSOPSequence,objIDataSet.getTransferSyntax(),objIDataSet.getDictionaryReader());
			objIDataSet.addDataElement(objIDataElement);
			objIDataElement = new ImplicitDE(0x0008,0x1198,objRSPFailedSOPSequence,objIDataSet.getTransferSyntax(),objIDataSet.getDictionaryReader());
			objIDataSet.addDataElement(objIDataElement);
			objRstDataCollection.add(objIDataSet);
		}
		catch(IOException ioe)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger.getLogger().log(ioe.getMessage());
			}
			throw new SourceException(ResourceBundleWrapper.getValue("exceptions", "StorageCommitment.SourceException", new Locale(DicomConfig.createInstance().getLocale())),ioe);
		}
		catch(SQLException sqe)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger.getLogger().log(sqe.getMessage());
			}
			throw new SourceException(ResourceBundleWrapper.getValue("exceptions", "StorageCommitment.SourceException", new Locale(DicomConfig.createInstance().getLocale())),sqe);
		}
		catch (InvalidDataElementException ide)
		{
			if (DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ide.getMessage());
			}
		}
		return EnumImplOpsStatus.SUCCESS.getValue();
	}

	/**
	 * @see cdac.medinfo.sdk.dcm30_04.services.dimsecommons.IDataSource#find(cdac.medinfo.sdk.dcm30_04.services.dimsecommons.QueryModel, cdac.medinfo.sdk.dcm30_04.services.dimsecommons.QueryModel)
	 */
	public short find(IQueryModel objQueryModel,IQueryModel objRstQueryModel)
	{
		return EnumImplOpsStatus.FAILURE.getValue();
	}

	/**
	 * This function finds SOP Class UID and SOP Instance UID DataSets arrives in Storage Commitment Request,
	 * Searches for the SOP Class UID and SOP Instance UID for which Storage Commitment Needs to be provided.
	 * Retrieves them and add then to the resultant DataSet Collection. 
	 * Referenced SOP Sequence contains information for SOP Class UID for which Storage Commitment can be provided.
	 * Failed SOP Sequence contains information for which Storage Commitment Cannot be provided.
	 * @return short - shows status value which will be used in generation of response
	 * 					Refer EnumImplOpsStatus for supported response status values.
	 * @throws SourceException Signals if search operation fails.
	 */
	public short retrieve(DicomMessage objDicomMessage, ISingleCollection<IDataSet> objRstDataCollection)
	{
		try
		{
			/*
			 * Query Model Does not has the capability to search for Sequence
			 * Thus creating DataSet from Each Item and finding them.
			 * Also the sequence contains Reference SOP Class UID and Referenced SOP Instance UID
			 * while Query Model Knows about SOP Class UID and SOP Instance UID.
			 * Thus creating these data elements and adding to temporary data set to search.
			 * Adding the retrieve DataSet to DataSet Collection.
			 */
			DicomMessage objTempDicomMessage = new DicomMessage();
			IDataSet objNActionDataSet = objDicomMessage.getDataSet();
			StorageCommitmentIOD objStorageCommitmentIOD = new StorageCommitmentIOD(objNActionDataSet);
			StorageCommitmentModule objStorageCommitmentModule = objStorageCommitmentIOD.getStorageCommitmentModule();
			ReferencedSOPSequence objReferencedSOPSequence = objStorageCommitmentModule.getReferencedSOPSequence();
			for(IItem objItem : objReferencedSOPSequence)
			{
				IDataSet objTempIDataSet = new DataSet();
				ReferencedSOPSequenceItem objReferencedSOPSequenceItem = (ReferencedSOPSequenceItem)objItem;
				IDataElement objIDataElement = new ImplicitDE(0x0008,0x0016,objReferencedSOPSequenceItem.getDataForTag(0x0008,0x1150).getValue(),objNActionDataSet.getDictionaryReader());
				objTempIDataSet.addDataElement(objIDataElement);
				objIDataElement = new ImplicitDE(0x0008,0x0018,objReferencedSOPSequenceItem.getDataForTag(0x0008,0x1155).getValue(),objNActionDataSet.getDictionaryReader());
				objTempIDataSet.addDataElement(objIDataElement);
				objTempDicomMessage.setDataSet(objTempIDataSet);
				IDataSet objIDataSet = objTempDicomMessage.getDataSet();
				CServices_QueryModel objDCMQuery = new CServices_QueryModel(); 
				objDCMQuery.decode(objIDataSet);
				ISingleCollection<String> filePathCollection = objDBAgent.retrive(objDCMQuery);
				ICollectionIterator<String> objFileIterator = filePathCollection.iterator();
				while(objFileIterator.hasNext())
				{
					IDataSet objIDataSetMatch = null;
					DicomParser objDicomParser = new DicomParser();
					String strFilePath = objFileIterator.next();
					try
					{
						objIDataSetMatch = objDicomParser.parseAsPart10File(strFilePath, true);
					}
					catch(IOException ioe)
					{
						if(DicomConfig.createInstance().isLoggingOn())
						{
							DicomLogger.getLogger().log(ioe.getMessage());
						}
					}
					if(objIDataSetMatch != null)
						objRstDataCollection.add(objIDataSetMatch);
				}
			}
		}
		catch(IOException ioe)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger.getLogger().log(ioe.getMessage());
			}
			throw new SourceException(ResourceBundleWrapper.getValue("exceptions", "StorageCommitment.SourceException", new Locale(DicomConfig.createInstance().getLocale())),ioe);
		}
		catch(SQLException sqe)
		{
			if(DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger.getLogger().log(sqe.getMessage());
			}
			throw new SourceException(ResourceBundleWrapper.getValue("exceptions", "StorageCommitment.SourceException", new Locale(DicomConfig.createInstance().getLocale())),sqe);
		}
		catch (InvalidDataElementException ide)
		{
			if (DicomConfig.createInstance().isLoggingOn())
			{
				DicomLogger objDicomLogger = DicomLogger.getLogger();
				objDicomLogger.log(ide.getMessage());
			}
		}
		return EnumImplOpsStatus.SUCCESS.getValue();
	}

	@Override
	public short retrieve(IQueryModel arg0, IQueryModel arg1)
	{
		return EnumImplOpsStatus.FAILURE.getValue();
	}
}
