package com.wsm.admin.init;

import com.alibaba.fastjson.JSONObject;
import com.wsm.admin.model.Device;
import com.wsm.admin.model.DeviceStatus;
import com.wsm.admin.service.IDeviceService;
import com.wsm.admin.service.IDeviceStatusService;
import com.wsm.common.util.ByteUtil;
import com.wsm.common.util.DeviceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Component
public class TcpServer {
    private static final Logger log = LoggerFactory.getLogger(TcpServer.class);

    //解码buffer
    private Charset cs = Charset.forName("UTF-8");
    //发送数据缓冲区
    private static ByteBuffer sBuffer = ByteBuffer.allocate(1024);
    //接受数据缓冲区
    private static ByteBuffer rBuffer = ByteBuffer.allocate(1024);
    //选择器（叫监听器更准确些吧应该）
    private static Selector selector;

    @Autowired
    private IDeviceStatusService deviceStatusService;
    @Autowired
    private IDeviceService deviceService;
    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    private static final Map<String, String> deviceStatusMap = new HashMap<>();

    static {
        deviceStatusMap.put("08", "硬件版本号");
        deviceStatusMap.put("15", "SIM 卡CCID 号码");
        deviceStatusMap.put("05", "NB/GPRS 信号强度");
        deviceStatusMap.put("2A00", "电池：低压");
        deviceStatusMap.put("3500", "NB底座的电池：低压");
        deviceStatusMap.put("3600", "烟感底座上的烟感电池：低压");
        deviceStatusMap.put("3701", "消防门状态：打开");
        deviceStatusMap.put("3801", "消防手报状态：弹起");
        deviceStatusMap.put("3E01", "喷淋状态：打开");

        deviceStatusMap.put("111", "烟雾报警");
        deviceStatusMap.put("384", "低压报警");
        deviceStatusMap.put("993", "底座上的烟感低压");
        deviceStatusMap.put("992", "NB 底座低压报警 ");
        deviceStatusMap.put("991", "燃气报警");
        deviceStatusMap.put("990", "消防手报按钮报警");
        deviceStatusMap.put("989", "消防门打开");
        deviceStatusMap.put("988", "消防门关闭");
        deviceStatusMap.put("987", "消防门长时间未关闭");
        deviceStatusMap.put("986", "高温报警");
        deviceStatusMap.put("985", "漏电报警");
        deviceStatusMap.put("984", "温感低压");
        deviceStatusMap.put("966", "喷淋打开");
        deviceStatusMap.put("965", "喷淋关闭");

    }

    /**
     * 启动socket服务，开启监听
     *
     * @param port
     * @throws IOException
     */
    @Async("updAsyncPool")
    public void startSocketServer(int port) {
        try {
            //打开通信信道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //设置为非阻塞
            serverSocketChannel.configureBlocking(false);
            //获取套接字
            ServerSocket serverSocket = serverSocketChannel.socket();
            //绑定端口号
            serverSocket.bind(new InetSocketAddress(port));
            //打开监听器
            selector = Selector.open();
            //将通信信道注册到监听器
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            //监听器会一直监听，如果客户端有请求就会进入相应的事件处理
            while (true) {
                selector.select();//select方法会一直阻塞直到有相关事件发生或超时
                Set<SelectionKey> selectionKeys = selector.selectedKeys();//监听到的事件
                for (SelectionKey key : selectionKeys) {
                    handle(key);
                }
                selectionKeys.clear();//清除处理过的事件
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理不同的事件
     *
     * @param selectionKey
     * @throws IOException
     */
    private void handle(SelectionKey selectionKey) throws IOException {
        ServerSocketChannel serverSocketChannel = null;
        SocketChannel socketChannel = null;
        String requestMsg = "";
        String responseMsg = "";
        int count = 0;
        if (selectionKey.isAcceptable()) {
            //每有客户端连接，即注册通信信道为可读
            serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
            socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_READ);
        } else if (selectionKey.isReadable()) {
            socketChannel = (SocketChannel) selectionKey.channel();
            rBuffer.clear();
            count = socketChannel.read(rBuffer);
            //读取数据
            if (count > 0) {
                rBuffer.flip();

                byte[] bytes = new byte[rBuffer.remaining()];
                rBuffer.get(bytes, 0, bytes.length);

                requestMsg = ByteUtil.bytesToHexString(bytes).toUpperCase();
                log.warn("Received TCP Msg :" + requestMsg);

                try {
                    responseMsg = handleYG(requestMsg);
                }catch (Exception ex){
                    log.error(ex.getMessage());
                }
            }
            //返回数据
            sBuffer = ByteBuffer.allocate(responseMsg.getBytes("UTF-8").length);
            sBuffer.put(responseMsg.getBytes("UTF-8"));
            sBuffer.flip();
            socketChannel.write(sBuffer);
            socketChannel.close();
        }
    }

    private String handleYG(String receiveMsg) {
        log.warn("======接收到烟感传感器推送信息======");
        String[] str = DeviceUtil.turnToArray(receiveMsg);
        String deviceID = DeviceUtil.getYGDeviceID(str);
        log.warn("deviceID："+deviceID);
        Device device = deviceService.findByDeviceId16Hex(deviceID);
        if (device != null){
            log.warn("接收到："+device.getDeviceId()+" 推送的信息");
        }

        String type = str[6];
        switch (type){
            // 心跳
            case "02":
                log.warn("======心跳信息，无异常状态======");
                break;
            // 状态
            case "04":
                int total = Integer.valueOf(str[16]);
                for (int i = 0, length = 3; i < total; i++){
                    String a = str[17 + i * length];
                    //String b = str[18 + i * length];
                    String c = str[19 + i * length];
                    // 状态异常
                    if (!org.springframework.util.StringUtils.isEmpty(deviceStatusMap.get(a+c)) && device != null){
                        DeviceStatus status =  new DeviceStatus();
                        status.setContent(deviceStatusMap.get(a+c));
                        status.setDevice(device);
                        status.setStatus((byte)0);
                        deviceStatusService.save(status);
                        log.warn(device.getDeviceId() + ":"+ status.getContent());

                        JSONObject result = new JSONObject();
                        result.put("id", device.getDeviceId());
                        result.put("deviceType", device.getDeviceType());
                        result.put("status", a+c);

                        messagingTemplate.convertAndSend("/topic/device", result.toJSONString());
                    }
                }
                break;
            // 报警
            case "09":
                // 报警
                String hex = ByteUtil.hexStringToString(str[24]+str[25]+str[26]);
                if (!org.springframework.util.StringUtils.isEmpty(deviceStatusMap.get(hex)) && device != null){
                    DeviceStatus status =  new DeviceStatus();
                    status.setContent(deviceStatusMap.get(hex));
                    status.setDevice(device);
                    status.setStatus((byte)1);
                    deviceStatusService.save(status);
                    log.warn(device.getDeviceId() + ":"+ status.getContent());

                    JSONObject result = new JSONObject();
                    result.put("id", device.getDeviceId());
                    result.put("deviceType", device.getDeviceType());
                    result.put("status", hex);
                    messagingTemplate.convertAndSend("/topic/device", result.toJSONString());
                }
                break;
        }

        return DeviceUtil.getOKFeedback(str);
    }

}
