/**
 * Copyright 2008-2009 DRIVER PROJECT (ICM UW)
 * Original author: Marek Horst
 *
 * 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 eu.dnetlib.data.index.servlet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.security.InvalidParameterException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
 * WSDLProvider servlet. Takes two optional params: action = getWSDL|refreshWSDL and
 * type which is WSDL type, e.g. indexService or resultSetService. Default values are: 
 * action = getWSDL, type = indexService.
 * @author Marek Horst
 * @version 0.7.6
 *
 */
public class WSDLProvider extends HttpServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = -6811731784138030075L;
	
	protected static final Logger log = Logger.getLogger(WSDLProvider.class);
	
	/* params */
	public static final String PARAM_ACTION 			= "action";
	
	public static final String PARAM_TYPE 				= "type";
	
	/* params values */
	public static final String VALUE_ACTION_GET_WSDL 	= "getWSDL";
	
	public static final String VALUE_ACTION_REFRESH_WSDL= "refreshWSDL";
	
	public static final String VALUE_DEFAULT_ACTION 	= VALUE_ACTION_GET_WSDL;
	
	public static final String VALUE_DEFAULT_PARAM_TYPE	= "indexService";
	
	/* bean names */
	private static final String BEAN_WSDL_LOC_MAP		= "wsdlLocationMap";
	
	private static final String BEAN_SERVICE_LOC_MAP	= "serviceLocationMap";
	
	/* other */
	public static final String MASK_SERVICE_ADDRESS		= "${service.address}";
	
	
	/**
	 * Map containing wsdl type as a key and its location as a value.
	 */
	private Map<String, String> wsdlLocation;
	
	/**
	 * Map containing service location (without IP and port).
	 */
	private Map<String, String> serviceLocation;
	
	
	private Map<String, String> cachedWsdls = new HashMap<String, String>();
	
	

	/* (non-Javadoc)
	 * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		if (wsdlLocation == null) {
			WebApplicationContext appContext = WebApplicationContextUtils
					.getWebApplicationContext(config.getServletContext());
			wsdlLocation = (Map<String,String>) appContext
					.getBean(BEAN_WSDL_LOC_MAP);
		}
		if (serviceLocation == null) {
			WebApplicationContext appContext = WebApplicationContextUtils
					.getWebApplicationContext(config.getServletContext());
			serviceLocation = (Map<String,String>) appContext
					.getBean(BEAN_SERVICE_LOC_MAP);
		}
	}

	/* (non-Javadoc)
	 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		String paramAction = req.getParameter(PARAM_ACTION);
		String paramType = req.getParameter(PARAM_TYPE);
		if (paramAction==null)
			paramAction = VALUE_DEFAULT_ACTION;
		if (paramType==null)
			paramType = VALUE_DEFAULT_PARAM_TYPE;
		
		String currentServicePath = serviceLocation.get(paramType);
		if (currentServicePath==null)
			throw new InvalidParameterException("Couldn't find service path for " +
					paramType + " service!");
		
		if (paramAction.equals(VALUE_ACTION_GET_WSDL)) {
			String storedWSDLContent = getFromCache(paramType);
			if (storedWSDLContent!=null) {
//				replacing serviceLocation mask
				storedWSDLContent = storedWSDLContent.replace(MASK_SERVICE_ADDRESS, 
						buildServiceLocationString(req,currentServicePath));
				putMessageToResponse(storedWSDLContent, res);
				return;
			}
			String currentServiceWSDLLoc = wsdlLocation.get(paramType);
			if (currentServiceWSDLLoc==null)
				throw new InvalidParameterException("Couldn't find wsdl location for " +
						paramType + " service!");
			
			String wsdlContent = loadWSDLContent(currentServiceWSDLLoc);
			storeInCache(paramType, wsdlContent);
//			replacing serviceLocation mask
			wsdlContent = wsdlContent.replace(MASK_SERVICE_ADDRESS, 
					buildServiceLocationString(req,currentServicePath));
			putMessageToResponse(wsdlContent, res);
			return;
			
		} else if (paramAction.equals(VALUE_ACTION_REFRESH_WSDL)) {
			log.info("Removing WSDL from cache for type: " + paramType);
			removeFromCache(paramType);
			return;
		} else {
			throw new InvalidParameterException("Unsupported "+PARAM_ACTION+" parameter value: "+paramAction);
		}
	}

	/**
	 * Builds service location string dynamically.
	 * @param req
	 * @param currentServicePath
	 * @return generated service location for given HTTPServletRequest
	 */
	private String buildServiceLocationString(HttpServletRequest req, 
			String currentServicePath) {
		StringBuffer strBuffServiceLoc = new StringBuffer();
		strBuffServiceLoc.append("http://");
		strBuffServiceLoc.append(req.getServerName());
		strBuffServiceLoc.append(':');
		strBuffServiceLoc.append(req.getServerPort());
		if (!currentServicePath.startsWith("/"))
			strBuffServiceLoc.append('/');
		strBuffServiceLoc.append(currentServicePath);
		return strBuffServiceLoc.toString();
	}
	
	/**
	 * Puts message given as parameter to the response.
	 * 
	 * @param msg
	 * @param response
	 * @throws IOException
	 */
	private void putMessageToResponse(String msg, HttpServletResponse response)
			throws IOException {
		response.setContentType("text/xml");
		response.setCharacterEncoding("utf-8");
		PrintWriter out = response.getWriter();
		out.write(msg);
		out.write('\n');
		// out.close();
	}

	/**
	 * Loads WSDL content from location.
	 * @param location
	 * @return WSDL content
	 */
	private String loadWSDLContent(String location) {
		if (location==null)
			throw new InvalidParameterException("WSDL location cannot be null!");
		try {
			ClasspathResourceLoader cpResourceLoader = new ClasspathResourceLoader();
			InputStream is = cpResourceLoader.getResourceStream(location);
			if (is==null)
				throw new InvalidParameterException("Cannot find WSDL resource for location: "+location);
			BufferedReader reader = new BufferedReader(new InputStreamReader(is));
			StringBuffer strBuff = new StringBuffer();
			String line = null;
			while ((line=reader.readLine())!=null)
				strBuff.append(line);
			return strBuff.toString();
		} catch (IOException e) {
			throw new InvalidParameterException("Invalid WSDL location: "+location);
		} catch (ResourceNotFoundException e) {
			throw new InvalidParameterException("Invalid WSDL location: "+location);
		} 
	}
	
	/**
	 * Stores WSDL content in cache.
	 * @param wsdlName
	 * @param wsdlContent
	 */
	public void storeInCache(String wsdlName, String wsdlContent) {
		cachedWsdls.put(wsdlName, wsdlContent);
	}
	
	/**
	 * Returns WSDL from cache.
	 * @param wsdlName
	 * @return WSDL content stored in cache.
	 */
	public String getFromCache(String wsdlName) {
		return cachedWsdls.get(wsdlName);
	}
	
	/**
	 * Removes WSDL from cache.
	 * @param wsdlName
	 * @return WSDL removed from cache
	 */
	public String removeFromCache(String wsdlName) {
		return cachedWsdls.remove(wsdlName);
	}
	
	/* (non-Javadoc)
	 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		doGet(req, res);
	}

	/**
	 * Returns wsdl location map.
	 * @return wsdl location map
	 */
	public Map<String, String> getWsdlLocation() {
		return wsdlLocation;
	}

	/**
	 * Sets wsdl location map.
	 * @param wsdlLocation
	 */
	public void setWsdlLocation(Map<String, String> wsdlLocation) {
		this.wsdlLocation = wsdlLocation;
	}

	/**
	 * Returns service location map.
	 * @return service location map
	 */
	public Map<String, String> getServiceLocation() {
		return serviceLocation;
	}

	/**
	 * Sets service location map.
	 * @param serviceLocation
	 */
	public void setServiceLocation(Map<String, String> serviceLocation) {
		this.serviceLocation = serviceLocation;
	}

}
