package zmo.technology.lite.httpcli;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

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;

public abstract class ZeroHttpRequest {

	public class ZeroX509TrustManager implements X509TrustManager, HostnameVerifier {

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

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

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

		@Override
		public boolean verify(String s, SSLSession sslsession) {
			return true;
		}
	}

	protected List<String> requestReport;
	protected int connectTimeout;
	protected int readTimeout;
	private String responseType;

	protected ZeroHttpRequest() {
		super();
	}

	protected ZeroHttpRequest(int connectTimeout, int readTimeout) {
		super();
		this.connectTimeout = connectTimeout;
		this.readTimeout = readTimeout;
	}

	public ZeroHttpRequest setConnectTimeout(int connectTimeout) {
		this.connectTimeout = connectTimeout;
		return this;
	}

	public ZeroHttpRequest setReadTimeout(int readTimeout) {
		this.readTimeout = readTimeout;
		return this;
	}

	public String getResponseType() {
		return responseType;
	}

	protected String requestParams(Map<String, Object> params, boolean urlencode) throws UnsupportedEncodingException {
		StringBuffer requestParam = new StringBuffer();
		if (params != null && params.size() > 0) {
			int i = 0;
			for (Entry<String, Object> e : params.entrySet()) {
				requestParam.append(e.getKey());
				requestParam.append("=");
				if (urlencode)
					requestParam.append(URLEncoder.encode(e.getValue().toString(), "UTF-8"));
				else
					requestParam.append(e.getValue().toString());
				if (i != params.size() - 1)
					requestParam.append("&");
				i++;
			}
		}
		return requestParam.toString();
	}

	protected byte[] send(String method, URL requestURL, Map<String, String> headers, byte[] body, String charset)
			throws Exception {
		if (requestURL.toURI().toString().startsWith("https")) {
			SSLContext sslcontext = SSLContext.getInstance("SSL", "SunJSSE");
			sslcontext.init(null, new TrustManager[] { new ZeroX509TrustManager() }, new SecureRandom());
			HttpsURLConnection.setDefaultHostnameVerifier(new ZeroX509TrustManager());
			HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
		}

		requestReport = new ArrayList<>();
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
		Date open = new Date();
		requestReport.add("Connection Open Date : " + dateFormat.format(open));

		HttpURLConnection connect = (HttpURLConnection) requestURL.openConnection();

		connect.setConnectTimeout(connectTimeout);
		connect.setReadTimeout(readTimeout);
		connect.setRequestMethod(method);
		if (body != null)
			connect.setDoOutput(true);
		connect.setDoInput(true);
		connect.setUseCaches(false);

		for (Map.Entry<String, String> entry : headers.entrySet())
			connect.setRequestProperty(entry.getKey(), entry.getValue());

		requestReport.add("request URL : " + requestURL.toString());
		requestReport.add("request method : " + method);

		if (body != null) {
			requestReport.add("body : " + new String(body));
			requestReport.add("charset : " + charset);
			OutputStream outputStream = connect.getOutputStream();
			outputStream.write(body);
		}

		Date sendDate = new Date();
		requestReport.add("send date : " + dateFormat.format(sendDate));
		requestReport.add("use : " + (sendDate.getTime() - open.getTime()) + " millisecond");

		InputStream inputStream = null;
		try {
			inputStream = connect.getInputStream();
		} catch (Exception e) {
			e.printStackTrace();
			inputStream = connect.getErrorStream();
		}

		Date responseDate = new Date();
		requestReport.add("response date : " + dateFormat.format(responseDate));
		requestReport.add("use : " + (responseDate.getTime() - open.getTime()) + " millisecond");

		ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
		byte[] buff = new byte[1000];
		int rc = 0;
		while ((rc = inputStream.read(buff, 0, 1000)) > 0)
			swapStream.write(buff, 0, rc);
		byte[] bytes = swapStream.toByteArray();

		responseType = connect.getContentType();
		requestReport.add("Content-Type : " + responseType);
		
		if (connect.getHeaderField("Content-Length") == null)
			requestReport.add("Content-Length : " + bytes.length);
		else {
			int contentLength = Integer.parseInt(connect.getHeaderField("Content-Length"));
			requestReport.add("Content-Length : " + contentLength);
		}

		requestReport.add("response body : " + new String(bytes));
		connect.disconnect();

		return bytes;
	}

	protected byte[] post(String url, Map<String, String> headers, Map<String, Object> params, String charset,
			boolean urlencode)
			throws Exception {
		String requestParam = requestParams(params, urlencode);
		URL requestURL = new URL(url);
		return send("POST", requestURL, headers, requestParam.getBytes(), charset);
	}

	protected byte[] post(String url, Map<String, Object> params, String charset) throws Exception {
		Map<String, String> headers = new HashMap<>();
		headers.put("Content-Type", "application/x-www-form-urlencoded");
		return post(url, headers, params, charset, true);
	}

	protected byte[] post(String url, Map<String, Object> params) throws Exception {
		return post(url, params, "UTF-8");
	}

	protected byte[] get(String url, Map<String, String> headers, Map<String, Object> params, String charset,
			boolean urlencode) throws Exception {
		String requestParam = requestParams(params, urlencode);
		requestParam = requestParam.length() > 0 ? "?" + requestParam : requestParam;
		URL requestURL = new URL(url + requestParam);
		return send("GET", requestURL, headers, null, charset);
	}

	protected byte[] get(String url, Map<String, Object> params, String charset)
			throws Exception {
		Map<String, String> headers = new HashMap<>();
		headers.put("Content-Type", "application/x-www-form-urlencoded");
		return get(url, headers, params, charset, true);
	}

	protected byte[] get(String url, Map<String, Object> params) throws Exception {
		return get(url, params, "UTF-8");
	}
}
