package com.platform.modules.trtc.socket;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.platform.common.utils.DateUtils;
import com.platform.common.utils.SpringContextUtils;
import com.platform.modules.trtc.service.TrtcService;

import net.sf.json.JSONObject;

/**
 * 
 * 虽然@Component默认是单例模式的，但是spring boot
 * 还是会为每个websocket连接初始化一个bean,所以这里使用一个静态的set保存spring boot
 * 
 * 创建的bean--MyWebSocket.
 * 
 * @author weiyan
 * @date 2019-07-27
 */
@ServerEndpoint(value = "/websocket/{uid}") // websocket连接点映射.
@Component
public class WebSocket {

	private Logger log = LoggerFactory.getLogger(getClass());

	// 用来存储每个客户端对应的MyWebSocket对象.
	private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<WebSocket>();
	// 用来记录userId和该session之间的绑定关系.
	private static Map<Long, Session> map = new HashMap<Long, Session>();
	
	private Session session;// 当前会话的session.
	private Long userId;// 用户ID
	
	private TrtcService trtcService; 

	/**
	 * 成功建立连接调用的方法.
	 */
	@OnOpen
	public void onOpen(Session session, @PathParam("uid") String uid) {
		log.info("connection open : {}", uid);
		if(uid=="" || uid.equals("") || uid==null || uid.equals(null) || uid=="null" || uid.equals("null") || uid=="undefined" || uid.equals("undefined")){
			return;
		}
		this.session = session;
		this.userId = Long.valueOf(uid);
		this.trtcService = (TrtcService) SpringContextUtils.getBean("trtcService");	
	}

	/**
	 * 连接关闭调用的方法.
	 */
	@OnClose
	public void onClose(Session session, @PathParam("uid") String uid) {
		log.info("connection close : {}", uid);
		if(uid=="" || uid.equals("") || uid==null || uid.equals(null) || uid=="null" || uid.equals("null") || uid=="undefined" || uid.equals("undefined")){
			return;
		}
		this.userId = Long.valueOf(uid);
		webSocketSet.remove(session);
//		map.remove(userId);
	}

	/**
	 * 收到客户端消息后调用的方法.
	 */
	@OnMessage
	public void onMessage(String message, Session session, @PathParam("uid") String uid) {

		log.info("receive message, uid : {}, message : {}", uid, message);
		// message 不是普通的string ，而是我们定义的SocketMsg json字符串.
		try {
			if(uid=="" || uid.equals("") || uid==null || uid.equals(null) || uid=="null" || uid.equals("null") || uid=="undefined" || uid.equals("undefined")){
				return;
			}
			this.userId = Long.valueOf(uid);
			
			
			SocketMsg socketMsg = new ObjectMapper().readValue(message, SocketMsg.class);

			log.info("websocket::::::::websocket::::::::"+socketMsg.getFromUserId()+"-----------angle:"+socketMsg.getAngle()+"-----------"+socketMsg.getType()+"----touserId:"+socketMsg.getToUserId()+"---------------"+DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
			// 单聊.
			if (socketMsg.getType() >= 10002 && socketMsg.getType()<=10009) {
				log.info("p 2 p, {} to {}", socketMsg.getFromUserId(), socketMsg.getToUserId());
				// 单聊：需要找到发送者和接受者即可.
				//socketMsg.setFromUserId(userId);// 发送者.
				// socketMsg.setToUser(toUser);//这个是由客户端进行设置.
				Session toSession = map.get(socketMsg.getToUserId());// 用于处理接收者打开多个窗口
				Integer state = trtcService.getReporterOnLineRedisById(userId);
//				System.out.println("websocket::::::::"+userId+"-----------state:::::::"+state+"-----------"+"formSessionId:::::"+session.getId()+"---toSessionId:::::"+toSession.getId()+"----------"+socketMsg.getType()+"------------"+DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
				if (toSession != null && toSession.getId()!=null && state!=0 && !state.equals("0")) {
					synchronized(toSession){
						try {
							toSession.getBasicRemote().sendText(JSONObject.fromObject(socketMsg).toString());
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				} else {
					log.info("对方不在线或正在通话中");
					socketMsg.setType(99999);
					synchronized(session){
						try {
							//System.out.println("formSessionId:::::"+session.getId()+"---toSessionId:::::"+toSession.getId()+"----------"+socketMsg.getType()+"------------"+DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
							session.getBasicRemote().sendText(JSONObject.fromObject(socketMsg).toString());
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
				if(socketMsg.getType() == 10004){
					log.info("发起方发起通话邀请");
					socketMsg.setType(10012);
					broadcast(socketMsg, userId);
					trtcService.updateReporterStatusRedisById(userId, 2);
					trtcService.updateReporterStatusRedisById(socketMsg.getToUserId(), 2);
					socketMsg.setFromUserId(socketMsg.getToUserId());
					socketMsg.setFromUserName(socketMsg.getToUserName());
					socketMsg.setFromUserImg(socketMsg.getToUserImg());
					socketMsg.setToUserId(userId);
					socketMsg.setToUserName("");
					socketMsg.setToUserImg("");
					broadcast(socketMsg, socketMsg.getToUserId());
				}else if(socketMsg.getType() == 10005 || socketMsg.getType() == 10007 || socketMsg.getType() == 10008 || socketMsg.getType() == 10009){
					log.info("10005   发起方中断通话邀请\n" +
							"10006   被邀请方接受通话邀请\n" +
							"10007   被邀请发拒绝通话邀请\n" +
							"10008   发起方中断通话\n" +
							"10009   被邀请方中断通话");
					socketMsg.setType(10010);
					broadcast(socketMsg, userId);
					trtcService.updateReporterStatusRedisById(userId, 1);
					trtcService.updateReporterStatusRedisById(socketMsg.getToUserId(), 1);
					socketMsg.setFromUserId(socketMsg.getToUserId());
					socketMsg.setFromUserName(socketMsg.getToUserName());
					socketMsg.setFromUserImg(socketMsg.getToUserImg());
					socketMsg.setToUserId(userId);
					socketMsg.setToUserName("");
					socketMsg.setToUserImg("");
					broadcast(socketMsg, socketMsg.getToUserId());
				}
			} else if (socketMsg.getType() == 10000){//上线
				log.info("用户登录：{}", userId);
				Integer state = trtcService.getReporterOnLineRedisById(userId);
				if(state!=0){
					synchronized(session){
						try {
							socketMsg.setType(99997);
							session.getBasicRemote().sendText(JSONObject.fromObject(socketMsg).toString());
//							map.remove(socketMsg.getFromUserId(), session);
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}else{
					// 加入set中.
					map.put(userId, session);
					webSocketSet.add(this);
					trtcService.updateReporterStatusRedisById(userId, 1);
					socketMsg.setType(10010);
					broadcast(socketMsg, userId);
				}
			}else if(socketMsg.getType() == 22222){
				log.info("ping : {}", userId);
				synchronized(session){
					try {
						session.getBasicRemote().sendText(JSONObject.fromObject(socketMsg).toString());
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			} else if (socketMsg.getType() == 10001){//下线
				log.info("用户下线 : {}", userId);
				socketMsg.setType(10011);
				broadcast(socketMsg, userId);
				trtcService.deleteReporterStatusRedisById(userId);
				//20200306 记者下线清除记者定位
				trtcService.deleteReporterLocationRedisById(userId);
				map.remove(userId);
				session.close();
				webSocketSet.remove(session);// 从set中移除.
			} else if(socketMsg.getType() == 10013){//无用
				webSocketSet.remove(session);// 从set中移除.
			} else if(socketMsg.getType() == 10010){
				log.info("用户上线：{}", userId);
				map.put(userId, session);
				webSocketSet.add(this);// 加入set中.
				trtcService.updateReporterStatusRedisById(userId, 1);
				socketMsg.setType(10010);
				broadcast(socketMsg, userId);
			}else if (socketMsg.getType() == 10015){// 在线中（APP端使用）
				trtcService.updateReporterStatusRedisById(userId, 1);
			}else if (socketMsg.getType() == 10016){//通话中（APP端使用）
				trtcService.updateReporterStatusRedisById(userId, 2);
			}else if(socketMsg.getType() == 99998){//强制下线
				log.info("用户强制下线：{}", userId);
				Session s = map.get(socketMsg.getFromUserId());// 用于处理接收者打开多个窗口
				if(s!=null && s.getId()!=null && s.isOpen()){
					synchronized(s){
						try {
							s.getBasicRemote().sendText(JSONObject.fromObject(socketMsg).toString());
							s.close();
							webSocketSet.remove(s);
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
				map.put(socketMsg.getFromUserId(), session);
			}else if(socketMsg.getType() == 20001 || socketMsg.getType() == 20002 || socketMsg.getType() == 20003 || socketMsg.getType() == 20004 || socketMsg.getType() == 29997){
				log.info("pad 请求： {}", socketMsg.getType());
				Session s = map.get(socketMsg.getToUserId());// 用于处理pad
				System.out.println("::::::::websocket::::::::"+"-----------angle:"+socketMsg.getAngle()+"-----------tab:"+socketMsg.getTab()+"-----------touserId:"+socketMsg.getToUserId()+"---------------fromUserId:"+socketMsg.getFromUserId()+"----------"+socketMsg.getType()+"------------"+DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
				if(s!=null && s.getId()!=null && s.isOpen()){
					synchronized(s){
						try {
							s.getBasicRemote().sendText(JSONObject.fromObject(socketMsg).toString());
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}

				System.out.println("************websocket::::::::isOpen"+s.isOpen()+"-----------touserId:"+socketMsg.getToUserId()+"---------------fromUserId:"+socketMsg.getFromUserId()+"---------formSessionId:::::"+session.getId()+"---toSessionId:::::"+s.getId()+"----------"+socketMsg.getType()+"------------"+DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
			}else{
				// 群发给每个客户端.
//				broadcast(socketMsg, userId);
			}

			if ((socketMsg.getAngle() != null && socketMsg.getAngle()>0) || (socketMsg.getTab() != null && socketMsg.getTab()>0)) {
				log.info("pad 处理： angle ： {}， tab : {}", socketMsg.getAngle(), socketMsg.getTab());
				Session s = map.get(socketMsg.getToUserId());// 用于处理pad
				System.out.println("::::::::websocket::::::::angle:"+socketMsg.getAngle()+"-----------tab:"+socketMsg.getTab()+"-----------touserId:"+socketMsg.getToUserId()+"---------------fromUserId:"+socketMsg.getFromUserId()+"----------"+socketMsg.getType()+"------------"+DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
				if(s!=null && s.getId()!=null && s.isOpen()){
					synchronized(s){
						try {
							s.getBasicRemote().sendText(JSONObject.fromObject(socketMsg).toString());
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}else{
					socketMsg.setType(99999);
					synchronized(session){
						try {
							session.getBasicRemote().sendText(JSONObject.fromObject(socketMsg).toString());
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}

			}
		} catch (JsonParseException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 发生错误时调用.
	 */
	@OnError
	public void onError(Session session, Throwable error, @PathParam("uid") String uid) {
		log.error("connection error : {}", uid);
		try {
//		error.printStackTrace();
//		System.out.println("onError:::id=" + session.getId()+";;;;;userId:::"+uid + "的用户,----" + DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN)+"---连接出错,原因:" + error);
			if(uid=="" || uid.equals("") || uid=="0" || uid.equals("0") || uid==null || uid.equals(null) || uid=="null" || uid.equals("null") || uid=="undefined" || uid.equals("undefined")){
				return;
			}
			this.userId = Long.valueOf(uid);
//		SocketMsg socketMsg = new SocketMsg();
//		socketMsg.setFromUserId(Long.valueOf(uid));
//		socketMsg.setType(11111);
//		synchronized(session){
//			try {
//				session.getBasicRemote().sendText(JSONObject.fromObject(socketMsg).toString());
//			} catch (Exception e) {
//				e.printStackTrace();
//			}
//		}
			SocketMsg socketMsg = new SocketMsg();
			socketMsg.setType(10011);
			socketMsg.setFromUserId(userId);
			broadcast(socketMsg, userId);
			
			trtcService.deleteReporterStatusRedisById(userId);
			//20200306 记者下线清除记者定位
			trtcService.deleteReporterLocationRedisById(userId);
			map.remove(userId);
			session.close();
			webSocketSet.remove(session);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}	
	/**
	 * 群发的方法.
	 * 
	 * @param
	 */
	private void broadcast(SocketMsg socketMsg, Long userId) {
		//System.out.println("broadcast::::::::"+socketMsg.getFromUserId()+"-----------"+socketMsg.getType()+"-----------"+socketMsg.getToUserId()+"----"+DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
		for (WebSocket item : webSocketSet) {
			// 发送消息.
			Session s = item.session;
			if(s!=null && s.getId()!=null && s.isOpen()){
				synchronized(s){
					try {
						s.getBasicRemote().sendText(JSONObject.fromObject(socketMsg).toString());
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}
