package com.uas.sso.common.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.logging.Logger;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.uas.sso.SSOConfig;

/**
 * <p>
 * HTTP工具类
 * </p>
 * 
 */
public class HttpUtil {

	private static final Logger logger = Logger.getLogger(HttpUtil.class.getName());

	/**
	 * 
	 * 允许 JS 跨域设置
	 * 
	 * <p>
	 * <!-- 使用 nginx 注意在 nginx.conf 中配置 -->
	 * 
	 * http { ...... add_header Access-Control-Allow-Origin *; ...... }
	 * </p>
	 * 
	 * <p>
	 * 非 ngnix 下，如果该方法设置不管用、可以尝试增加下行代码。
	 * 
	 * response.setHeader("Access-Control-Allow-Origin", "*");
	 * </p>
	 * 
	 * @param response
	 *            响应请求
	 */
	public static void allowJsCrossDomain(HttpServletResponse response) {
		response.setHeader("Access-Control-Allow-Credentials", "true");
		response.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS, POST, PUT, DELETE");
		response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
		response.setHeader("Access-Control-Max-Age", "3600");
	}

	/**
	 * 
	 * <p>
	 * 判断请求是否为 AJAX
	 * </p>
	 * 
	 * @param request
	 *            当前请求
	 * @return
	 */
	public static boolean isAjax(HttpServletRequest request) {
		return "XMLHttpRequest".equals(request.getHeader("X-Requested-With")) ? true : false;
	}

	/**
	 * 
	 * <p>
	 * AJAX 设置 response 返回状态
	 * </p>
	 * 
	 * @param response
	 * @param status
	 *            HTTP 状态码
	 * @param tip
	 */
	public static void ajaxStatus(HttpServletResponse response, int status, String tip) {
		try {
			response.setContentType("text/html;charset=" + SSOConfig.getSSOEncoding());
			response.setStatus(status);
			PrintWriter out = response.getWriter();
			out.print(tip);
			out.flush();
		} catch (IOException e) {
			logger.severe(e.toString());
		}
	}

	/**
	 * 
	 * <p>
	 * 获取当前 URL 包含查询条件
	 * </p>
	 * 
	 * @param request
	 * @param encode
	 *            URLEncoder编码格式
	 * @return
	 * @throws IOException
	 */
	public static String getQueryString(HttpServletRequest request, String encode) throws IOException {
		StringBuffer sb = new StringBuffer(request.getRequestURL());
		String query = request.getQueryString();
		if (query != null && query.length() > 0) {
			sb.append("?").append(query);
		}
		return URLEncoder.encode(sb.toString(), encode);
	}

	public static String getReferer(HttpServletRequest request, String encode) throws UnsupportedEncodingException {
		if (null != request.getHeader("Referer")){
			return URLEncoder.encode(request.getHeader("Referer"), encode);
		}
		return null;
	}

	/**
	 * 
	 * <p>
	 * getRequestURL是否包含在URL之内
	 * </p>
	 * 
	 * @param request
	 * @param url
	 *            参数为以';'分割的URL字符串
	 * @return
	 */
	public static boolean inContainURL(HttpServletRequest request, String url) {
		boolean result = false;
		if (url != null && !"".equals(url.trim())) {
			String[] urlArr = url.split(";");
			StringBuffer reqUrl = new StringBuffer(request.getRequestURL());
			for (int i = 0; i < urlArr.length; i++) {
				if (reqUrl.indexOf(urlArr[i]) > 1) {
					result = true;
					break;
				}
			}
		}
		return result;
	}

	/**
	 * <p>
	 * URLEncoder 返回地址
	 * </p>
	 * 
	 * @param url
	 *            跳转地址
	 * @param retParam
	 *            返回地址参数名
	 * @param retUrl
	 *            返回地址
	 * @return
	 */
	public static String encodeRetURL(String url, String retParam, String retUrl) {
		return encodeRetURL(url, retParam, retUrl, null);
	}

	/**
	 * <p>
	 * URLEncoder 返回地址
	 * </p>
	 * 
	 * @param url
	 *            跳转地址
	 * @param retParam
	 *            返回地址参数名
	 * @param retUrl
	 *            返回地址
	 * @param Map
	 *            携带参数
	 * @return
	 */
	public static String encodeRetURL(String url, String retParam, String retUrl, Map<String, String> data) {
		if (url == null) {
			return null;
		}

		StringBuffer retStr = new StringBuffer(url);
		retStr.append("?");
		retStr.append(retParam);
		retStr.append("=");
		try {
			retStr.append(URLEncoder.encode(retUrl, SSOConfig.getSSOEncoding()));
		} catch (UnsupportedEncodingException e) {
			logger.severe("encodeRetURL error." + url);
			e.printStackTrace();
		}

		if (data != null) {
			for (Map.Entry<String, String> entry : data.entrySet()) {
				retStr.append("&").append(entry.getKey()).append("=").append(entry.getValue());
			}
		}

		return retStr.toString();
	}

	/**
	 * <p>
	 * URLDecoder 解码地址
	 * </p>
	 * 
	 * @param url
	 *            解码地址
	 * @return
	 */
	public static String decodeURL(String url) {
		if (url == null) {
			return null;
		}
		String retUrl = "";

		try {
			retUrl = URLDecoder.decode(url, SSOConfig.getSSOEncoding());
		} catch (UnsupportedEncodingException e) {
			logger.severe("encodeRetURL error." + url);
			e.printStackTrace();
		}

		return retUrl;
	}

	/**
	 * <p>
	 * GET 请求
	 * </p>
	 * 
	 * @param request
	 * @return boolean
	 */
	public static boolean isGet(HttpServletRequest request) {
		if ("GET".equalsIgnoreCase(request.getMethod())) {
			return true;
		}
		return false;
	}

	/**
	 * <p>
	 * POST 请求
	 * </p>
	 * 
	 * @param request
	 * @return boolean
	 */
	public static boolean isPost(HttpServletRequest request) {
		if ("POST".equalsIgnoreCase(request.getMethod())) {
			return true;
		}
		return false;
	}

	/**
	 * 
	 * <p>
	 * 请求重定向至地址 location
	 * </p>
	 * 
	 * @param response
	 *            请求响应
	 * @param location
	 *            重定向至地址
	 */
	public static void sendRedirect(HttpServletResponse response, String location) {
		try {
			response.sendRedirect(location);
		} catch (IOException e) {
			logger.severe("sendRedirect location:" + location);
			e.printStackTrace();
		}
	}

	/**
	 * <p>
	 * 获取Request Playload 内容
	 * </p>
	 * 
	 * @param request
	 * @return Request Playload 内容
	 */
	public static String requestPlayload(HttpServletRequest request) throws IOException {
		StringBuilder stringBuilder = new StringBuilder();
		BufferedReader bufferedReader = null;
		try {
			InputStream inputStream = request.getInputStream();
			if (inputStream != null) {
				bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
				char[] charBuffer = new char[128];
				int bytesRead = -1;
				while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
					stringBuilder.append(charBuffer, 0, bytesRead);
				}
			} else {
				stringBuilder.append("");
			}
		} catch (IOException ex) {
			throw ex;
		} finally {
			if (bufferedReader != null) {
				try {
					bufferedReader.close();
				} catch (IOException ex) {
					throw ex;
				}
			}
		}
		return stringBuilder.toString();
	}

	/**
	 * <p>
	 * 获取当前完整请求地址
	 * </p>
	 * 
	 * @param request
	 * @return 请求地址
	 */
	public static String getRequestUrl(HttpServletRequest request) {
		StringBuffer url = new StringBuffer(request.getScheme());
		// 请求协议 http,https
		url.append("://");
		url.append(request.getHeader("host"));// 请求服务器
		url.append(request.getRequestURI());// 工程名
		if (request.getQueryString() != null) {
			// 请求参数
			url.append("?").append(request.getQueryString());
		}
		return url.toString();
	}

	public static ResponseWrap doPost(String postUrl, String formData) throws Exception {
		return doPost(postUrl, formData, 30000);
	}

	public static ResponseWrap doPost(String postUrl, String formData, int timeout) throws Exception {
		if (postUrl.startsWith("https")) {
			return doHttpsPost(postUrl, formData, timeout);
		}
		return doHttpPost(postUrl, formData, timeout);
	}

	public static ResponseWrap doHttpPost(String postUrl, String formData, int timeout) throws Exception {
		URL url = new URL(postUrl);
		HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
		try {
			urlConn.setDoOutput(true);
			urlConn.setDoInput(true);
			urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
			urlConn.setUseCaches(false);
			urlConn.setInstanceFollowRedirects(true);
			urlConn.setRequestMethod("POST");
			urlConn.setConnectTimeout(timeout);
			urlConn.setReadTimeout(timeout);
			if (null != formData) {
				OutputStreamWriter osw = new OutputStreamWriter(urlConn.getOutputStream(), "UTF-8");
				osw.write(formData);
				osw.flush();
				osw.close();
			}
			return new ResponseWrap(urlConn.getResponseCode() == 200, streamToString(urlConn.getInputStream()));
		} catch (Exception e) {
			return new ResponseWrap(false, e.getMessage());
		} finally {
			if (urlConn != null) {
				urlConn.disconnect();
			}
		}
	}

	public static ResponseWrap doHttpsPost(String postUrl, String formData, int timeout) throws Exception {
		URL url = new URL(postUrl);
		HttpsURLConnection urlConn = null;
		try {
			SSLContext ctx = SSLContext.getInstance("TLS");
			ctx.init(new KeyManager[0], new TrustManager[] { new DefaultTrustManager() }, new SecureRandom());
			SSLContext.setDefault(ctx);
			urlConn = (HttpsURLConnection) url.openConnection();
			urlConn.setDoOutput(true);
			urlConn.setDoInput(true);
			urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
			urlConn.setUseCaches(false);
			urlConn.setInstanceFollowRedirects(true);
			urlConn.setRequestMethod("POST");
			urlConn.setConnectTimeout(timeout);
			urlConn.setReadTimeout(timeout);
			urlConn.setHostnameVerifier(new HostnameVerifier() {

				@Override
				public boolean verify(String hostname, SSLSession session) {
					return true;
				}
			});
			if (null != formData) {
				OutputStreamWriter osw = new OutputStreamWriter(urlConn.getOutputStream(), "UTF-8");
				osw.write(formData);
				osw.flush();
				osw.close();
			}
			return new ResponseWrap(urlConn.getResponseCode() == 200, streamToString(urlConn.getInputStream()));
		} catch (Exception e) {
			return new ResponseWrap(false, e.getMessage());
		} finally {
			if (urlConn != null) {
				urlConn.disconnect();
			}
		}
	}

	public static ResponseWrap doGet(String getUrl) throws Exception {
		return doGet(getUrl, 30000);
	}

	public static ResponseWrap doGet(String getUrl, int timeout) throws Exception {
		if (getUrl.startsWith("https")) {
			return doHttpsGet(getUrl, timeout);
		}
		return doHttpGet(getUrl, timeout);
	}

	public static ResponseWrap doHttpGet(String getUrl, int timeout) throws Exception {
		URL url = new URL(getUrl);
		HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
		try {
			urlConn.setDoOutput(true);
			urlConn.setDoInput(true);
			urlConn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
			urlConn.setUseCaches(false);
			urlConn.setInstanceFollowRedirects(true);
			urlConn.setRequestMethod("GET");
			urlConn.setConnectTimeout(timeout);
			urlConn.setReadTimeout(timeout);
			return new ResponseWrap(urlConn.getResponseCode() == 200, streamToString(urlConn.getInputStream()));
		} catch (Exception e) {
			return new ResponseWrap(false, e.getMessage());
		} finally {
			if (urlConn != null) {
				urlConn.disconnect();
			}
		}
	}

	public static ResponseWrap doHttpsGet(String getUrl, int timeout) throws Exception {
		URL url = new URL(getUrl);
		HttpsURLConnection urlConn = null;
		try {
			SSLContext ctx = SSLContext.getInstance("TLS");
			ctx.init(new KeyManager[0], new TrustManager[] { new DefaultTrustManager() }, new SecureRandom());
			SSLContext.setDefault(ctx);
			urlConn = (HttpsURLConnection) url.openConnection();
			urlConn.setDoOutput(true);
			urlConn.setDoInput(true);
			urlConn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
			urlConn.setUseCaches(false);
			urlConn.setInstanceFollowRedirects(true);
			urlConn.setRequestMethod("GET");
			urlConn.setConnectTimeout(timeout);
			urlConn.setReadTimeout(timeout);
			urlConn.setHostnameVerifier(new HostnameVerifier() {

				@Override
				public boolean verify(String hostname, SSLSession session) {
					return true;
				}
			});
			return new ResponseWrap(urlConn.getResponseCode() == 200, streamToString(urlConn.getInputStream()));
		} catch (Exception e) {
			return new ResponseWrap(false, e.getMessage());
		} finally {
			if (urlConn != null) {
				urlConn.disconnect();
			}
		}
	}

	public static String streamToString(InputStream in) throws Exception {
		BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
		StringBuilder buf = new StringBuilder();

		try {
			char[] chars = new char[2048];
			for (;;) {
				int len = reader.read(chars, 0, chars.length);
				if (len < 0) {
					break;
				}
				buf.append(chars, 0, len);
			}
		} catch (Exception ex) {
			throw new Exception("read string from reader error", ex);
		}

		return buf.toString();
	}

	public static ResponseWrap doPost(String postUrl, Map<String, Object> data) throws Exception {
		return doPost(postUrl, getFormData(data));
	}

	public static ResponseWrap doPost(String postUrl, Map<String, Object> data, int timeout) throws Exception {
		return doPost(postUrl, getFormData(data), timeout);
	}

	public static ResponseWrap doGet(String getUrl, Map<String, Object> data) throws Exception {
		return doGet(getUrl + (getUrl.contains("?") ? "&" : "?") + getFormData(data));
	}

	public static ResponseWrap doGet(String getUrl, Map<String, Object> data, int timeout) throws Exception {
		return doGet(getUrl + (getUrl.contains("?") ? "&" : "?") + getFormData(data), timeout);
	}

	public static ResponseWrap doHttpsPost(String postUrl, Map<String, Object> data, int timeout) throws Exception {
		return doHttpsPost(postUrl, getFormData(data), timeout);
	}

	private static String getFormData(Map<String, Object> data) throws Exception {
		StringBuffer sb = new StringBuffer();
		for (String key : data.keySet())
			if (data.get(key) != null)
				sb.append(URLEncoder.encode(key, "UTF-8")).append("=")
						.append(URLEncoder.encode(data.get(key).toString(), "UTF-8")).append("&");
		return sb.substring(0, sb.length() - 1).toString();
	}

	public static class ResponseWrap {
		private boolean success;
		private String content;

		public ResponseWrap(boolean success, String content) {
			super();
			this.success = success;
			this.content = content;
		}

		public boolean isSuccess() {
			return success;
		}

		public void setSuccess(boolean success) {
			this.success = success;
		}

		public String getContent() {
			return content;
		}

		public void setContent(String content) {
			this.content = content;
		}
	}

	private static class DefaultTrustManager implements X509TrustManager {

		@Override
		public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
		}

		@Override
		public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
		}

		@Override
		public X509Certificate[] getAcceptedIssuers() {
			return null;
		}

	}
}
