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

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Locale;
import cdac.medinfo.sdk.dcm30_04.commoninterface.IDataSet;
import cdac.medinfo.sdk.dcm30_04.enums.EnumCommandField;
import cdac.medinfo.sdk.dcm30_04.exceptions.InvalidDataSetException;
import cdac.medinfo.sdk.dcm30_04.services.association.AssociateRQ;
import cdac.medinfo.sdk.dcm30_04.services.cstore.commons.CStoreRQ;
import cdac.medinfo.sdk.dcm30_04.services.cstore.scu.CStoreSCU;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumServiceType;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.InvalidDICOMMessageException;
import cdac.medinfo.sdk.dcm30_04.services.dimsecommons.DicomMessage;
import cdac.medinfo.sdk.dcm30_04.services.scu.commons.SCU;
import cdac.medinfo.sdk.dcm30_04.services.scu.commons.SCUSession;
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.ResourceBundleWrapper;
import cdac.medinfo.sdk.dcm30_04.utils.SingleCollection;
/**
 * ORMS_SCU works as a Service Class User for Ophthalmic Refractive Measurements Storage Class.
 * ORMS Service Class includes C-Store service as specified by DIMSE service group.
 * ORMS_SCU sends the DIMSE service request for supported C-Service and handles the response.
 */
public class ORMS_SCU extends SCU
{

	
	/** The i message id. */
	static short iMsgID = 1;

	/**
	 * The IP and port to which user connects.
	 * @param strIP String value of IP to which user connects.
	 * @param port  Port to which user connects.
	 * @throws IOException Signals IO Exception if IO operation fails.
	 */
	public ORMS_SCU(String strIP, int port) throws IOException
	{
		super(new InetSocketAddress(strIP,port));
	}

	/**
	 * The InetAddress and port to which user connects.
	 * @param addr InetAddress to which user connects.
	 * @throws IOException Signals IO Exception if IO operation fails.
	 */
	public ORMS_SCU(InetSocketAddress addr) throws IOException
	{
		super(addr);
	}

	/**
	 * Instantiates a new ORMS_SCU on the SCU session provided by user.
	 * @param objSCUSession the object of SCU session on which  ORMS_SCU is to be initiated.
	 */
	public ORMS_SCU(SCUSession objSCUSession) 
	{
		super(objSCUSession);
	}
	/**
	 * This method creates association request according to the DataSet collection provided by the user and the Default association.
	 * Then it will perform the requested operation on the data set provided by the user, release the association and close the connection.
	 * @param strcallingAETitle value of Calling AE Title
	 * @param strCalledAETitle value of Called AE Title
	 * @param objAssociateRQ The association request i.e. to be sent to SCP.
	 * @param strSOPInstanceUID  SOP Instance UID.
	 * @param objIDataSet Object of IDataSet
	 * @throws IOException Signals IOException if IO operation fails.
	 */
	public void send(String strCallingAETitle,String strCalledAETitle,String strSOPInstanceUID, ISingleCollection<IDataSet> objDataSetCollection) throws IOException
	{
		EnumServiceType[] arrenumServiceType = new EnumServiceType[1];
		arrenumServiceType[0] = EnumServiceType.CSTORE_ORMS;
		AssociateRQ objAssociateRQ = new AssociateRQ(objDataSetCollection,strCalledAETitle,strCallingAETitle);
		send(objAssociateRQ, strSOPInstanceUID, objDataSetCollection);

	}

	/**
	 * Sends the available data sets to the SCP. While sending the data sets, if the connection is not made to the
	 * destination SCP, then the SCU first creates the connection. If the association is not created with the
	 * destination SCP, then the SCU first establishes the association and then sends the data sets.
	 * Note:
	 * The SCU will not close the connection, if already created connection is given to the SCU in the form of SCUSession.
	 * The SCU will neither release the association nor close the connection, if the association is already established
	 * with the destination SCP through SCUSession. User has to explicitly release the association and close the connection
	 * after completion of send process of CSTORE SCU.
	 * 
	 * @param objAssociateRQ The association request i.e. to be sent to SCP.
	 * @param strSOPInstanceUID  SOP Instance UID.
	 * @param objIDataSet Object of IDataSet
	 * @throws IOException Signals IOException if IO operation fails.
	 */
	public void send(AssociateRQ objAssociateRQ,String strSOPInstanceUID,ISingleCollection<IDataSet> objDataSetCollection) throws IOException
	{
		if(objSCUSession != null)
		{
			sendRQ( objAssociateRQ,strSOPInstanceUID, objDataSetCollection);
		}
		else
		{
			objSCUSession = new SCUSession();
			objSCUSession.start(objInetSocketAddress);

			objSCUSession.setMessageListener(this);

			sendRQ( objAssociateRQ, strSOPInstanceUID, objDataSetCollection);

			if(!objSCUSession.getAssociationStatus())
			{
				if(objSCUSession.close())
					objSCUSession = null;
			}
		}
	}

	/**
	 * Send request.
	 * @param objAssociateRQ the object associate request
	 * @param strSOPClasUID  SOP Class UID
	 * @param strSOPInstanceUID SOP Instance UID.
	 * @param objDataSetCollection the object data set collection
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	private void sendRQ(AssociateRQ objAssociateRQ,String strSOPInstanceUID,ISingleCollection<IDataSet> objDataSetCollection) throws IOException
	{
		boolean associationStatusForSession = objSCUSession.getAssociationStatus();
		if(!associationStatusForSession)
		{
			objSCUSession.associate(objAssociateRQ);
		}

		ISingleCollection<CStoreRQ> objCStoreRQ = getDefaultRQ(objDataSetCollection);
		sendRequests(objCStoreRQ);

		if(!associationStatusForSession)
		{
			objSCUSession.releaseAssociation();
		}
	}
	/**
	 * This method sends multiple CStore RQ.
	 * @param objRQCollection Collection of CStoreRQ i.e. to be send.
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	public void sendRequests(ISingleCollection<CStoreRQ> objRQCollection) throws IOException
	{
		ICollectionIterator<CStoreRQ> objRQIterator = objRQCollection.iterator();
		while(objRQIterator.hasNext())
		{
			sendMessage(objRQIterator.next());
		}
	}

	/**
	 * This method sends the Request for ORMS.
	 * @param objDicomMessage  The Dicom Message containing Request for ORMS.
	 * @return true, if send message
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	public boolean sendMessage(DicomMessage objDicomMessage) throws IOException 
	{
		
		 /* at the time of sending message the SCU should register itself as message listener to SCU session.
		 * because if multiple request are send through the SCU session then when response is received which
		 * SCU should be notified. If Message listener is set at time of sending message then in case of sync mode 
		 * the response is received to the proper SCU.
		 */
		try
		{
			associateStatus = objSCUSession.getAssociationStatus();
			objSCUSession.setMessageListener(this);
			boolean isConnected = objSCUSession.connectionState();

			if(isConnected)
			{
				if(associateStatus)
				{
					EnumCommandField enumRequestType = objDicomMessage.getCommandType();
					String strAffectedSOPClassUID = objDicomMessage.getCommandSet().getValue(0x0000,0x0002).toString();
			
					switch(enumRequestType)
					{
					case C_STORE_RQ :
					{
						/*
						 * In this code it referring C_STORE SCP through reflection
						 * so that if our CStore SCU is not present then it can throw exception.
						 */
					if(!DicomConstants.DCM_LESOMETRY_MEASUREMENTS_STORAGE.equals(strAffectedSOPClassUID)||
								!DicomConstants.DCM_AUTOREFRACTION_MEASUREMENTS_STORAGE.equals(strAffectedSOPClassUID)||
								!DicomConstants.DCM_KERAMETRY_MEASUREMENTS_STORAGE.equals(strAffectedSOPClassUID)||
								!DicomConstants.DCM_SUBJECTIVE_REFRACTION_MEASUREMENTS_STORAGE.equals(strAffectedSOPClassUID)||
								!DicomConstants.DCM_VISUAL_ACUITY_MEASUREMENTS_STORAGE.equals(strAffectedSOPClassUID)								
							)
						{
							CStoreSCU objStoreSCU = new CStoreSCU(objSCUSession);
							objStoreSCU.setDIMSEResponse(objDIMSERSPCollection);
							boolean status = objStoreSCU.sendMessage(objDicomMessage);
							return status;
						}
						else
						{

							if(DicomConfig.createInstance().isLoggingOn())
							{
								DicomLogger objDicomLogger = DicomLogger.getLogger();
								objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","InvalidCommandSet",new Locale(DicomConfig.createInstance().getLocale())));
							}
						}

						
					}
					default :
						if(DicomConfig.createInstance().isLoggingOn())
						{
							DicomLogger objDicomLogger = DicomLogger.getLogger();
							objDicomLogger.log(ResourceBundleWrapper.getValue("warning","ServiceRequestNotSupported",new Locale(DicomConfig.createInstance().getLocale()))+ enumRequestType.toString());
						}
					}
				}
				else
				{
					if(DicomConfig.createInstance().isLoggingOn())
					{
						DicomLogger objDicomLogger = DicomLogger.getLogger();
						objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","Association.AssociationDoesNotExist",new Locale(DicomConfig.createInstance().getLocale())));
					}
				}
			}
			else
			{
				if(DicomConfig.createInstance().isLoggingOn())
				{
					DicomLogger objDicomLogger = DicomLogger.getLogger();
					objDicomLogger.log(ResourceBundleWrapper.getValue("exceptions","SessionInvalidated",new Locale(DicomConfig.createInstance().getLocale())));
				}
			}
		}
		catch(InvalidDataSetException ide)
		{
			if(DicomConfig.createInstance().isLoggingOn())
				DicomLogger.getLogger().log(ide);
			throw new InvalidDICOMMessageException(ide.getMessage(),ide);
		}
		return false;
	}

	/**
	 * This method makes CStore RQ's according to the given DataSet Collection.
	 * @param objIDataSetColl Collection of DataSet.
	 * @return ISingleCollection<CStoreRQ> Collection of CStore RQ.
	 */
	public static ISingleCollection<CStoreRQ> getDefaultRQ(ISingleCollection<IDataSet> objIDataSetColl)
	{
		ICollectionIterator<IDataSet> objDataSetColIterator = objIDataSetColl.iterator();
		ISingleCollection<CStoreRQ> objCollecCStoreRQ = new SingleCollection<CStoreRQ>();
		CStoreRQ objCStoreRQ = null;


		while(objDataSetColIterator.hasNext())
		{
			IDataSet objIDataSet = objDataSetColIterator.next();
			objCStoreRQ = CStoreRQ.getDefaultRQ(objIDataSet);
			objCollecCStoreRQ.add(objCStoreRQ);
		}
		return objCollecCStoreRQ;
	}
}
