package org.bench4q.share.communication;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.log4j.Logger;
import org.bench4q.share.helper.ExceptionLog;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

@Component
public class HttpRequester {
	private String defaultContentEncoding;
	private HttpClient httpClient;

	private HttpClient getHttpClient() {
		return httpClient;
	}

	private void setHttpClient(HttpClient httpClient) {
		this.httpClient = httpClient;
	}

	public HttpRequester() {
		this.setDefaultContentEncoding("utf-8");
		this.setHttpClient(new HttpClient());
	}

	public String getDefaultContentEncoding() {
		return defaultContentEncoding;
	}

	public void setDefaultContentEncoding(String defaultContentEncoding) {
		this.defaultContentEncoding = defaultContentEncoding;
	}

	public HttpResponse sendPost(String url, Map<String, String> params,
			Map<String, String> properties) throws IOException {
		return this.send(url, "POST", params, "", properties);
	}

	public static boolean isInvalidResponse(HttpResponse httpResponse) {
		return httpResponse == null || httpResponse.getContent() == null;
	}

	public HttpResponse sendPostXml(String url, String contentString,
			Map<String, String> properties) throws IOException {
		if (properties == null) {
			properties = new HashMap<String, String>();
		}
		properties.put("Content-Type", "application/xml");
		return this.send(url, "POST", null, contentString, properties);
	}

	public HttpResponse sendGet(String url, Map<String, String> params,
			Map<String, String> properties) throws IOException {

		return this.send(url, "GET", params, "", properties);
	}

	public HttpResponse sendPutXml(String url, String content,
			Map<String, String> properties) throws IOException {
		if (properties == null) {
			properties = new HashMap<String, String>();
		}
		properties.put("Content-Type", "application/xml;charset=utf-8");
		return this.send(url, "PUT", null, content, properties);
	}

	public HttpResponse postFile(String url, String name, String filePath) {
		PostMethod postMethod = new PostMethod(url);
		try {
			FilePart filePart = new FilePart(name, new File(filePath));
			postMethod.setRequestEntity(new MultipartRequestEntity(
					new FilePart[] { filePart }, postMethod.getParams()));
			return buildResponseWithMethod(postMethod);
		} catch (Exception e) {
			e.printStackTrace();
			postMethod = null;
			return null;
		} finally {
			postMethod.releaseConnection();
		}
	}

	private HttpResponse buildResponseWithMethod(PostMethod postMethod)
			throws IOException {
		HttpResponse httpResponse = new HttpResponse();
		int statusCode = this.getHttpClient().executeMethod(postMethod);
		httpResponse.setCode(statusCode);
		httpResponse.setContent(postMethod.getResponseBodyAsString());
		httpResponse.setContentType(postMethod
				.getResponseHeader("Content-Type").equals(null) ? postMethod
				.getResponseHeader("Content-Type").getValue() : null);
		httpResponse.setContentEncoding(postMethod.getResponseCharSet());
		return httpResponse;
	}

	public HttpResponse postFiles(Map<String, String> headers, String url,
			String filePartName, List<File> files,
			Map<String, String> stringParts) {
		if (!url.startsWith("http"))
			url = "http://" + url;
		PostMethod postMethod = new PostMethod(url);

		try {
			if (headers != null) {
				for (Entry<String, String> entry : headers.entrySet()) {
					Header header = new Header(entry.getKey(), entry.getValue());
					postMethod.addRequestHeader(header);
				}

			}
			Part[] parts = new Part[files.size() + stringParts.size()];
			int i = 0;
			for (; i < files.size(); i++) {
				FilePart filePart = new FilePart(filePartName, files.get(i)
						.getName(), files.get(i));
				parts[i] = filePart;
			}
			for (Entry<String, String> entry : stringParts.entrySet()) {
				parts[i] = new StringPart(entry.getKey(), entry.getValue(),
						"utf-8");
				i++;
			}
			postMethod.setRequestEntity(new MultipartRequestEntity(parts,
					postMethod.getParams()));
			return buildResponseWithMethod(postMethod);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			postMethod.releaseConnection();
		}
	}

	public HttpResponse postFilesMulti(Map<String, String> headers, String url,
			String filePartName, MultipartFile[] multipartFiles,
			Map<String, String> stringParts) throws IOException {
		List<File> files = new LinkedList<File>();
		if (multipartFiles != null) {
			for (MultipartFile multipartFile : multipartFiles) {
				File file = new File(multipartFile.getOriginalFilename());
				multipartFile.transferTo(file);
				files.add(file);

			}
		}

		return postFiles(headers, url, filePartName, files, stringParts);

	}

	public HttpResponse sendDelete(String url, Map<String, String> parameters,
			Map<String, String> properties) throws IOException {
		return this.send(url, "DELETE", parameters, null, properties);
	}

	private HttpResponse send(String urlString, String method,
			Map<String, String> parameters, String Content,
			Map<String, String> propertys) throws IOException {
		HttpURLConnection urlConnection = null;

		if (method.equalsIgnoreCase("GET") && parameters != null) {
			StringBuffer param = new StringBuffer();
			int i = 0;
			for (String key : parameters.keySet()) {
				if (i == 0)
					param.append("?");
				else
					param.append("&");
				String encodedValue = URLEncoder.encode(parameters.get(key),
						"UTF-8");
				param.append(key).append("=").append(encodedValue);
				i++;
			}
			urlString += param;
		}

		if (!urlString.startsWith("http://")) {
			urlString = "http://" + urlString;
		}
		URL url = new URL(urlString);
		urlConnection = (HttpURLConnection) url.openConnection();
		urlConnection.setRequestMethod(method);
		urlConnection.setDoOutput(true);
		urlConnection.setDoInput(true);
		urlConnection.setUseCaches(false);
		if (propertys != null)
			for (String key : propertys.keySet()) {
				urlConnection.addRequestProperty(key, propertys.get(key));
			}

		if ((method.equalsIgnoreCase("POST") || method.equalsIgnoreCase("PUT"))
				&& parameters != null) {
			StringBuffer param = new StringBuffer();
			for (String key : parameters.keySet()) {
				param.append("&");
				String encodedValueString = URLEncoder.encode(
						parameters.get(key), "UTF-8");
				param.append(key).append("=").append(encodedValueString);
			}
			urlConnection.getOutputStream().write(param.toString().getBytes());
			urlConnection.getOutputStream().flush();
			urlConnection.getOutputStream().close();
		} else if ((method.equalsIgnoreCase("POST") || method
				.equalsIgnoreCase("PUT")) && !Content.isEmpty()) {
			urlConnection.getOutputStream().write(Content.getBytes("utf-8"));
			urlConnection.getOutputStream().flush();
			urlConnection.getOutputStream().close();
		}
		return this.makeContent(urlString, urlConnection);
	}

	private HttpResponse makeContent(String urlString,
			HttpURLConnection urlConnection) {
		InputStream in;
		try {
			in = urlConnection.getInputStream();
			return buildHttpResponse(urlConnection,
					readConnectionInputStream(in));
		} catch (IOException e) {
			Logger.getLogger(HttpRequester.class).info(
					ExceptionLog.getStackTrace(e));
			in = urlConnection.getErrorStream();
			try {
				return buildHttpResponse(urlConnection,
						readConnectionInputStream(in));
			} catch (Exception e1) {
				Logger.getLogger(HttpRequester.class).info(
						ExceptionLog.getStackTrace(e1));
				return getDefaultErrorResponse();
			}
		} finally {
			if (urlConnection != null) {
				urlConnection.disconnect();
			}
		}

	}

	private HttpResponse buildHttpResponse(HttpURLConnection urlConnection,
			StringBuffer temp) throws UnsupportedEncodingException, IOException {
		HttpResponse httpResponser = new HttpResponse();
		httpResponser.setUrlString(urlConnection.getURL().getPath());
		httpResponser.setDefaultPort(urlConnection.getURL().getDefaultPort());
		httpResponser.setPort(urlConnection.getURL().getPort());
		httpResponser.setProtocol(urlConnection.getURL().getProtocol());
		String ecod = urlConnection.getContentEncoding();
		if (ecod == null) {
			ecod = this.defaultContentEncoding;
		}
		httpResponser.setContent(new String(temp.toString().getBytes("utf-8"),
				ecod));
		httpResponser.setContentEncoding(ecod);
		httpResponser.setCode(urlConnection.getResponseCode());
		httpResponser.setMessage(urlConnection.getResponseMessage());
		httpResponser.setContentType(urlConnection.getContentType());
		httpResponser.setConnectTimeout(urlConnection.getConnectTimeout());
		httpResponser.setReadTimeout(urlConnection.getReadTimeout());
		return httpResponser;
	}

	private StringBuffer readConnectionInputStream(InputStream in)
			throws IOException {
		StringBuffer ret = new StringBuffer();
		BufferedReader bufferedReader = new BufferedReader(
				new InputStreamReader(in, "UTF-8"));
		String line = bufferedReader.readLine();
		while (line != null) {
			ret.append(line).append("\r\n");
			line = bufferedReader.readLine();
		}
		bufferedReader.close();
		return ret;
	}

	private HttpResponse getDefaultErrorResponse() {
		HttpResponse httpResponse = new HttpResponse();
		return httpResponse;
	}

	public static class HttpResponse {

		String urlString;

		int defaultPort;

		int port;

		String protocol;

		String contentEncoding;

		String content;

		String contentType;

		int code;

		String message;

		int connectTimeout;

		int readTimeout;

		public String getUrlString() {
			return urlString;
		}

		public void setUrlString(String urlString) {
			this.urlString = urlString;
		}

		public int getDefaultPort() {
			return defaultPort;
		}

		public void setDefaultPort(int defaultPort) {
			this.defaultPort = defaultPort;
		}

		public int getPort() {
			return port;
		}

		public void setPort(int port) {
			this.port = port;
		}

		public String getProtocol() {
			return protocol;
		}

		public void setProtocol(String protocol) {
			this.protocol = protocol;
		}

		public String getContentEncoding() {
			return contentEncoding;
		}

		public void setContentEncoding(String contentEncoding) {
			this.contentEncoding = contentEncoding;
		}

		public String getContent() {
			return content;
		}

		public void setContent(String content) {
			this.content = content;
		}

		public String getContentType() {
			return contentType;
		}

		public void setContentType(String contentType) {
			this.contentType = contentType;
		}

		public int getCode() {
			return code;
		}

		public void setCode(int code) {
			this.code = code;
		}

		public String getMessage() {
			return message;
		}

		public void setMessage(String message) {
			this.message = message;
		}

		public int getConnectTimeout() {
			return connectTimeout;
		}

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

		public int getReadTimeout() {
			return readTimeout;
		}

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

		public static String[] getStringArray(String content) {
			content = content.substring(1, content.length() - 1);
			return content.split(",");
		}

	}
}
