package com.jeeplus.modules.esignature.utils;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * HTTP 简单封装
 * @author Administrator
 *
 */
public class HttpRequest {
	
	/**
	 * https 域名校验
	 */
	private class TrustAnyHostnameVerifier implements HostnameVerifier {
		public boolean verify(String hostname, SSLSession session) {
			return true;
		}
	}
	
	/**
	 * https 证书管理
	 */
	private class TrustAnyTrustManager implements X509TrustManager {
		public X509Certificate[] getAcceptedIssuers() {
			return null;  
		}
		public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		}
		public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		}
	}
	
	private static String CHARSET = "UTF-8";
	
	private static final SSLSocketFactory sslSocketFactory = initSSLSocketFactory();
	private static final TrustAnyHostnameVerifier trustAnyHostnameVerifier = new HttpRequest(null).new TrustAnyHostnameVerifier();
	
	private static SSLSocketFactory initSSLSocketFactory() {
		try {
			TrustManager[] tm = {new HttpRequest(null).new TrustAnyTrustManager() };
			SSLContext sslContext = SSLContext.getInstance("TLS");	// ("TLS", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			return sslContext.getSocketFactory();
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	
	private static String end = "\r\n";
	private static String PREFIX = "--";
	
	public String url;
	
	private String requestMethod = "POST";
	
	
	private int readTimeout =30000;
	
	protected Map<String,String> headers = new LinkedHashMap<String, String>();
	protected Map<String , List<FileHolder>> fileMap =  new LinkedHashMap<String, List<FileHolder>>();

	protected Map<String,List<String>> params = new LinkedHashMap<String, List<String>>();

	HttpURLConnection conn;

	private int responseCode;

	private String charset = CHARSET;

	public HttpRequest(String url){
		this.url = url;
	}

	/**
	 * 添加header信息
	 * @param key
	 * @param value
	 */
	public HttpRequest addHeader(String key , String value){
		this.headers.put(key, value);
		return this;
	}

	public HttpRequest addHeaders(Map<String,String> maps){
		this.headers.putAll(maps);
		return this;
	}


	public String getCharset() {
		return charset;
	}

	public void setCharset(String charset) {
		this.charset = charset;
	}

	/**
	 * 添加参数
	 * @param key
	 * @param value
	 */
	public HttpRequest addParam(String key , String value){
		List<String> files = params.get(key);
		if(files == null){
			files = new ArrayList<String>();
		}
		files.add(value);
		this.params.put(key, files);
		return this;
	}



	public HttpRequest addInputStream(String key , String filename , InputStream is){
		List<FileHolder> files = fileMap.get(key);
		if(files == null){
			files = new ArrayList<FileHolder>();
		}
		files.add(new FileHolder(filename ,is ));
		this.fileMap.put(key, files);
		return this;
	}

	public HttpRequest addFile(String key , File file){
		List<FileHolder> files = fileMap.get(key);
		if(files == null){
			files = new ArrayList<FileHolder>();
		}
		files.add(new FileHolder(file));
		this.fileMap.put(key, files);
		return this;
	}


	private void writeFile(OutputStream out, InputStream fis , String name , String filename) throws IOException{

		out.write((PREFIX + boundary + end).getBytes(charset));
    	String contentDisposition = "Content-Disposition: form-data; name=\"" + name
                 + "\"; filename=\"" + filename + "\""+end;

    	out.write(contentDisposition.getBytes(charset));
    	out.write(("Content-Type: application/octet-stream"+end).getBytes(charset));
    	out.write(end.getBytes(charset));

		byte[] buffer = new byte[1024];
		for (int n = -1; (n = fis.read(buffer)) != -1;) {
			out.write(buffer, 0, n);
		}
    	out.write(end.getBytes(charset));

	}


	private void writeFiles(OutputStream out) throws IOException{
		Set<String> keySet = fileMap.keySet();
        for (Iterator<String> it = keySet.iterator(); it.hasNext();) {
            String name = it.next();
            List<FileHolder> value = fileMap.get(name);

            for (FileHolder fileHolder : value) {
            	
            	InputStream fis = null;
            	try{
            		fis = fileHolder.getInputStream();
            		writeFile(out, fis, name, fileHolder.getFilename());
            	}finally{
            		if(fis!=null){
            			try{
            				fis.close();
            			}catch(Exception e){
            				
            			}
            		}
            	}
			}
        }  
        out.write((PREFIX + boundary + PREFIX + end).getBytes(charset));
	}
	
	private void writeParams(OutputStream out) throws  IOException{
		Set<String> keySet = params.keySet();  
		StringBuffer sb = new StringBuffer();
		int i = 0;
        for (Iterator<String> it = keySet.iterator(); it.hasNext();) {  
            String name = it.next();  
            List<String> values = params.get(name);
            for (String string : values) {
            	if(i>0){
            		sb.append("&");
            	}
            	sb.append(name).append("=").append(URLEncoder.encode(string , charset));
            	i++;
			}
        }
        out.write(sb.toString().getBytes(charset));
	}
	
	
	private void writeParamsWithFile(OutputStream out) throws  IOException{
		Set<String> keySet = params.keySet();  
		StringBuffer sb = new StringBuffer();
        for (Iterator<String> it = keySet.iterator(); it.hasNext();) {  
            String name = it.next();  
            List<String> values = params.get(name);  
            for (String string : values) {
            	sb.append(PREFIX).append(boundary).append(end);//分界符  
                sb.append("Content-Disposition: form-data; name=\"" + name + "\"" + end);  
                //sb.append("Content-Type: text/plain; charset=" + charset + end);  
                sb.append(end);  
                sb.append(string);  
                sb.append(end);
			}
        }  
        out.write(sb.toString().getBytes(charset));
	}
	
	private String boundary = "----TosignFormBoundary5TGBNHY67UJM";
	
	
	
	public HttpRequest open() throws IOException{
		URL _url = new URL(url);
		conn = (HttpURLConnection)_url.openConnection();
		if (conn instanceof HttpsURLConnection) {
			((HttpsURLConnection)conn).setSSLSocketFactory(sslSocketFactory);
			((HttpsURLConnection)conn).setHostnameVerifier(trustAnyHostnameVerifier);
		}
		
		conn.setRequestMethod(this.requestMethod);
		conn.setDoInput(true); //允许输入流  
        conn.setDoOutput(true); //允许输出流  
        conn.setUseCaches(false); //不允许使用缓存  
		
		conn.setConnectTimeout(10000);
		conn.setReadTimeout(readTimeout);
		
		boolean hasUploadFile = !fileMap.isEmpty();
		
		if(hasUploadFile){
			conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);  
	    }else{
	    	conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded;charset=" + charset);
	    }
		
		conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36");
		if (headers != null && !headers.isEmpty())
			for (Entry<String, String> entry : headers.entrySet())
				conn.setRequestProperty(entry.getKey(), entry.getValue());
		
		return this;
		
	}
	
	
	public int getReadTimeout() {
		return readTimeout;
	}

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

	public HttpRequest write(byte[] data) throws IOException{
		OutputStream out = conn.getOutputStream();
		out.write(data);
		return this;
	}
	
	
	public HttpRequest request() throws IOException{
		boolean hasUploadFile = !fileMap.isEmpty();
		OutputStream out = conn.getOutputStream();
		if(hasUploadFile){
			writeParamsWithFile(out);
			writeFiles(out);
		}else{
			writeParams(out);
		}
		
		out.flush();
		out.close();
		responseCode = conn.getResponseCode();
		return this;
	}
	
	
	/**
	 * 发送请求
	 * @return 返回状态码 requestCode
	 * @throws IOException 
	 */
	public HttpRequest send() throws IOException{
		open();
		request();
		return this;
	}
	
	
	public int getResponseCode() {
		return responseCode;
	}

	
	/**
	 * 将响应结果以string返回
	 * @return
	 * @throws IOException
	 */
	public String resultToString() throws IOException{
		 String _charset = charset;
         String contentType = conn.getContentType();
         if(StrKit.notBlank(contentType)){
        	 Pattern pattern = Pattern.compile("charset=\\S*");  
        	 Matcher matcher = pattern.matcher(conn.getContentType());  
             if (matcher.find()) {  
            	 _charset = matcher.group().replace("charset=", "");  
             }  
         }
        
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		result(baos);
		return new String(baos.toByteArray() , _charset);
	}
	
	
	public String getDownFileName() throws IOException{
        String contentDisposition = URLDecoder.decode(conn.getHeaderField("content-Disposition"), "UTF-8");
        System.out.println(contentDisposition);
        // 匹配文件名
        Pattern pattern = Pattern.compile(".*filename=(.*)");
        Matcher matcher = pattern.matcher(contentDisposition);
        System.out.println(matcher.groupCount());
        System.out.println(matcher.matches());
        System.out.println(matcher.group(1));
        String filename = matcher.group(1);
		return filename.substring(1,filename.length()-1);
	}
	
	
	/**
	 * 将响应结果写入输出流，主要用于接收服务返回的文件
	 * @param os
	 * @throws IOException
	 */
	public void result(OutputStream os ) throws IOException{
		InputStream inputStream = null;
		try {
			if(getResponseCode() >=400 && getResponseCode()!=404){
				inputStream = conn.getErrorStream();
			}else{
				inputStream = conn.getInputStream();
			}
			
			byte[] buffer = new byte[2048];
			for (int n = -1; (n = inputStream.read(buffer)) != -1;) {
				os.write(buffer, 0, n);
				os.flush();
			}
		}
		
		finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					
				}
			}
			conn.disconnect();  
		}
	}

	public HttpURLConnection getConn() {
		return conn;
	}

	public String getRequestMethod() {
		return requestMethod;
	}

	public void setRequestMethod(String requestMethod) {
		this.requestMethod = requestMethod;
	}
	
	
}
