package com.cyporj.until;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;


/**
 * Java SDK
 * 
 */
public class GeetestLib {

	protected final String verName = "3.3.0";// SDK版本编号
	protected final String sdkLang = "java";// SD的语言类型

	protected final String apiUrl = "http://api.geetest.com"; //极验验证API URL
	protected final String baseUrl = "api.geetest.com";
	
	protected final String registerUrl = "/register.php"; //register url
	protected final String validateUrl = "/validate.php"; //validate url

	/**
	 * 极验验证二次验证表单数据 chllenge
	 */
	public static final String fn_geetest_challenge = "geetest_challenge";
	
	/**
	 * 极验验证二次验证表单数据 validate
	 */
	public static final String fn_geetest_validate = "geetest_validate";
	
	/**
	 * 极验验证二次验证表单数据 seccode
	 */
	public static final String fn_geetest_seccode = "geetest_seccode";

	/**
	 * 公钥
	 */
	private String captchaId = "";

	/**
	 * 私钥
	 */
	
	private String privateKey = "";
	
	private String userId = "";

	private String responseStr = "";
	
	/**
	 * 调试开关，是否输出调试日志
	 */
	public boolean debugCode = false;
	
	/**
	 * 极验验证API服务状态Session Key
	 */
	public String gtServerStatusSessionKey = "gt_server_status";
	
	/**
	 * 带参数构造函数
	 * 
	 * @param captchaId
	 * @param privateKey
	 */
	public GeetestLib(String captchaId, String privateKey) {
		this.captchaId = captchaId;
		this.privateKey = privateKey;
	}
	
	/**
	 * 获取本次验证初始化返回字符串
	 * 
	 * @return 初始化结果
	 */
	public String getResponseStr() {
		return responseStr;
	}
	
	public String getVersionInfo() {
		return verName;
	}

	/**
	 * 预处理失败后的返回格式串
	 * 
	 * @return
	 */
	private String getFailPreProcessRes() {

		Long rnd1 = Math.round(Math.random() * 100);
		Long rnd2 = Math.round(Math.random() * 100);
		String md5Str1 = md5Encode(rnd1 + "");
		String md5Str2 = md5Encode(rnd2 + "");
		String challenge = md5Str1 + md5Str2.substring(0, 2);

		return String.format(
				"{\"success\":%s,\"gt\":\"%s\",\"challenge\":\"%s\"}", 0,
				this.captchaId, challenge);
	}

	/**
	 * 预处理成功后的标准串
	 * 
	 */
	private String getSuccessPreProcessRes(String challenge) {
		
		gtlog("challenge:" + challenge);
		return String.format(
				"{\"success\":%s,\"gt\":\"%s\",\"challenge\":\"%s\"}", 1,
				this.captchaId, challenge);
	}

	/**
	 * 验证初始化预处理
	 *
	 * @return 1表示初始化成功，0表示初始化失败
	 */
	public int preProcess() {

		if (registerChallenge() != 1) {
			
			this.responseStr = this.getFailPreProcessRes();
			return 0;
		}
		
		return 1;

	}
	
	/**
	 * 验证初始化预处理
	 *
	 * @param userid
	 * @return 1表示初始化成功，0表示初始化失败
	 */
	public int preProcess(String userid){
		
		this.userId = userid;
		return this.preProcess();
	}
	
	

	/**
	 * 用captchaID进行注册，更新challenge
	 * 
	 * @return 1表示注册成功，0表示注册失败
	 */
	private int registerChallenge() {
		try {
			String GET_URL = apiUrl + registerUrl+"?gt=" + this.captchaId;
			if (this.userId != ""){
				GET_URL = GET_URL + "&user_id=" + this.userId;
				this.userId = "";
			}
			gtlog("GET_URL:" + GET_URL);
			String result_str = readContentFromGet(GET_URL);
			gtlog("register_result:" + result_str);
			if (32 == result_str.length()) {

				this.responseStr = this.getSuccessPreProcessRes(this.md5Encode(result_str + this.privateKey));
				
				return 1;
			} else {
				gtlog("gtServer register challenge failed");
				return 0;
			}
		} catch (Exception e) {
			gtlog("exception:register api");
		}
		return 0;
	}

	/**
	 * 发送请求，获取服务器返回结果
	 * 
	 * @param getURL
	 * @return 服务器返回结果
	 * @throws IOException
	 */
	private String readContentFromGet(String getURL) throws IOException {

		URL getUrl = new URL(getURL);
		HttpURLConnection connection = (HttpURLConnection) getUrl
				.openConnection();

		connection.setConnectTimeout(2000);// 设置连接主机超时（单位：毫秒）
		connection.setReadTimeout(2000);// 设置从主机读取数据超时（单位：毫秒）

		// 建立与服务器的连接，并未发送数据
		connection.connect();
		
		// 发送数据到服务器并使用Reader读取返回的数据
		StringBuffer sBuffer = new StringBuffer();

		InputStream inStream = null;
		byte[] buf = new byte[1024];
		inStream = connection.getInputStream();
		for (int n; (n = inStream.read(buf)) != -1;) {
			sBuffer.append(new String(buf, 0, n, "UTF-8"));
		}
		inStream.close();
		connection.disconnect();// 断开连接

		return sBuffer.toString();
	}
	
	
	
	
	/**
	 * 判断一个表单对象值是否为空
	 * 
	 * @param gtObj
	 * @return
	 */
	protected boolean objIsEmpty(Object gtObj) {
		if (gtObj == null) {
			return true;
		}

		if (gtObj.toString().trim().length() == 0) {
			return true;
		}

		return false;
	}

	/**
	 * 检查客户端的请求是否合法,三个只要有一个为空，则判断不合法
	 * 
	 * @param request
	 * @return
	 */
	private boolean resquestIsLegal(String challenge, String validate, String seccode) {

		if (objIsEmpty(challenge)) {
			return false;
		}

		if (objIsEmpty(validate)) {
			return false;
		}

		if (objIsEmpty(seccode)) {
			return false;
		}

		return true;
	}
	
	
	/**
	 * 服务正常的情况下使用的验证方式,向gt-server进行二次验证,获取验证结果
	 * 
	 * @param challenge
	 * @param validate
	 * @param seccode
	 * @return 验证结果,1表示验证成功0表示验证失败
	 */
	public int enhencedValidateRequest(String challenge, String validate, String seccode) {	
		
		if (!resquestIsLegal(challenge, validate, seccode)) {
			return 0;
		}
		gtlog("request legitimate");
		
		String host = baseUrl;
		String path = validateUrl;
		int port = 80;
		String query = String.format("seccode=%s&sdk=%s", seccode,
				(this.sdkLang + "_" + this.verName));
		String response = "";
		
		if (this.userId != ""){
			query = query + "&user_id=" + this.userId;
			this.userId = "";
		}
		gtlog(query);
		try {
			if (validate.length() <= 0) {
				return 0;
			}

			if (!checkResultByPrivate(challenge, validate)) {
				return 0;
			}
			gtlog("checkResultByPrivate");
			response = postValidate(host, path, query, port);

			gtlog("response: " + response);
		} catch (Exception e) {
			e.printStackTrace();
		}

		gtlog("md5: " + md5Encode(seccode));

		if (response.equals(md5Encode(seccode))) {
			return 1;
		} else {
			return 0;
		}
	}
	
	/**
	 * 服务正常的情况下使用的验证方式,向gt-server进行二次验证,获取验证结果
	 * 
	 * @param challenge
	 * @param validate
	 * @param seccode
	 * @param userid
	 * @return 验证结果,1表示验证成功0表示验证失败
	 */
	public int enhencedValidateRequest(String challenge, String validate, String seccode, String userid) {	
		
		this.userId = userid;
		return this.enhencedValidateRequest(challenge, validate, seccode);
	}

	/**
	 * failback使用的验证方式
	 * 
	 * @param challenge
	 * @param validate
	 * @param seccode
	 * @return 验证结果,1表示验证成功0表示验证失败
	 */
	public int failbackValidateRequest(String challenge, String validate, String seccode) {

		gtlog("in failback validate");

		if (!resquestIsLegal(challenge, validate, seccode)) {
			return 0;
		}
		gtlog("request legitimate");

		String[] validateStr = validate.split("_");
		String encodeAns = validateStr[0];
		String encodeFullBgImgIndex = validateStr[1];
		String encodeImgGrpIndex = validateStr[2];

		gtlog(String.format(
				"encode----challenge:%s--ans:%s,bg_idx:%s,grp_idx:%s",
				challenge, encodeAns, encodeFullBgImgIndex, encodeImgGrpIndex));
			
		int decodeAns = decodeResponse(challenge, encodeAns);
		int decodeFullBgImgIndex = decodeResponse(challenge, encodeFullBgImgIndex);
		int decodeImgGrpIndex = decodeResponse(challenge, encodeImgGrpIndex);

		gtlog(String.format("decode----ans:%s,bg_idx:%s,grp_idx:%s", decodeAns,
				decodeFullBgImgIndex, decodeImgGrpIndex));

		int validateResult = validateFailImage(decodeAns,decodeFullBgImgIndex, decodeImgGrpIndex);

		return validateResult;
	}
	
	

	/**
	 *
	 * @param ans
	 * @param full_bg_index
	 * @param img_grp_index
	 * @return
	 */
	private int validateFailImage(int ans, int full_bg_index,
			int img_grp_index) {
		final int thread = 3;// 容差值

		String full_bg_name = md5Encode(full_bg_index + "").substring(0, 9);
		String bg_name = md5Encode(img_grp_index + "").substring(10, 19);

		String answer_decode = "";

		// 通过两个字符串奇数和偶数位拼接产生答案位
		for (int i = 0; i < 9; i++) {
			if (i % 2 == 0) {
				answer_decode += full_bg_name.charAt(i);
			} else if (i % 2 == 1) {
				answer_decode += bg_name.charAt(i);
			} else {
				gtlog("exception");
			}
		}

		String x_decode = answer_decode.substring(4, answer_decode.length());

		int x_int = Integer.valueOf(x_decode, 16);// 16 to 10

		int result = x_int % 200;
		if (result < 40) {
			result = 40;
		}

		if (Math.abs(ans - result) <= thread) {
			return 1;
		} else {
			return 0;
		}
	}
	
	
	
	
	/**
	 * 解码随机参数
	 * 
	 * @param encodeStr
	 * @param challenge
	 * @return
	 */
	private int decodeResponse(String challenge, String string) {
		if (string.length() > 100) {
			return 0;
		}

		int[] shuzi = new int[] { 1, 2, 5, 10, 50 };
		String chongfu = "";
		HashMap<String, Integer> key = new HashMap<String, Integer>();
		int count = 0;

		for (int i = 0; i < challenge.length(); i++) {
			String item = challenge.charAt(i) + "";

			if (chongfu.contains(item) == true) {
				continue;
			} else {
				int value = shuzi[count % 5];
				chongfu += item;
				count++;
				key.put(item, value);
			}
		}

		int res = 0;

		for (int j = 0; j < string.length(); j++) {
			res += key.get(string.charAt(j) + "");
		}

		res = res - decodeRandBase(challenge);

		return res;

	}

	/**
	 * 输入的两位的随机数字,解码出偏移量
	 * 
	 * @param randStr
	 * @return
	 */
	private int decodeRandBase(String challenge) {

		String base = challenge.substring(32, 34);
		ArrayList<Integer> tempArray = new ArrayList<Integer>();

		for (int i = 0; i < base.length(); i++) {
			char tempChar = base.charAt(i);
			Integer tempAscii = (int) (tempChar);

			Integer result = (tempAscii > 57) ? (tempAscii - 87)
					: (tempAscii - 48);

			tempArray.add(result);
		}

		int decodeRes = tempArray.get(0) * 36 + tempArray.get(1);
		return decodeRes;

	}
	
	

	/**
	 * 输出debug信息，需要开启debugCode
	 * 
	 * @param message
	 */
	public void gtlog(String message) {
		if (debugCode) {
			System.out.println("gtlog: " + message);
		}
	}

	protected boolean checkResultByPrivate(String challenge, String validate) {
		String encodeStr = md5Encode(privateKey + "geetest" + challenge);
		return validate.equals(encodeStr);
	}

	/**
	 * 貌似不是Post方式，后面重构时修改名字
	 * 
	 * @param host
	 * @param path
	 * @param data
	 * @param port
	 * @return
	 * @throws Exception
	 */
	protected String postValidate(String host, String path, String data,
			int port) throws Exception {
		String response = "error";
		
		InetAddress addr = InetAddress.getByName(host);
		Socket socket = new Socket(addr, port);
		BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(
				socket.getOutputStream(), "UTF8"));
		wr.write("POST " + path + " HTTP/1.0\r\n");
		wr.write("Host: " + host + "\r\n");
		wr.write("Content-Type: application/x-www-form-urlencoded\r\n");
		wr.write("Content-Length: " + data.length() + "\r\n");
		wr.write("\r\n"); // 以空行作为分割
		
		// 发送数据
		wr.write(data);
		wr.flush();
		
		// 读取返回信息
		BufferedReader rd = new BufferedReader(new InputStreamReader(
				socket.getInputStream(), "UTF-8"));
		String line;
		while ((line = rd.readLine()) != null) {
			response = line;
		}
		wr.close();
		rd.close();
		socket.close();
		return response;
	}


	/**
	 * md5 加密
	 * 
	 * @time 2014年7月10日 下午3:30:01
	 * @param plainText
	 * @return
	 */
	private String md5Encode(String plainText) {
		String re_md5 = new String();
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.update(plainText.getBytes());
			byte b[] = md.digest();
			int i;
			StringBuffer buf = new StringBuffer("");
			for (int offset = 0; offset < b.length; offset++) {
				i = b[offset];
				if (i < 0)
					i += 256;
				if (i < 16)
					buf.append("0");
				buf.append(Integer.toHexString(i));
			}

			re_md5 = buf.toString();

		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return re_md5;
	}

}
