package com.bootdo.common.utils;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
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.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.zip.GZIPInputStream;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.lang3.StringUtils;

import com.alibaba.fastjson.JSONObject;

/**
 * 模块名称: 调第三方API工具类（支持HTTP和HTTPS）
 * 类名称：  WebUtils   
 * 类描述：  调第三方API工具类
 * 创建人：  陈力军   
 * 创建时间：2017年3月30日 
 * 修改备注：  
 * @version    
 * 
 */
public abstract class WebUtils {
	
	private static final String DEFAULT_CHARSET = "UTF-8";
	private static final String METHOD_POST = "POST";
	private static final String METHOD_GET = "GET";
	private static boolean ignoreSSLCheck = true;
	private static boolean ignoreHostCheck = true;

	/**
	 * Post请求
	 * @param url
	 * @param params
	 * @param connectTimeout
	 * @param readTimeout
	 * @return
	 * @throws IOException   
	*/
	public static String doPost(String url, Map<String, String> params, int connectTimeout, int readTimeout) throws IOException {
		return doPost(url, params, DEFAULT_CHARSET, connectTimeout, readTimeout);
	}

	/**
	 * 带参数编码Post请求
	 * @param url
	 * @param params
	 * @param charset
	 * @param connectTimeout
	 * @param readTimeout
	 * @return
	 * @throws IOException   
	*/
	public static String doPost(String url, Map<String, String> params, String charset, int connectTimeout, int readTimeout) throws IOException {
		return doPost(url, params, charset, connectTimeout, readTimeout, null);
	}

	/**
	 * 带参数编码及请求头Post请求
	 * @param url
	 * @param params
	 * @param charset
	 * @param connectTimeout
	 * @param readTimeout
	 * @param headerMap
	 * @return
	 * @throws IOException   
	*/
	public static String doPost(String url, Map<String, String> params, String charset, int connectTimeout, int readTimeout, Map<String, String> headerMap) throws IOException {
		String ctype = "application/x-www-form-urlencoded;charset=" + charset;
	    String query = buildQuery(params, charset);
	    byte[] content = new byte[0];
	    if (query != null){
	    	content = query.getBytes(charset);
	    }
	    return post(url, ctype, content, connectTimeout, readTimeout, headerMap);
	}

	/**
	 * 参数在请求体带参数编码及请求头Post请求
	 * @param url
	 * @param apiBody
	 * @param charset
	 * @param connectTimeout
	 * @param readTimeout
	 * @param headerMap
	 * @return
	 * @throws IOException   
	*/
	public static String doPost(String url, String apiBody, String charset, int connectTimeout, int readTimeout, Map<String, String> headerMap) throws IOException {
	    String ctype = "text/plain;charset=" + charset;
	    byte[] content = apiBody.getBytes(charset);
	    return post(url, ctype, content, connectTimeout, readTimeout, headerMap);
	}

	/**
	 * 参数在请求体带参数编码及请求头Post请求
	 * @param url
	 * @param ctype
	 * @param content
	 * @param connectTimeout
	 * @param readTimeout
	 * @return
	 * @throws IOException   
	*/
	public static String doPost(String url, String ctype, byte[] content, int connectTimeout, int readTimeout) throws IOException {
		return post(url, ctype, content, connectTimeout, readTimeout, null);
	}

	public static String doPost(String url, String ctype, byte[] content, int connectTimeout, int readTimeout, Map<String, String> headerMap) throws IOException {
		return post(url, ctype, content, connectTimeout, readTimeout, headerMap);
	}

	private static String post(String url, String ctype, byte[] content, int connectTimeout, int readTimeout, Map<String, String> headerMap) throws IOException {
	    HttpURLConnection conn = null;
	    OutputStream out = null;
	    String rsp = null;
	    try {
	    	conn = getConnection(new URL(url), METHOD_POST, ctype, headerMap);
	    	conn.setConnectTimeout(connectTimeout);
    		conn.setReadTimeout(readTimeout);
			out = conn.getOutputStream();
			out.write(content);
			rsp = getResponseAsString(conn);
	    } finally {
	    	if (out != null){
	    		out.close();
	    	}
	    	if (conn != null){
	    		conn.disconnect();
	    	}
	    }
	    return rsp;
	}

	/**
	 * 带附件Byte Post请求   
	 * @param url
	 * @param params
	 * @param fileParams
	 * @param charset
	 * @param connectTimeout
	 * @param readTimeout
	 * @param headerMap
	 * @return
	 * @throws IOException   
	*/
	public static String doPostVoice(String url, JSONObject params, byte[] fileBytes, String charset, int connectTimeout, int readTimeout, Map<String, String> headerMap) throws IOException {
	    return doPostWithFileByte(url, params, fileBytes, charset, connectTimeout, readTimeout, headerMap);
	}
	
	/**
	 * 带附件Post请求 
	 * @param url
	 * @param params
	 * @param fileParams
	 * @param charset
	 * @param connectTimeout
	 * @param readTimeout
	 * @param headerMap
	 * @return
	 * @throws IOException   
	*/
	private static String doPostWithFileByte(String url, JSONObject params, byte[] fileBytes, String charset, int connectTimeout, int readTimeout, Map<String, String> headerMap) throws IOException {
		String boundary = String.valueOf(System.nanoTime());
	    HttpURLConnection conn = null;
	    OutputStream out = null;
	    String rsp = null;
	    try {
	    	String ctype = "multipart/form-data; charset="+charset+" ;boundary=" + boundary;
	    	conn = getConnection(new URL(url), METHOD_POST, ctype, headerMap);
	    	out = conn.getOutputStream();
	    	String headerBoundary = "--" + boundary;
	    	byte[] headerBoundaryBytes = headerBoundary.getBytes(charset);
	    	out.write(headerBoundaryBytes);
	    	
	    	String endBoundary = "\r\n--" + boundary;
	    	byte[] endBoundaryBytes = endBoundary.getBytes(charset);
	    	byte[] paramsEntry = getTextEntry("json", JSONObject.toJSONString(params), charset);
	    	out.write(paramsEntry);
	    	out.write(endBoundaryBytes);
			byte[] fileEntryBytes = getFileEntry("file", "file", "application/octet-stream", charset);
			out.write(fileEntryBytes);
			out.write(fileBytes);
			out.write(endBoundaryBytes);
			out.flush();
			out.close();
	    	rsp = getResponseAsString(conn);
	    } finally {
	    	if (out != null){
	    		out.close();
	    	}
	    	if (conn != null){
	    		conn.disconnect();
	    	}
	    }
	    return rsp;
	}

	private static byte[] getTextEntry(String fieldName, String fieldValue, String charset) throws IOException {
	    StringBuilder entry = new StringBuilder();
	    entry.append("\r\nContent-Disposition:form-data;name=\"");
	    entry.append(fieldName);
	    entry.append("\"\r\nContent-Type:text/plain\r\n\r\n");
	    entry.append(fieldValue);
	    return entry.toString().getBytes(charset);
	}

	private static byte[] getFileEntry(String fieldName, String fileName, String mimeType, String charset) throws IOException {
	    StringBuilder entry = new StringBuilder();
	    entry.append("\r\nContent-Disposition:form-data; name=\"");
	    entry.append(fieldName);
	    entry.append("\"; filename=\"");
	    entry.append(fileName);
	    entry.append("\" Content-Type:");
	    entry.append(mimeType);
	    entry.append("\r\n\r\n");
	    return entry.toString().getBytes(charset);
	}

	public static String doGet(String url, Map<String, String> params) throws IOException {
		return doGet(url, params, DEFAULT_CHARSET);
	}

	/**
	 * GET请求
	 * 	不带消息头   
	 * @param url
	 * @param params
	 * @param charset
	 * @return
	 * @throws IOException   
	*/
	public static String doGet(String url, Map<String, String> params, String charset) throws IOException {
	    HttpURLConnection conn = null;
	    String rsp = null;
	    try {
	    	String ctype = "application/x-www-form-urlencoded;charset=" + charset;
	    	String query = buildQuery(params, charset);
	    	conn = getConnection(buildGetUrl(url, query), METHOD_GET, ctype, null);
	    	rsp = getResponseAsString(conn);
	    } finally {
	    	if (conn != null){
	    		conn.disconnect();
	    	}
	    }
	    return rsp;
	}
	
	/**
	 * GET请求
	 * 	带消息头   
	 * @param url
	 * @param params
	 * @param charset
	 * @return
	 * @throws IOException   
	*/
	public static String doGet(String url, Map<String, String> params, String charset, Map<String, String> headerMap) throws IOException {
	    HttpURLConnection conn = null;
	    String rsp = null;
	    try {
	    	String ctype = "application/x-www-form-urlencoded;charset=" + charset;
	    	String query = buildQuery(params, charset);
	    	conn = getConnection(buildGetUrl(url, query), METHOD_GET, ctype, headerMap);
	    	rsp = getResponseAsString(conn);
	    } finally {
	    	if (conn != null){
	    		conn.disconnect();
	    	}
	    }
	    return rsp;
	}

	private static HttpURLConnection getConnection(URL url, String method, String ctype, Map<String, String> headerMap) throws IOException {
		HttpURLConnection conn = (HttpURLConnection)url.openConnection();
		if (conn instanceof HttpsURLConnection) {
			HttpsURLConnection connHttps = (HttpsURLConnection)conn;
			if (ignoreSSLCheck){
				try {
					SSLContext ctx = SSLContext.getInstance("TLS");
					ctx.init(null, new TrustManager[] { new TrustAllTrustManager() }, new SecureRandom());
					connHttps.setSSLSocketFactory(ctx.getSocketFactory());
					connHttps.setHostnameVerifier(new HostnameVerifier() {
						@Override
						public boolean verify(String hostname, SSLSession session) {
							return true;
						} } );
				} catch (Exception e) {
					throw new IOException(e.toString());
				}
			}
			if (ignoreHostCheck){
				connHttps.setHostnameVerifier(new HostnameVerifier() {
					@Override
					public boolean verify(String hostname, SSLSession session) {
						return true;
					}
				});
			}
			conn = connHttps;
		}
		conn.setRequestMethod(method);
		conn.setDoInput(true);
		conn.setDoOutput(true);
		conn.setRequestProperty("Host", url.getHost());
//		conn.setRequestProperty("Accept", "text/xml,text/javascript");
		conn.setRequestProperty("Content-Type", ctype);
		if (headerMap != null){
			for (Entry entry : headerMap.entrySet()){
				conn.setRequestProperty((String)entry.getKey(), (String)entry.getValue());
			}
		}
		return conn;
	}

	private static URL buildGetUrl(String url, String query) throws IOException {
		if (StringUtils.isEmpty(query)) {
			return new URL(url);
		}
		return new URL(buildRequestUrl(url, new String[] { query }));
	}

	public static String buildRequestUrl(String url, String[] queries) {
		if ((queries == null) || (queries.length == 0)) {
			return url;
		}

		StringBuilder newUrl = new StringBuilder(url);
		boolean hasQuery = url.contains("?");
		boolean hasPrepend = (url.endsWith("?")) || (url.endsWith("&"));
		String[] array = queries; 
		int len$ = array.length; 
		for (int i = 0; i < len$; ++i) {
			String query = array[i];
			if (!(StringUtils.isEmpty(query))) {
		        if (!(hasPrepend)){
		        	if (hasQuery) {
		        		newUrl.append("&");
		        	} else {
		        		newUrl.append("?");
		        		hasQuery = true;
		        	}
		        }
		        newUrl.append(query);
		        hasPrepend = false;
			}
		}
		return newUrl.toString();
	}

	public static String buildQuery(Map<String, String> params, String charset) throws IOException {
	    if ((params == null) || (params.isEmpty())) {
	    	return null;
	    }
	    StringBuilder query = new StringBuilder();
	    Set<Entry<String, String>> entries = params.entrySet();
	    boolean hasParam = false;
	    for (Entry entry : entries) {
	    	String name = (String)entry.getKey();
	    	String value = (String)entry.getValue();
	    	if (StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
	    		if (hasParam)
	    			query.append("&");
				else {
					hasParam = true;
				}
    			query.append(name).append("=").append(URLEncoder.encode(value, charset));
	    	}
	    }
	    return query.toString();
	}

	protected static String getResponseAsString(HttpURLConnection conn) throws IOException {
		String charset = getResponseCharset(conn.getContentType());
		if (conn.getResponseCode() < 400) {
			String contentEncoding = conn.getContentEncoding();
			if ("gzip".equalsIgnoreCase(contentEncoding)){
				return getStreamAsString(new GZIPInputStream(conn.getInputStream()), charset);
			}
			return getStreamAsString(conn.getInputStream(), charset);
		}
		throw new IOException(conn.getResponseCode() + " " + conn.getResponseMessage());
	}

	public static String getStreamAsString(InputStream stream, String charset) throws IOException {
		Reader reader = null;
		try {
			reader = new InputStreamReader(stream, charset);
			StringBuilder response = new StringBuilder();
			char[] buff = new char[1024];
                int read = 0;
                while ((read = reader.read(buff)) > 0) {
                    response.append(buff, 0, read);
                }
                String str = response.toString();
                return str;
            } finally {
                if (stream != null){
                    stream.close();
			}
		}
	}

	public static String getResponseCharset(String ctype) {
		String[] array = null;
		int i;
		String charset = DEFAULT_CHARSET;
		if (!(StringUtils.isEmpty(ctype))) {
			String[] params = ctype.split(";");
			array = params;
			int len = array.length;
			for (i = 0; i < len; ++i) {
				String param = array[i];
				param = param.trim();
				if (param.startsWith("charset")) {
					String[] pair = param.split("=", 2);
					if ((pair.length != 2) || (StringUtils.isEmpty(pair[1]))){
						break;
					}
					charset = pair[1].trim();
					break;
				}
			}
		}
		return charset;
	}

	public static String decode(String value) {
		return decode(value, DEFAULT_CHARSET);
	}

	public static String encode(String value) {
		return encode(value, DEFAULT_CHARSET);
	}

	public static String decode(String value, String charset) {
		String result = null;
		if (!(StringUtils.isEmpty(value))){
			try {
				result = URLDecoder.decode(value, charset);
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		return result;
	}

	public static String encode(String value, String charset) {
		String result = null;
		if (!(StringUtils.isEmpty(value))){
			try {
				result = URLEncoder.encode(value, charset);
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		return result;
	}

	public static Map<String, String> splitUrlQuery(String query) {
		String[] array = null;
		int i = 0;
		Map result = new HashMap();
		String[] pairs = query.split("&");
		if ((pairs != null) && (pairs.length > 0)) {
			array = pairs;
			int len$ = array.length;
			for (i = 0; i < len$; ++i) {
				String pair = array[i];
				String[] param = pair.split("=", 2);
				if ((param != null) && (param.length == 2)){
					result.put(param[0], param[1]);
				}
			}
		}
		return result;
	}

	public static class TrustAllTrustManager implements X509TrustManager {
		@Override
		public X509Certificate[] getAcceptedIssuers() {
			return null;
		}
		@Override
		public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		}
		@Override
		public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		}
	}
	
	public static void setIgnoreSSLCheck(boolean ignoreSSLCheck){
		ignoreSSLCheck = ignoreSSLCheck;
	}

	public static void setIgnoreHostCheck(boolean ignoreHostCheck) {
		ignoreHostCheck = ignoreHostCheck;
	}

	public static void main(String[] args) throws Exception{
        Map<String, String> map = new HashMap<>();
        map.put("ticketSNO", "");
        long a1 = System.currentTimeMillis();
	    String a = WebUtils.doPost("http://10.19.19.52:8081/agent/getTokenWithTicket",map,20000,20000);
        long a2 = System.currentTimeMillis();
	    System.out.println("返回值 = " + a);
	    long t = a2 - a1;
	    System.out.println("耗時 = " + t);
	}
	}