/*******************************************************************************
 *  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.scp.commons;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Locale;


import cdac.medinfo.sdk.dcm30_04.services.association.AssociationLoadFailedException;
import cdac.medinfo.sdk.dcm30_04.services.association.DefaultAssociation;
import cdac.medinfo.sdk.dcm30_04.services.dicomnetcommons.EnumServiceType;
import cdac.medinfo.sdk.dcm30_04.utils.DicomConfig;
import cdac.medinfo.sdk.dcm30_04.utils.DicomLogger;
import cdac.medinfo.sdk.dcm30_04.utils.ISingleCollection;
import cdac.medinfo.sdk.dcm30_04.utils.ResourceBundleWrapper;

/**
 *  This class starts the SCP Session. SCPSession is responsible for accepting TCP connections from SCUs.
 *  It also maintains the Default capability of supported Presentation Contexts in the form of  {@link DefaultAssociation}
 *  Before the SCPSession is started to listen for accepting connections, it is necessary to configure the SCPSession.
 *  This configuration involves adding the {@link ProcessHandler},{@link DefaultAssociation} onto the SCPSession.
 */
public class SCPSession 
{
	protected SCPController controller;
	protected DefaultAssociation objDefaultAssociation;
	protected IProcessHandler objIProcessHandler;
	protected EnumServiceType[] enumServiceTypes;
	protected int itimeout = 0;


	/**
	 * Gets the default association.
	 * @return DefaultAssociation - instance of default association
	 */
	public DefaultAssociation getDefaultAssociation()
	{
		return objDefaultAssociation;
	}

	/**
	 * Sets the default association.
	 * @param objDefaultAssociation instance of Default Association.
	 */
	public void setDefaultAssociation(DefaultAssociation objDefaultAssociation)
	{
		this.objDefaultAssociation = objDefaultAssociation;
	}

	/**
	 * Gets the Process handler.
	 * @return IProcessHandler - instance of Process handler.
	 */
	public IProcessHandler getIProcessHandler() 
	{
		return objIProcessHandler;
	}

	/**
	 * Sets the  process handler.
	 * @param objIProcessHandler  - instance of Process handler.
	 */
	public void setIProcessHandler(IProcessHandler objIProcessHandler)
	{
		this.objIProcessHandler = objIProcessHandler;
	}

	public void selectServices(EnumServiceType[] enumServiceTypes)
	{
		this.enumServiceTypes = enumServiceTypes; 
	}

	/***
	 * 
	 * @param strAETitle
	 */
	protected void initDefaultAssociation(String strAETitle)
	{		
		if(objDefaultAssociation == null)
			objDefaultAssociation = new DefaultAssociation(strAETitle);

		if(!objDefaultAssociation.isAssociationLoaded())
		{
			if(enumServiceTypes == null || enumServiceTypes.length == 0)
			{
				if(DicomConfig.createInstance().isLoggingOn())
				{
					DicomLogger objDicomLogger = DicomLogger.getLogger();
					objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "NoServiceTypeSelected",new Locale(DicomConfig.createInstance().getLocale())));
				}
			}
			else
			{
				int iCount = 0;
				for(EnumServiceType enumServiceType : enumServiceTypes)
				{
					if(enumServiceType == null)
						iCount++;
				}
				if(iCount == enumServiceTypes.length)
				{
					if(DicomConfig.createInstance().isLoggingOn())
					{
						DicomLogger objDicomLogger = DicomLogger.getLogger();
						objDicomLogger.log(ResourceBundleWrapper.getValue("warning", "NoServiceTypeSelected",new Locale(DicomConfig.createInstance().getLocale())));
					}
				}
			}
			objDefaultAssociation.loadAssociation(enumServiceTypes);
		}
		controller.setDefaultAssociation(objDefaultAssociation);
	}


	/**
	 * 
	 * @param strAETitle
	 * @param objInetSocketAddress
	 * @throws IOException
	 * @throws AssociationLoadFailedException
	 */
	private void start(String strAETitle,InetSocketAddress objInetSocketAddress)throws IOException,AssociationLoadFailedException
	{
		controller = new SCPController();
		initDefaultAssociation(strAETitle);		
		controller.setProcessHandler(objIProcessHandler);
		controller.accept(objInetSocketAddress,itimeout);
	}


	/**
	 * This method starts the SCP session on specified IP and port.
	 * @param strAETitle Application Entity Title representation used to identify the DICOM nodes communicating between each other. 
	 * @param strIP String value of IP.
	 * @param iPort int value of port.
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	public void start(String strAETitle,String strIP, int iPort) throws IOException,AssociationLoadFailedException
	{
		InetSocketAddress objInetSocketAddress = new InetSocketAddress(strIP,iPort);
		start(strAETitle, objInetSocketAddress);		
	}

	/**
	 * This method starts the SCP session on the local host.
	 * @param strAETitle Application Entity Title representation used to identify the DICOM nodes communicating between each other.
	 * @param iPort int value of port.
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	public void start(String strAETitle,int iPort)throws IOException
	{
		InetSocketAddress objInetSocketAddress = new InetSocketAddress(InetAddress.getLocalHost(),iPort);
		start(strAETitle, objInetSocketAddress);		
	}



	/**
	 *This method starts the SCP session on the specified Ip and Port.
	 * @param strAETitle Application Entity Title representation used to identify the DICOM nodes communicating between each other.
	 * @param ip instance of INet Address.
	 * @param iPort int value of port.
	 * @throws IOException  Signals that an I/O exception has occurred.
	 */
	public void start(String strAETitle,InetAddress ip, int iPort)throws IOException
	{
		InetSocketAddress objInetSocketAddress = new InetSocketAddress(ip,iPort);
		start(strAETitle, objInetSocketAddress);	
	}


	/**
	 * This method starts the SCP session on the local host.
	 * @param strAETitle Application Entity Title representation used to identify the DICOM nodes communicating between each other.
	 * @param socket.
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	public void start(String strAETitle,Socket socket )throws IOException
	{
		if(controller == null)
			controller = new SCPController();
		
		initDefaultAssociation(strAETitle);
		controller.setProcessHandler(objIProcessHandler);
		controller.initClientSession(socket,itimeout);	
	}


	/**
	 * Retrieves number of clients currently connected to SCP.    
	 * @return int - no. of Clients connected
	 */
	public int noOfClientsConnected()
	{
		return controller.noOfClientsConnected();
	}

	/**
	 * Shows current status of client,whether it is active or not, for given Inet Socket Address
	 * @param objAddress InetSocketAddress of client 
	 * @return boolean - shows status as true or false.
	 */
	public boolean isClientConnected(InetSocketAddress objAddress)
	{
		return controller.isClientConnected(objAddress);
	}

	/**
	 * Retrieves list of connection info for all currently active clients
	 * @return ISingleCollection<InetSocketAddress> - list of InetSocketAddress for all active clients
	 */
	public ISingleCollection<InetSocketAddress> getClientsConnectionInfo()
	{
		return controller.getClientsConnectionInfo();
	}

	/**
	 * Closes the SCP Session.
	 *
	 */
	public void close()
	{
		if(DicomConfig.createInstance().isLoggingOn())
		{
			DicomLogger objDicomLogger = DicomLogger.getLogger();
			objDicomLogger.log(ResourceBundleWrapper.getValue("status", "SCPSessionClosed",new Locale(DicomConfig.createInstance().getLocale())));
		}
		if(controller != null)
			controller.close();
		objDefaultAssociation = null;
		objIProcessHandler = null;
	}


	/**
	 * Stops listening on this SCPSession so that new client connection
	 * not accepted and continue to process already connected clients.
	 */
	public void stopListening()
	{
		if(controller != null)
			controller.stopListening();
	}

	/**
	 * Returns setting for Socket read timeout of this session, 
	 *  0 returns implies that the option is disabled (i.e., timeout of infinity).
	 * @return the setting for socket read timeout.
	 */
	public int getTimeOut() 
	{
		return itimeout;
	}

	/**
	 *  sets  Socket read timeout of this session, in
	 *  milliseconds.  With this option set to a non-zero timeout,
	 *  a read() call on the InputStream associated with this Socket
	 *  will block for only this amount of time.If the timeout expires,
	 *  a session is closed,The timeout must be > 0. A timeout of zero
	 *  is interpreted as an infinite timeout. 
	 * @param timeout the specified timeout, in milliseconds.
	 */
	public void setTimeOut(int itimeout)
	{
		this.itimeout = itimeout;
	}


}