package com.ruoyi.iot.socket;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.web.service.TokenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
@Slf4j
@ServerEndpoint("/websocket/{deviceId}/{token}")  // 接口路径 ws://localhost:8087/webSocket/userId;
public class WebSocket {
    
    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;
        /**
     * 用户ID
     */
    private String token;

    private String deviceId;

    private String key;

    private static TokenService tokenService;

    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    //虽然@Component默认是单例模式的，但springboot还是会为每个websocket连接初始化一个bean，所以可以用一个静态set保存起来。
    //  注：底下WebSocket是当前类名
    private static CopyOnWriteArraySet<WebSocket> webSockets =new CopyOnWriteArraySet<>();
    // 用来存在线连接用户信息
    private static ConcurrentHashMap<String,Session> sessionPool = new ConcurrentHashMap<String,Session>();

    @PostConstruct
    public void init(){
        tokenService = SpringUtils.getBean(TokenService.class);
    }

    public static String SPLIT_CHAR = "__";
    /**
     * 链接成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value="token")String token,@PathParam(value="deviceId")String deviceId) {
        try {
			this.session = session;
			this.token = token;
            this.deviceId = deviceId;
            this.key = this.token+SPLIT_CHAR+this.deviceId;
            LoginUser loginUser = tokenService.checkWebocketToken(token);
            if(loginUser == null){
                session.close();
            }else{
                boolean contains = containerKey(this.key);
                if(!contains){
                    webSockets.add(this);
                    sessionPool.put(this.key, session);
                    log.info("【websocket消息】有新的连接，总数为:"+webSockets.size());
                }

            }

		} catch (Exception e) {
            e.printStackTrace();
		}
    }

    private boolean containerKey(String target){
        Enumeration<String> keys = sessionPool.keys();
        while (keys.hasMoreElements())
        {
            String key = keys.nextElement();
            if(StringUtils.equals(target,key)){
                return true;
            }
        }
        return false;
    }

    /**
     * 链接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        try {
			webSockets.remove(this);
			sessionPool.remove(this.key);
			log.info("【websocket消息】连接断开，总数为:"+webSockets.size());
		} catch (Exception e) {
            e.printStackTrace();
		}
    }
    /**
     * 收到客户端消息后调用的方法
     *
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("【websocket消息】收到客户端消息:"+message);
    }
    
	  /** 发送错误时的处理
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {

        log.error("用户错误,原因:"+error.getMessage());
        error.printStackTrace();
    }

    
    // 此为广播消息
    public void sendAllMessage(String message) {
    	log.info("【websocket消息】广播消息:"+message);
        for(WebSocket webSocket : webSockets) {
            try {
            	if(webSocket.session.isOpen()) {
            		webSocket.session.getAsyncRemote().sendText(message);
            	}
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * 向指定设备发消息
     * @param deviceId
     * @param obj
     */
    public static void sendMsg(String deviceId, JSONObject obj){
        Enumeration<String> keys = sessionPool.keys();
        while (keys.hasMoreElements())
        {
            String key = keys.nextElement();
            String[] split = key.split(SPLIT_CHAR);
            String curDeviceId = split[1];
            if(StringUtils.equals(deviceId,curDeviceId)){
                Session session = sessionPool.get(key);
                try {
                    log.info("接收到消息-向设备："+deviceId);
                    session.getAsyncRemote().sendText(obj.toJSONString());
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }

        }
    }

}
