package com.hand.kinggrid;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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


/**
 * 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){
        if(value==null || "".equals(value)){
            return this;
        }
        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 = new String(conn.getHeaderField("content-disposition").getBytes("ISO8859-1") ,this.charset);

        // 匹配文件名
        Pattern pattern = Pattern.compile(".*filename=(.*)");
        Matcher matcher = pattern.matcher(contentDisposition);

        System.out.println(matcher.groupCount()+"1");
        System.out.println(matcher.matches()+"2");
        System.out.println(matcher.group(1)+"3");
        String filename = matcher.group(1);
        String s=filename.substring(1,filename.length()-1);
        //      System.out.println(s);
        return filename.substring(1,filename.length()-1).replace("_1_saved.",".");
        //	return filename;

    }


    /**
     * 将响应结果写入输出流,主要用于接收服务返回的文件
     * @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;
    }


}