package com.gyee.frame.netty.websocket;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.gyee.frame.common.conf.ExceptJobConfig;
import com.gyee.frame.common.conf.UserLoginContext;
import com.gyee.frame.common.quartz.SocketTaskUtil;
import com.gyee.frame.common.spring.SpringUtils;
import com.gyee.frame.netty.websocket.util.WebsocketUtil;
import com.gyee.frame.service.GyeeuserService;
import com.gyee.frame.util.StringUtils;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
/**
 *@author:Wang Jiawen
 *
 *@description: socketserver 封装类
 *
 *@createTime:2019/12/31
 *
 */

@ServerEndpoint(value = "/websocket/{pageNumber}/{functionNumber}/{keyid}",encoders = { ServerEncoder.class })
@Component
public class WebSocketServer extends SocketTaskUtil {

	static Log log=LogFactory.get(WebSocketServer.class);
    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set，功能名称与页面名称+对象编号（风场、项目、线路、风机）为key
    private static Map<String,CopyOnWriteArraySet<WebSocketServer>> webSocketMapSet = new HashMap<>();

//    //功能名称与页面名称为key 举例 页面名称_功能名称>>WebSocketServer
//    private static Map<String,WebSocketServer> webSocketMap = new HashMap();
    //用来存储每个调度任务请求的数量
    private static ConcurrentHashMap<String,Integer> funcationNunberMap = new ConcurrentHashMap();

    public static ConcurrentHashMap<String, CopyOnWriteArraySet<String>> keyidMap = new ConcurrentHashMap();



    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;

    //接收sid
    private String sid="";
    //接收pageNumber
    private String pageNumber="";
    //接收functionNumber
    private String functionNumber="";

    //接收风场编号、接收项目编号、收线路编号、接收风机编号编号
    private String keyid="";
    //页数和功能数组合key
    private String jobkey="";
    //页数和功能数组合+对象编号（风场、项目、线路、风机）
    private String pfkey="";
    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session, @PathParam("pageNumber") String pageNumber, @PathParam("functionNumber") String functionNumber
            , @PathParam("keyid") String keyid) {

        //目标连接成功后注入基本信息
        this.session = session;

        this.sid=session.getId();
        this.pageNumber=pageNumber; //页面编码
        this.functionNumber=functionNumber;//功能编码
        this.keyid=keyid;

        StringBuilder sb=new StringBuilder();
        sb.append(pageNumber).append("_").append(functionNumber);

        jobkey=String.valueOf(sb);
        //判断是否有对象编号
        if(StringUtils.isNotNull(keyid))
        {
            sb.append("_").append(keyid);
        }

        pfkey=String.valueOf(sb);

        //根据调度任务key进行map分组，二级存储不同seesion的WebSocketServer对象



        //根据调度任务统计数量
        if(funcationNunberMap.containsKey(jobkey))
        {
            Integer number=funcationNunberMap.get(jobkey);
            number++;
            funcationNunberMap.put(jobkey,number);
        }else
            {
                funcationNunberMap.put(jobkey,1);
            }

        if(jobkey.equals("pageNumber_4_functionNumber_5"))
        {
            UserLoginContext.userLoginMap.put(keyid,session);
        }
        Set<Map.Entry<String, CopyOnWriteArraySet<WebSocketServer>>> webSocketset = webSocketMapSet.entrySet();
        for (Iterator<Map.Entry<String, CopyOnWriteArraySet<WebSocketServer>>> it = webSocketset.iterator(); it.hasNext();) {
            Map.Entry<String, CopyOnWriteArraySet<WebSocketServer>> entry = (Map.Entry<String, CopyOnWriteArraySet<WebSocketServer>>) it.next();
            for(WebSocketServer item:entry.getValue())
            {
                InetSocketAddress curryip=WebsocketUtil.getRemoteAddress(session);
                InetSocketAddress itemip=WebsocketUtil.getRemoteAddress(item.session);
                if(curryip.getHostString().equals(itemip.getHostString()) &&
                           (item.session.getRequestURI().getPath().indexOf("/wp_")>0 ||
                                   item.session.getRequestURI().getPath().indexOf("/pj_")>0
                                )
                )
                {
                    entry.getValue().remove(item);
                }
            }
          //  //System.out.println(entry.getKey() + "--->" + entry.getValue());
        }

        //页数和功能数组合+对象编号（风场、项目、线路、风机）作为KEY，保存对应的set集合
        if(webSocketMapSet.containsKey(pfkey))
        {
            CopyOnWriteArraySet<WebSocketServer> set=webSocketMapSet.get(pfkey);
            set.add(this);
            webSocketMapSet.put(pfkey,set);
        }else
        {
            CopyOnWriteArraySet<WebSocketServer> set=new CopyOnWriteArraySet<>();
            set.add(this);
            webSocketMapSet.put(pfkey,set);
        }

        addOnlineCount();           //在线数加1
        log.info("有新窗口开始监听:页面编码:"+pageNumber+",功能编码:"+functionNumber+",当前在线人数为" + getOnlineCount());


        if(keyidMap.containsKey(jobkey))
        {
            Set<String> set=keyidMap.get(jobkey);
            set.add(keyid);
        }else
        {
            CopyOnWriteArraySet<String> set= new CopyOnWriteArraySet<String>();
            set.add(keyid);
            keyidMap.put(jobkey,set);
        }

        log.info("连接成功");



//        if(funcationNunberMap.containsKey(jobkey))
//        {
//            Integer number=funcationNunberMap.get(jobkey);
//            if(number==1){
//                this.restartJob((jobkey));//重启任务
//            }
//        }


        if(!ExceptJobConfig.exceptmaps.containsKey(jobkey))
        {
            this.restartJob((jobkey));//重启任务
        }
//        else
//        {
//            if(jobkey.equals("pageNumber_4_functionNumber_3"))
//            {
//
//                GyeeuserService gyeeuserService=SpringUtils.getBean("gyeeuserService");
//                gyeeuserService.initialUserList();
//            }
//        }

    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam("pageNumber") String pageNumber,@PathParam("functionNumber") String functionNumber,@PathParam("keyid") String keyid) {
        StringBuilder sb=new StringBuilder();
        sb.append(pageNumber).append("_").append(functionNumber);

        jobkey=String.valueOf(sb);
        //判断是否有对象编号
        if(StringUtils.isNotNull(keyid))
        {
            sb.append("_").append(keyid);
        }

        pfkey=String.valueOf(sb);

//        if(webSocketMap.containsKey(pfkey))
//        {
//            webSocketMap.remove(pfkey);
//
//        }

        if(webSocketMapSet.containsKey(pfkey))
        {
            CopyOnWriteArraySet<WebSocketServer> set=webSocketMapSet.get(pfkey);
            set.remove(this);
            webSocketMapSet.put(pfkey,set);
        }
        subOnlineCount();           //在线数减1
        log.info("有一连接关闭！当前在线人数为" + getOnlineCount());
        if(funcationNunberMap.containsKey(jobkey))
        {
            int fnumber=funcationNunberMap.get(jobkey);
            if(fnumber>0)
            {
                fnumber--;
            }else {
                fnumber=0;
            }
            funcationNunberMap.put(jobkey,fnumber);
            if(fnumber==0){
                this.deleteJob((jobkey));//关闭任务
                keyidMap=new ConcurrentHashMap<>();
            }
        }

        if(getOnlineCount()==0){
            this.deleteJob((jobkey));//关闭任务
            keyidMap=new ConcurrentHashMap<>();
        }
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
//    	log.info("收到来自窗口:"+pageNumber+",功能编码:"+functionNumber+"的信息:"+message);
//        //群发消息
//        if(webSocketMapSet.containsKey(pfkey))
//        {
//            CopyOnWriteArraySet<WebSocketServer> set=webSocketMapSet.get(pfkey);
//            for (WebSocketServer item : set) {
//                try {
//                    if(item.session.isOpen())
//                    {
//                        item.sendMessage(message);
//                    }
//
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
//        }

    }

	/**
	 * 
	 * @param session
	 * @param error
	 */
    @OnError
    public void onError(Session session, Throwable error,@PathParam("pageNumber") String pageNumber,@PathParam("functionNumber") String functionNumber,@PathParam("keyid") String keyid) {
        log.error("发生错误");

        log.error(error.getMessage());
    }
	/**
	 * 实现服务器主动推送
	 */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }
    /**
     *@author:Wang Jiawen
     *
     *@description:直接发送object类型消息，实现服务器主动推送，新增传输object类型消息
     *
     *@param:[message] object
     *
     *@return:void
     *
     *@createTime:2020/1/9
     *
     */
    public void sendMessage(Object message) throws IOException, EncodeException {
        this.session.getBasicRemote().sendObject(message);
    }

    /**
     * 群发自定义消息
     * */
//    public static void sendInfo(Object message,@PathParam("pageNumber") String pageNumber,@PathParam("functionNumber") String functionNumber) throws IOException, EncodeException {
//    	log.info("推送消息到窗口:"+pageNumber+",功能编码:"+functionNumber+"的信息:"+message);
//        WebSocketServer item = webSocketMap.get(pageNumber + "_" + functionNumber);
//
//        //这里可以设定只推送给这个sid的，为null则全部推送
//        if(functionNumber==null||pageNumber==null) {
//            throw new BindException("请核对编码信息(包括页面编码以及功能编码非空)");
//        }else if(item.pageNumber.equals(pageNumber)&&item.functionNumber.equals(functionNumber)){
//            item.sendMessage(message);
//        }
//        //利用set容器循环的方式暂时被关闭,原因节约资源
///*        for (WebSocketServer item : webSocketSet) {
//            try {
//            	//这里可以设定只推送给这个sid的，为null则全部推送
//            	if(functionNumber==null||pageNumber==null) {
//                    throw new BindException("请核对编码信息");
//            	}else if(item.pageNumber.equals(pageNumber)&&item.functionNumber.equals(functionNumber)){
//            		item.sendMessage(message);
//            	}
//            } catch (IOException e) {
//                e.printStackTrace();
//                continue;
//            }
//        }*/
//    }
    public void sendObject(Object message,@PathParam("pageNumber") String pageNumber,@PathParam("functionNumber") String functionNumber,@PathParam("keyid") String keyid) throws IOException, EncodeException {
        log.info("推送消息到窗口:"+pageNumber+",功能编码:"+functionNumber+"的信息:"+message);


        StringBuilder sb=new StringBuilder();
        sb.append(pageNumber).append("_").append(functionNumber);

        //判断是否有对象编号
        if(StringUtils.isNotNull(keyid))
        {
            sb.append("_").append(keyid);
        }

        pfkey=String.valueOf(sb);
        if(webSocketMapSet.containsKey(pfkey))
        {

            //这里可以设定只推送给这个sid的，为null则全部推送
            if(functionNumber==null||pageNumber==null) {
                throw new BindException("请核对编码信息(包括页面编码以及功能编码非空)");
            }else{
                if(webSocketMapSet.containsKey(pfkey))
                {
                    CopyOnWriteArraySet<WebSocketServer> set=webSocketMapSet.get(pfkey);
                    for (WebSocketServer item : set) {
                        try {
                            item.sendMessage(message);

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

            }

        }



        //利用set容器循环的方式暂时被关闭,原因节约资源
/*        for (WebSocketServer item : webSocketSet) {
            try {
            	//这里可以设定只推送给这个sid的，为null则全部推送
            	if(functionNumber==null||pageNumber==null) {
                    throw new BindException("请核对编码信息");
            	}else if(item.pageNumber.equals(pageNumber)&&item.functionNumber.equals(functionNumber)){
            		item.sendMessage(message);
            	}
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }*/
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }



}

