package com.ardic.arcsp.ws.impl;

import javax.xml.namespace.QName;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.util.AXIOMUtil;
import org.apache.axiom.soap.SOAP11Constants;
import org.apache.axiom.soap.SOAP12Constants;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.OperationClient;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.OutInAxisOperation;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.i18n.Messages;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;

import com.ardic.arcsp.ws.WsClientException;
import com.ardic.arcsp.ws.WsClientHelper;

public abstract class Axis2WsClientHelper implements WsClientHelper{


	private  ServiceClient client;
	private static MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
	private static AxisService service;
	
	private static boolean isInit = false;
	private static ConfigurationContext clientConfigContext = null;
	
	/**
	 * Required to call init(configPath) after initialization
	 * @throws WsClientException
	 */
	public Axis2WsClientHelper() throws WsClientException {

		
			service = new AxisService();
	}
	
	/**
	 * 
	 * @param absolute path of the configuration file
	 * @throws WsClientException
	 */
	public Axis2WsClientHelper(String configPath) throws WsClientException {

		service = new AxisService();
		init(configPath);
	}
	
	
	/**
	 * @deprecated
	 * @param serviceName name of the Web Service
	 * @param configPath absolute path of axis2_client configuration file
	 * @throws WsClientException
	 */
	public Axis2WsClientHelper(String serviceName,String configPath) throws WsClientException {

		try {
			service = new AxisService(serviceName);
			clientConfigContext = ConfigurationContextFactory.createConfigurationContextFromFileSystem(configPath);
			
			HttpConnectionManagerParams params = new HttpConnectionManagerParams();
			Parameter defaultMaxConnPerHostParam = clientConfigContext
					.getAxisConfiguration().getParameter(
							"defaultMaxConnPerHost");
			int defaultMaxConnPerHost = 500;
			if (defaultMaxConnPerHostParam != null) {
				defaultMaxConnPerHost = Integer
						.parseInt((String) defaultMaxConnPerHostParam
								.getValue());
			}
			params.setDefaultMaxConnectionsPerHost(defaultMaxConnPerHost);

			// Set the max total connections
			int maxTotalConnections = 15000;
			Parameter maxTotalConnectionsParam = clientConfigContext
					.getAxisConfiguration().getParameter("maxTotalConnections");
			if (maxTotalConnectionsParam != null) {
				maxTotalConnections = Integer
						.parseInt((String) maxTotalConnectionsParam.getValue());
			}
			params.setMaxTotalConnections(maxTotalConnections);

			params.setSoTimeout(600000);
			params.setConnectionTimeout(600000);
			
			
			connectionManager.setParams(params);
			 clientConfigContext.setProperty(HTTPConstants.MULTITHREAD_HTTP_CONNECTION_MANAGER,
					 connectionManager);
			
			//httpClient = new HttpClient(connectionManager);
		} catch (AxisFault e) {
			e.printStackTrace();
			throw new WsClientException("Cannot initialize service client: "
					+ e.getMessage());
		}

	}
	
	

	/**
	 * @deprecated
	 * @param serviceName name of Web Service
	 * @param clientConfigContext instance of ConfigurationContext derived from a valid configuration file
	 * @throws WsClientException
	 */
	public Axis2WsClientHelper(String serviceName, ConfigurationContext clientConfigContext) throws WsClientException {
		
		
		service = new AxisService(serviceName);
		Axis2WsClientHelper.clientConfigContext = clientConfigContext;
		HttpConnectionManagerParams params = new HttpConnectionManagerParams();
		Parameter defaultMaxConnPerHostParam = clientConfigContext
				.getAxisConfiguration().getParameter(
						"defaultMaxConnPerHost");
		int defaultMaxConnPerHost = 500;
		if (defaultMaxConnPerHostParam != null) {
			defaultMaxConnPerHost = Integer
					.parseInt((String) defaultMaxConnPerHostParam
							.getValue());
		}
		params.setDefaultMaxConnectionsPerHost(defaultMaxConnPerHost);

		// Set the max total connections
		int maxTotalConnections = 15000;
		
		Parameter maxTotalConnectionsParam = clientConfigContext
				.getAxisConfiguration().getParameter("maxTotalConnections");
		if (maxTotalConnectionsParam != null) {
			maxTotalConnections = Integer
					.parseInt((String) maxTotalConnectionsParam.getValue());
		}
		params.setMaxTotalConnections(maxTotalConnections);

		params.setSoTimeout(600000);
		params.setConnectionTimeout(600000);
		
		
		connectionManager.setParams(params);
		 clientConfigContext.setProperty(HTTPConstants.MULTITHREAD_HTTP_CONNECTION_MANAGER,
				 connectionManager);
			
		
	}

	@Override
	public String executeRequest(String wsHost, String serviceName,
			String operationName, String payload, boolean nonBlocking)
			throws WsClientException {
		
		OMElement result = executeAxis2Request(wsHost, serviceName, operationName, payload, nonBlocking);
		if (nonBlocking)
			return null;
		else if (result == null){
			throw new RuntimeException("Web services did not return any result to be parsed");
		}
		return result.toString();
	}
	
	/**
	 * @deprecated
	 * Execute the request with axis2 OMElement request/response
	 * @param wsHost host containing web services in the format http(s)://host:port/services
	 * @param operationName name of the Web Service operation
	 * @param payload instance of OMElement containing the soap request body. For configuring payload use PayLoadUtil helper class
	 * @param nonBlocking flag for nonBlocking call if true no response will be returned
	 * @return if nonBlocking is set true null else response will be an instance of OMElement
	 * @throws WsClientException 
	 */
	public OMElement executeAxis2Request(String wsHost,
			String operationName, String payload, boolean nonBlocking) throws WsClientException {
		
		String serviceName = service.getName();
		OMElement result = null;
		String eprStr = null;
		
		if (wsHost.lastIndexOf("/") == wsHost.length()-1)
			eprStr = wsHost + serviceName;
		else
			eprStr = wsHost + "/" + serviceName;
		
		EndpointReference epr = new EndpointReference(eprStr);
		Options opt = new Options();
		opt.setProperty(HTTPConstants.CONNECTION_TIMEOUT,1000);
		opt.setTo(epr);
		opt.setAction("urn:"+ operationName);
		//opt.setProperty(HTTPConstants.REUSE_HTTP_CLIENT, Constants.VALUE_TRUE);
		//opt.setProperty(HTTPConstants.CACHED_HTTP_CLIENT, httpClient);
		//opt.setProperty(HTTPConstants.AUTO_RELEASE_CONNECTION, true);
	
		OMElement request;
		QName operation = new QName(operationName);
		
		service.addOperation(new OutInAxisOperation(operation));
		MessageContext messageContext = null;
		try {
			client = new ServiceClient(clientConfigContext,service);
			client.setOptions(opt);
			request = AXIOMUtil.stringToOM(payload);
			//Operation client setting
			OperationClient operationClient = client.createClient(operation);
            operationClient.getOptions().setAction("urn:" + operationName);
            operationClient.getOptions().setExceptionToBeThrownOnSOAPFault(true);

            //Message context setting
            SOAPEnvelope env = getFactory(operationClient.getOptions().getSoapVersionURI()).getDefaultEnvelope();
            messageContext = new MessageContext();
            env.getBody().addChild(request);
            messageContext.setEnvelope(env);
			
            operationClient.addMessageContext(messageContext);
           
			if (nonBlocking)
				operationClient.execute(false);
			else{
				operationClient.execute(true);
				MessageContext returnMessage = operationClient.getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
				SOAPEnvelope returnEnv = returnMessage.getEnvelope();
				returnEnv.getBody().toString();
				result = returnEnv.getBody().getFirstElement();
			}
				
		
		} catch (AxisFault e) {
			e.printStackTrace();
			throw new WsClientException("Error calling the web service: " + " Host: " + wsHost + " ServiceName: " + serviceName + " Operation Name: " + operationName + " Payload: " + payload + " Exception message: " + e.getMessage());
			
		} catch (Exception e){
			e.printStackTrace();
			throw new WsClientException("Error in the web service method: + " + wsHost + " ServiceName: " + serviceName + " Operation Name: " + operationName + " Payload: " + payload + " Exception message: " + e.getMessage());
		}finally{
			if (messageContext.getTransportOut() != null) {
                try {
					messageContext.getTransportOut().getSender().cleanup(messageContext);
				} catch (AxisFault e) {
					throw new WsClientException("Error cleaning up connection: + " + wsHost + " ServiceName: " + serviceName + " Operation Name: " + operationName + " Payload: " + payload + " Exception message: " + e.getMessage());
				}
          }
		}
	
		
		
		return result;
	}
	
	private static SOAPFactory getFactory(String soapVersionURI) {
	
		       if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(soapVersionURI)) {
		           return OMAbstractFactory.getSOAP11Factory();
		       } else if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(soapVersionURI)) {
		          return OMAbstractFactory.getSOAP12Factory();
		      } else {
		          throw new RuntimeException(Messages
		                 .getMessage("unknownsoapversion"));
	      }    }

	/**
	 * Execute the request 
	 * @param wsHost host containing web services in the format http(s)://host:port/services
	 * @param serviceName name of the web service
	 * @param operationName  name of the Web Service operation
	 * @param payload
	 * @param nonBlocking
	 * @return
	 * @throws WsClientException
	 */
	public OMElement executeAxis2Request(String wsHost,String serviceName,
			String operationName, String payload, boolean nonBlocking) throws WsClientException {
		

		service.setName(serviceName);
		
		if (!isInit)
			throw new WsClientException("The client is not initialized call init(String configPath) to initialize");
		
		OMElement result = null;
		String eprStr = null;
		
		if (wsHost.lastIndexOf("/") == wsHost.length()-1)
			eprStr = wsHost + serviceName;
		else
			eprStr = wsHost + "/" + serviceName;
		
		EndpointReference epr = new EndpointReference(eprStr);
		Options opt = new Options();
		opt.setProperty(HTTPConstants.CONNECTION_TIMEOUT,1000);
		opt.setTo(epr);
		opt.setAction("urn:"+ operationName);
		//opt.setProperty(HTTPConstants.REUSE_HTTP_CLIENT, Constants.VALUE_TRUE);
		//opt.setProperty(HTTPConstants.CACHED_HTTP_CLIENT, httpClient);
		//opt.setProperty(HTTPConstants.AUTO_RELEASE_CONNECTION, true);
	
		OMElement request;
		QName operation = new QName(operationName);
		
		
		MessageContext messageContext = null;
		try {
			client.setOptions(opt);
			service.addOperation(new OutInAxisOperation(operation));
			client.setAxisService(service);
			
				
				
				
				
			
			
			
			
			request = AXIOMUtil.stringToOM(payload);
			//Operation client setting
			OperationClient operationClient = client.createClient(operation);
            operationClient.getOptions().setAction("urn:" + operationName);
            operationClient.getOptions().setExceptionToBeThrownOnSOAPFault(true);

            //Message context setting
            SOAPEnvelope env = getFactory(operationClient.getOptions().getSoapVersionURI()).getDefaultEnvelope();
            messageContext = new MessageContext();
            env.getBody().addChild(request);
            messageContext.setEnvelope(env);
			
            operationClient.addMessageContext(messageContext);
           
			if (nonBlocking)
				operationClient.execute(false);
			else{
				operationClient.execute(true);
				MessageContext returnMessage = operationClient.getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
				SOAPEnvelope returnEnv = returnMessage.getEnvelope();
				returnEnv.getBody().toString();
				result = returnEnv.getBody().getFirstElement();
			}
				
			
		
		} catch (AxisFault e) {
			e.printStackTrace();
			throw new WsClientException("Error calling the web service: " + " Host: " + wsHost + " ServiceName: " + serviceName + " Operation Name: " + operationName + " Payload: " + payload + " Exception message: " + e.getMessage());
			
		} catch (Exception e){
			e.printStackTrace();
			throw new WsClientException("Error in the web servis method: + " + wsHost + " ServiceName: " + serviceName + " Operation Name: " + operationName + " Payload: " + payload + " Exception message: " + e.getMessage());
		}finally{
			if (messageContext !=null && messageContext.getTransportOut() != null) {
                try {
					messageContext.getTransportOut().getSender().cleanup(messageContext);
				} catch (AxisFault e) {
					throw new WsClientException("Error cleaning up connection: + " + wsHost + " ServiceName: " + serviceName + " Operation Name: " + operationName + " Payload: " + payload + " Exception message: " + e.getMessage());
				}
          }
		}
	
		
		
		return result;
	}
	
	/**
	 * initialization of the connection parameters
	 * @param configPath absolute path of the config file
	 * @throws WsClientException
	 */
	public  void init(String configPath) throws WsClientException{
		
		try {
			clientConfigContext = ConfigurationContextFactory.createConfigurationContextFromFileSystem(configPath);
			HttpConnectionManagerParams params = new HttpConnectionManagerParams();
			Parameter defaultMaxConnPerHostParam = clientConfigContext
					.getAxisConfiguration().getParameter(
							"defaultMaxConnPerHost");
			int defaultMaxConnPerHost = 500;
			if (defaultMaxConnPerHostParam != null) {
				defaultMaxConnPerHost = Integer
						.parseInt((String) defaultMaxConnPerHostParam
								.getValue());
			}
			params.setDefaultMaxConnectionsPerHost(defaultMaxConnPerHost);

			// Set the max total connections
			int maxTotalConnections = 15000;
			Parameter maxTotalConnectionsParam = clientConfigContext
					.getAxisConfiguration().getParameter("maxTotalConnections");
			if (maxTotalConnectionsParam != null) {
				maxTotalConnections = Integer
						.parseInt((String) maxTotalConnectionsParam.getValue());
			}
			params.setMaxTotalConnections(maxTotalConnections);

			params.setSoTimeout(600000);
			params.setConnectionTimeout(600000);
			
			
			connectionManager.setParams(params);
			 clientConfigContext.setProperty(HTTPConstants.MULTITHREAD_HTTP_CONNECTION_MANAGER,
					 connectionManager);
			 if(service.getName() == null)
				 service.setName("");
			 client = new ServiceClient(clientConfigContext,service);
			 isInit = true;
		} catch (AxisFault e) {
			e.printStackTrace();
			throw new WsClientException("Cannot initialize service client: "
					+ e.getMessage());
		}
		
		
	}

}
