package cn.hmsoft.weixin.control;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.hmsoft.application.SpringHelper;
import cn.hmsoft.application.web.Ajax;
import cn.hmsoft.application.web.AjaxControl;
import cn.hmsoft.frame.constants.FrameStatus;
import cn.hmsoft.frame.exception.BusinessException;
import cn.hmsoft.frame.util.FrameAssertUtil;
import cn.hmsoft.helper.StringHelper;
import cn.hmsoft.weixin.constants.WxMessageType;
import cn.hmsoft.weixin.data.dao.WxUserLogDao;
import cn.hmsoft.weixin.data.model.WxDefine;
import cn.hmsoft.weixin.data.model.WxMsgProcessor;
import cn.hmsoft.weixin.data.model.WxUser;
import cn.hmsoft.weixin.entity.message.BasicMessage;
import cn.hmsoft.weixin.entity.reply.WeiXinReply;
import cn.hmsoft.weixin.entity.request.JsApiRequestJson;
import cn.hmsoft.weixin.entity.request.WxRequestEntity;
import cn.hmsoft.weixin.processor.AbstractProcessor;
import cn.hmsoft.weixin.util.WeiXinParamUtil;
import cn.hmsoft.weixin.util.WeiXinUtil;

/***************************
 * 和微信交互的Web请求
 * 
 * @author: Zhang XiaoQing
 * @date: 2016-08-13 17:51:26
 * @version: 2.0.0
 * @email: revisit@126.com
 * @Company: www.hmsoft.cn
 */
@RestController
public class WxPublicControl extends AjaxControl {

	// 纯微信登陆
	@RequestMapping("/weixin/user")
	public Ajax login(String app_id, String code) {
		// 微信用户
		FrameAssertUtil.isNotEmpty(code, "请在微信内访该本网页");
		if (StringHelper.isEmpty(app_id)) {
			if (WeiXinUtil.WeiXinDefineArray.size() != 1) {
				throw new BusinessException("请指定用户对应的微信号！");
			}
			app_id = WeiXinUtil.WeiXinDefineArray.get(0).getApp_id();
		}
		WxUser user = WeiXinUtil.findUser(app_id, code);
		return new Ajax(user);

	}

	@RequestMapping("/weixin/config")
	public Ajax config(String app_id, Integer wx_id, String url) {
		JsApiRequestJson json = null;
		if (StringHelper.isEmpty(app_id) && wx_id == null) {
			if (WeiXinUtil.WeiXinDefineArray.size() != 1) {
				throw new BusinessException("请指定用户对应的微信号！");
			}
			app_id = WeiXinUtil.WeiXinDefineArray.get(0).getApp_id();
		}
		if (wx_id != null)
			json = new JsApiRequestJson(wx_id, url);
		else
			json = new JsApiRequestJson(app_id, url);
		return new Ajax(json);

	}

	@RequestMapping("/weixin/gobal")
	public void gobal(HttpServletRequest request, HttpServletResponse response, WxRequestEntity entity)
			throws Exception {
		entity.setToken(WeiXinParamUtil.AccessTokenKey);
		// 每次都验证是否来自于微信的服务器
		try {
			if (!entity.verify()) {
				this.writeText("");
				return;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 如果是get方法,是作为地址验证请求
		if (request.getMethod().equalsIgnoreCase("get")) {
			// this.writeText(entity.getEchostr());
			try {
				response.getWriter().write(entity.getEchostr());
			} catch (Exception e) {
				e.printStackTrace();
			}
			return;
		}

		// 获取请求的参数
		// 1、获取用户发送的信息，把post过来的xml数据读取到字符串中，
		String xml_string = this.readRequestString(request);

		if (StringHelper.isEmpty(xml_string)) {
			this.writeText("");
			return;
		}
		// LogHelper.debug("接收到微信信息:" + xml_string);
		// 判断消息类型
		JAXBContext jc = JAXBContext.newInstance(BasicMessage.class);
		Unmarshaller u = jc.createUnmarshaller();
		BasicMessage msg = null;
		try {
			msg = (BasicMessage) u.unmarshal(new StringReader(xml_string));
		} catch (Exception e) {
			this.writeText("");
			e.printStackTrace();
			return;
		}
		if (msg == null || StringHelper.isEmpty(msg.getMsgType())) {
			this.writeText("");
			return;
		}
		
		WxDefine wx_define = null;
		try {
			wx_define = WeiXinUtil.getWxDefineByCode(msg.getToUserName());
		} catch (Exception e) {
			System.out.println("App Code [" + msg.getToUserName() + "] not define!");
			this.writeText("");
			return;
		}

		WxMsgProcessor msgProcessor = null;
		if (WxMessageType.Event.equals(msg.getMsgType())) {
			msgProcessor = new WxMsgProcessor();
			msgProcessor.setMsg_type("event");
			msgProcessor.setProcess_class("cn.hmsoft.weixin.processor.EventProcessor");
			msgProcessor.setRun_thread(FrameStatus.InActive.toString());
		}

		else
			try {
				msgProcessor = WeiXinUtil.getProcessorClass(wx_define, msg.getMsgType());
			} catch (Exception e) {
				e.printStackTrace();
			}
		if (msgProcessor == null) {
			this.writeText("");
			return;
		}
		// 记录消息
		Date msg_time = SpringHelper.getBean(WxUserLogDao.class).logMessage(wx_define, msg, xml_string);

		// 如果是线程模式，直接返回
		AbstractProcessor<?> processor = null;
		try {

			processor = (AbstractProcessor<?>) Class.forName(msgProcessor.getProcess_class()).newInstance();
		} catch (Exception e) {
			 e.printStackTrace();
		}
		if (processor == null) {
			// System.out.println(msgProcessor.getProcess_class()+"："+xml_string);
			this.writeText("");
			return;
		}
		processor.setEntity(wx_define, msg_time, msg, xml_string);
		if (FrameStatus.Active.toString().equals(msgProcessor.getRun_thread())) {
			// 启动线程
			new Thread(processor).start();
			this.writeText("");
			return;
		}

		WeiXinReply reply = processor.process();
		if (reply == null) {
			this.writeText("");
			return;
		}
		try {
			this.setPushMessage(msg, reply);
			// 如果返回的对象不为空,需要直接回复微信消息
			// 把类转换为xml字符串
			jc = JAXBContext.newInstance(reply.getClass());
			Marshaller m = jc.createMarshaller();
			// 是否格式化生成的xml串
			// m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
			// 是否省略xml头信息
			m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
			// 编码格式
			m.setProperty(Marshaller.JAXB_ENCODING, "GBK");
			// 理论上应该试用sun的类库处理特殊字符，但目前版本存在bug，临时办法处理，先放到字符串中，然后替换掉特殊字符
			StringWriter swriter = new StringWriter();
			m.marshal(reply, swriter);
			String returnxml_string = swriter.getBuffer().toString().replaceAll("&lt;", "<").replaceAll("&gt;", ">")
					.replaceAll("&amp;", "&");
			// 输出xml数据
			// LogHelper.info("回复消息:" + returnxml_string);
			this.writeText(returnxml_string);
		} catch (Exception e) {
			this.writeText("");

		}
	}

	private void setPushMessage(BasicMessage from, Object push) {
		Class<?> clazz = push.getClass();
		try {
			Field field = clazz.getDeclaredField("ToUserName");
			field.setAccessible(true);
			field.set(push, from.getFromUserName());

			field = clazz.getDeclaredField("FromUserName");
			field.setAccessible(true);
			field.set(push, from.getToUserName());
		} catch (Exception e) {
		}
		try {
			Field field = clazz.getDeclaredField("CreateTime");
			field.setAccessible(true);
			field.set(push, new Date().getTime() / 1000);
		} catch (Exception e) {

		}
	}

	// 从输入流读取post数据，注意防止乱码，utf-8格式
	private String readRequestString(HttpServletRequest request) {
		StringBuilder buffer = new StringBuilder();
		BufferedReader reader = null;
		try {
			// utf-8防止乱码
			reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "utf-8"));
			String line = null;
			while ((line = reader.readLine()) != null) {
				buffer.append(line);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != reader) {
				try {
					reader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return buffer.toString();
	}
}
