package cn.hmsoft.pay.util;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.Set;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;

public class HttpClientHelper {

	private static final int TIMEOUT = 10 * 1000;
	private static CloseableHttpClient httpClient = null;
	private final static Object syncLock = new Object();
	public static CookieStore cookieStore = new BasicCookieStore();

	private HttpClientHelper() {

	}

	private static void config(HttpRequestBase base) {
		RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(TIMEOUT)
				.setConnectTimeout(TIMEOUT).setSocketTimeout(TIMEOUT).build();
		// base.setProtocolVersion(HttpVersion.HTTP_1_0);
		base.setConfig(requestConfig);
	}

	public static CloseableHttpClient getHttpClient(String url) {
		if (httpClient == null) {
			synchronized (syncLock) {
				if (httpClient == null) {
					httpClient = createHttpClient(200, 40, 100, url);
				}
			}
		}
		return httpClient;
	}

	public static CloseableHttpClient createHttpClient(int maxTotal, int maxPerRoute, int maxRoute, String url) {
		ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
		LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();
		Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", plainsf).register("https", sslsf).build();
		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
		// 将最大连接数增加
		cm.setMaxTotal(maxTotal);
		// 将每个路由基础的连接增加
		cm.setDefaultMaxPerRoute(maxPerRoute);
		HttpHost httpHost = new HttpHost(url);
		// 将目标主机的最大连接数增加
		cm.setMaxPerRoute(new HttpRoute(httpHost), maxRoute);

		// 请求重试处理
		HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
			public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
				if (executionCount >= 5) {// 如果已经重试了5次，就放弃
					return false;
				}
				if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了连接，那么就重试
					return true;
				}
				if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
					return false;
				}
				if (exception instanceof InterruptedIOException) {// 超时
					return false;
				}
				if (exception instanceof UnknownHostException) {// 目标服务器不可达
					return false;
				}
				if (exception instanceof ConnectTimeoutException) {// 连接被拒绝
					return false;
				}
				if (exception instanceof SSLException) {// SSL握手异常
					return false;
				}

				HttpClientContext clientContext = HttpClientContext.adapt(context);
				HttpRequest request = clientContext.getRequest();
				// 如果请求是幂等的，就再次尝试
				if (!(request instanceof HttpEntityEnclosingRequest)) {
					return true;
				}
				return false;
			}
		};

		CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm)
				.setRetryHandler(httpRequestRetryHandler).build();

		return httpClient;
	}

	private static void setPostParams(HttpPost httpost, Map<String, Object> params) {
		// List<NameValuePair> nvps = new ArrayList<NameValuePair>();
		Set<String> keySet = params.keySet();
		MultipartEntityBuilder builder = MultipartEntityBuilder.create();
		for (String key : keySet) {
			Object param = params.get(key);
			if (param.getClass().isArray()) {
				// 字符串数组
				Object[] arr = (Object[]) param;
				for (Object val : arr) {
					builder.addPart(key, new StringBody(val.toString(),
							ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), Consts.UTF_8)));
				}
			} else {
				// nvps.add(new BasicNameValuePair(key, params.get(key).toString()));
				builder.addPart(key, new StringBody(params.get(key).toString(),
						ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), Consts.UTF_8)));
			}
		}
		httpost.setEntity(builder.build());// new UrlEncodedFormEntity(nvps, "UTF-8")
	}

	/**
	 * post请求.
	 * 
	 * @param url
	 * @param params
	 * @return
	 * @throws IOException
	 */
	public static String post(String url, Map<String, Object> params) throws IOException {
		HttpPost httppost = new HttpPost(url);
		config(httppost);
		setPostParams(httppost, params);
		CloseableHttpResponse response = null;
		try {
			response = getHttpClient(url).execute(httppost, HttpClientContext.create());
			HttpEntity entity = response.getEntity();
			String result = EntityUtils.toString(entity, "utf-8");
			EntityUtils.consume(entity);
			return result;
		} catch (Exception e) {
			throw e;
		} finally {
			try {
				if (response != null) {
					response.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * post请求json.
	 * 
	 * @param url
	 * @param requestBody
	 * @param headers
	 * @return
	 * @throws IOException
	 */
	public static String postJson(String url, String requestBody, Map<String, Object> headers) throws IOException {
		HttpPost httppost = new HttpPost(url);
		config(httppost);
		StringEntity requestEntity = new StringEntity(requestBody, "utf-8");
		requestEntity.setContentEncoding("UTF-8");
		httppost.setHeader("Content-type", "application/json");
		httppost.setEntity(requestEntity);

		for (String key : headers.keySet()) {
			httppost.setHeader(key, headers.get(key).toString());
		}

		CloseableHttpResponse response = null;
		try {
			response = getHttpClient(url).execute(httppost, HttpClientContext.create());
			HttpEntity entity = response.getEntity();
			String result = EntityUtils.toString(entity, "utf-8");
			EntityUtils.consume(entity);
			return result;
		} catch (Exception e) {
			throw e;
		} finally {
			try {
				if (response != null) {
					response.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * get请求.
	 * 
	 * @param url
	 * @return
	 * @throws IOException
	 */
	public static String get(String url) throws IOException {
		HttpGet httpget = new HttpGet(url);
		config(httpget);
		CloseableHttpResponse response = null;
		try {
			response = getHttpClient(url).execute(httpget, HttpClientContext.create());
			HttpEntity entity = response.getEntity();
			String result = EntityUtils.toString(entity, "utf-8");
			EntityUtils.consume(entity);
			return result;
		} catch (IOException e) {
			throw e;
		} finally {
			try {
				if (response != null) {
					response.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * get请求.
	 * 
	 * @param url
	 * @param params
	 * @return
	 * @throws IOException
	 * @throws URISyntaxException
	 */
	public static String get(String url, Map<String, Object> params) throws IOException, URISyntaxException {
		CloseableHttpResponse response = null;
		try {
			final URIBuilder builder = new URIBuilder(url);
			for (String key : params.keySet()) {
				builder.setParameter(key, params.get(key).toString());
			}
			HttpGet httpget = new HttpGet(builder.build());
			config(httpget);
			response = getHttpClient(url).execute(httpget, HttpClientContext.create());
			HttpEntity entity = response.getEntity();
			String result = EntityUtils.toString(entity, "utf-8");
			EntityUtils.consume(entity);
			return result;
		} catch (IOException e) {
			throw e;
		} catch (URISyntaxException e) {
			throw e;
		} finally {
			try {
				if (response != null) {
					response.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public static void download(String url, Map<String, Object> params, String storePath) {
		CloseableHttpResponse response = null;
		try {
			final URIBuilder builder = new URIBuilder(url);
			for (String key : params.keySet()) {
				builder.setParameter(key, params.get(key).toString());
			}
			final HttpGet httpGet = new HttpGet(builder.build());
			response = getHttpClient(url).execute(httpGet);
			HttpEntity httpEntity = response.getEntity();
			InputStream is = httpEntity.getContent();
			/*
			 * ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer =
			 * new byte[4096]; int r = 0; while ((r = is.read(buffer)) > 0) {
			 * output.write(buffer, 0, r); } FileOutputStream fos = new
			 * FileOutputStream(filePath); output.writeTo(fos); output.flush();
			 * output.close(); fos.close();
			 */
			FileOutputStream fos = new FileOutputStream(storePath);
			IOUtils.copy(is, fos);
			fos.flush();
			fos.close();
			EntityUtils.consume(httpEntity);
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		} finally {
			try {
				response.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}