package org.fouram.core.interceptor;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.connector.CoyoteOutputStream;
import org.apache.catalina.connector.OutputBuffer;
import org.apache.commons.lang.StringUtils;
import org.apache.tomcat.util.buf.ByteChunk;
import org.fouram.core.base.constants.SysConstants.ConstSession;
import org.fouram.core.util.AppUtil;
import org.fouram.core.util.AppUtil.ResultConstant;
import org.fouram.core.util.Token;
import org.fouram.core.util.Tools;
import org.fouram.core.util.UUIDUtil;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.alibaba.fastjson.JSONObject;

/**
 * 防止重复提交
 * 
 * @author liup
 *
 */
public class TokenHandlerInterceptor extends HandlerInterceptorAdapter {

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		if (handler instanceof HandlerMethod) {
			HandlerMethod handlerMethod = (HandlerMethod) handler;
			Method method = handlerMethod.getMethod();
			Token annotation = method.getAnnotation(Token.class);
			if (annotation != null) {
				boolean needSaveSession = annotation.save();
				if (needSaveSession) {
					request.getSession(false).setAttribute(ConstSession.SESSION_TOKEN, UUIDUtil.get32UUID());
				}
				boolean needRemoveSession = annotation.remove();
				if (needRemoveSession) {
					if (isRepeatSubmit(request)) {
						Tools.writeJson(response, AppUtil.error("重复提交，请稍后再试"));
						return false;
					}
				}
			}
			return true;
		} else {
			return super.preHandle(request, response, handler);
		}
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		if (handler instanceof HandlerMethod) {
			HandlerMethod handlerMethod = (HandlerMethod) handler;
			Method method = handlerMethod.getMethod();
			Token annotation = method.getAnnotation(Token.class);
			if (annotation != null) {
				boolean needRemoveSession = annotation.remove();
				if (needRemoveSession) {
					String resCode = getResponseResult(response);
					if(StringUtils.isNotBlank(resCode) && ResultConstant.SUCCESS.equals(resCode)){
						request.getSession(false).removeAttribute(ConstSession.SESSION_TOKEN);
					}
				}
			}
		}
		super.afterCompletion(request, response, handler, ex);
	}

	private String getResponseResult(HttpServletResponse response) {
		// 截取响应流
		try {
			CoyoteOutputStream os = (CoyoteOutputStream) response.getOutputStream();
			// 取到流对象对应的Class对象
			Class<CoyoteOutputStream> c = CoyoteOutputStream.class;
			// 取出流对象中的OutputBuffer对象，该对象记录响应到客户端的内容
			Field fs = c.getDeclaredField("ob");
			if (fs.getType().toString().endsWith("OutputBuffer")) {
				fs.setAccessible(true);// 设置访问ob属性的权限
				OutputBuffer ob = (OutputBuffer) fs.get(os);// 取出ob
				Class<OutputBuffer> cc = OutputBuffer.class;
				Field ff = cc.getDeclaredField("outputChunk");// 取到OutputBuffer中的输出流
				ff.setAccessible(true);
				if (ff.getType().toString().endsWith("ByteChunk")) {
					ByteChunk bc = (ByteChunk) ff.get(ob);// 取到byte流
					byte[] src = bc.getBytes();
					byte[] destPos = subBytes(src, 0, bc.getLength());
					String res = new String(destPos, "UTF-8");// 最终的值
					return JSONObject.parseObject(res).getString("result");
				}
			}
		} catch (Exception e) {
		}
		return null;
	}

	public static byte[] subBytes(byte[] src, int begin, int count) {
		byte[] bs = new byte[count];
		for (int i = begin; i < begin + count; i++)
			bs[i - begin] = src[i];
		return bs;
	}

	private boolean isRepeatSubmit(HttpServletRequest request) {
		String serverToken = (String) request.getSession(false).getAttribute(ConstSession.SESSION_TOKEN);
		if (serverToken == null) {
			return true;
		}
		String clinetToken = request.getParameter("token");
		if (clinetToken == null) {
			return true;
		}
		if (!serverToken.equals(clinetToken)) {
			return true;
		}
		return false;
	}
}