package com.yeejoin.amos.boot.module.jcs.biz.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yeejoin.amos.boot.module.jcs.api.dto.ControllerDto;
import com.yeejoin.amos.boot.module.jcs.api.entity.Controller;
import com.yeejoin.amos.boot.module.jcs.api.enums.ControllerTypeEnum;
import com.yeejoin.amos.boot.module.jcs.api.mapper.ControllerMapper;
import com.yeejoin.amos.boot.module.jcs.api.service.IControllerService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.typroject.tyboot.core.rdbms.service.BaseService;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <pre>
 * 联动控制器 服务实现类
 * </pre>
 *
 * @author gwb
 * @version $Id: ControllerServiceImpl.java, v 0.1 2021年8月20日 上午10:15:49 gwb Exp $
 */
@Service
public class ControllerServiceImpl extends BaseService<ControllerDto, Controller, ControllerMapper> implements IControllerService {

    private final ConcurrentHashMap<Long, TimerTask> tasks = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<Long, Long> bellStartTime = new ConcurrentHashMap<>();
    private final Timer timer = new Timer();

    @Autowired
    ControllerMapper controllerMapper;

    @Autowired
    ControllerEquipServiceImpl controllerEquipServiceImpl;


    public List<ControllerDto> queryAllForList() {
        return this.queryForList("", false);
    }

    public List<ControllerDto> queryForList(String station, String type) {
        return this.queryForList("", false, station, type);
    }

    public List<ControllerDto> getCurrentStationController(Long sequenceNbr) {
        return controllerMapper.getCurrentStationController(sequenceNbr);
    }

    /**
     * 更新station联动控制器状态
     * 3.1.3  警铃、广播、车库门 联动规则：
     * 当 点击【全部启动】、【某大队全部启动】时：
     * 1>  首先：消防警铃第一时间  响铃持续15s 后自动关闭。
     * 2>  然后：警铃 响铃结束后，广播系统开启。广播 启动预制的 广播音频， 播放。
     * 3>  车库门和警铃第一时间自动开启。
     * 4>  系统 需要打印出 “警铃、广播、车库门 ” 启动指令和  停止指令的 日志。
     * 当同时 点击【启动警铃】、【启动广播】时
     * 1>  首先：消防警铃第一时间  响铃持续15s 后自动关闭。
     * 2>  然后：警铃 响铃结束后，广播系统开启。广播 启动预制的 广播音频， 播放。
     */
    public boolean changeControllerState(ControllerDto[] controllers) {
        //全部操作
        if (controllers.length > 1) {
            batchChange(controllers);
        }
        //单个操作
        else if (controllers.length == 1) {
            ControllerDto controller = controllers[0];
            //取消该controller的任务
            TimerTask historyTask = tasks.get(controller.getSequenceNbr());
            if (historyTask != null) {
                historyTask.cancel();
                tasks.remove(controller.getSequenceNbr());
            }
            String type = controller.getType();
            //关闭操作(只能关闭警铃)
            if ("0".equals(controller.getState())) {
                if (ControllerTypeEnum.BELL.getCode().equals(type)) {
                    //取消该警铃的任务
                    TimerTask stopBellTaskHistory = tasks.get(controller.getSequenceNbr());
                    if (stopBellTaskHistory != null) {
                        stopBellTaskHistory.cancel();
                        tasks.remove(controller.getSequenceNbr());
                    }
                    controllerEquipServiceImpl.changeBell(controller, "0");//关闭警铃
                }
            }
            //启动操作
            else {
                if (ControllerTypeEnum.BELL.getCode().equals(type)) {
                    bellStartTime.put(controller.getSequenceNbr(), System.currentTimeMillis());
                    controllerEquipServiceImpl.changeBell(controller, "1");//启动警铃
                    //开始计时15秒,15秒后关闭警铃
                    TimerTask timerTask = new TimerTask() {
                        @Override
                        public void run() {
                            tasks.remove(controller.getSequenceNbr());
                            controllerEquipServiceImpl.changeBell(controller, "0");//关闭警铃
                        }
                    };
                    tasks.put(controller.getSequenceNbr(), timerTask);
                    timer.schedule(timerTask, 15000);
                } else if (ControllerTypeEnum.BROADCAST.getCode().equals(type)) {
                    //开启广播前检测警铃是否开启，警铃开启的话，等待警铃15s结束后开启广播
                    LambdaQueryWrapper<Controller> lambdaQueryWrapper = new LambdaQueryWrapper<Controller>()
                            .eq(Controller::getStation, controller.getStation())
                            .eq(Controller::getType, ControllerTypeEnum.BELL.getCode());
                    Controller bellController = controllerMapper.selectOne(lambdaQueryWrapper);
                    Long startTime = bellStartTime.get(bellController.getSequenceNbr());
                    // 警铃开启5秒内开启广播，广播均排队等待15s
                    if (startTime != null && (System.currentTimeMillis() - startTime) / 1000 < 3) {
                        TimerTask timerTask = new TimerTask() {
                            @Override
                            public void run() {
                                tasks.remove(controller.getSequenceNbr());
                                controllerEquipServiceImpl.changeBroadcast(controller, "1");//开启广播
                            }
                        };
                        tasks.put(controller.getSequenceNbr(), timerTask);
                        timer.schedule(timerTask, 14000);
                    } else {
                        //如果警铃启动，则先关闭警铃，后开启广播
                        TimerTask timerTask = tasks.get(bellController.getSequenceNbr());
                        if (timerTask != null) {
                            timerTask.cancel();
                            tasks.remove(bellController.getSequenceNbr());
                            ControllerDto controllerDto = new ControllerDto();
                            BeanUtils.copyProperties(bellController, controllerDto);
                            controllerEquipServiceImpl.changeBell(controllerDto, "0");//关闭警铃
                        }
                        controllerEquipServiceImpl.changeBroadcast(controller, "1");//开启广播
                    }
                } else if (ControllerTypeEnum.DOOR.getCode().equals(type)) {
                    controllerEquipServiceImpl.changeDoor(controller, "1");//开启车库门
                }
            }
        }
        return true;
    }

    /**
     * 全部操作：启动警铃,启动车库门，15秒后关闭警铃，启动广播
     */
    public void batchChange(ControllerDto[] controllers) {
        //按大队分组
        Set<String> groupTypes = new LinkedHashSet<>();
        for (ControllerDto controller : controllers) {
            groupTypes.add(controller.getStation());
        }
        //按大队进行归类
        List<Map<String, ControllerDto>> groups = new ArrayList<>();
        groupTypes.forEach(groupType -> {
            Map<String, ControllerDto> group = new HashMap<>();
            for (ControllerDto controller : controllers) {
                if (groupType.equals(controller.getStation())) group.put(controller.getType(), controller);
            }
            groups.add(group);
        });
        // 某大队全部操作
        if (groups.size() == 1) {
            Map<String, ControllerDto> group = groups.get(0);
            ControllerDto bellController = group.get(ControllerTypeEnum.BELL.getCode());//警铃
            ControllerDto broadcastController = group.get(ControllerTypeEnum.BROADCAST.getCode());//广播
            ControllerDto doorController = group.get(ControllerTypeEnum.DOOR.getCode());//车库门
            //取消该大队所有历史任务
            TimerTask bellTask = tasks.get(bellController.getSequenceNbr());
            TimerTask broadcastTask = tasks.get(broadcastController.getSequenceNbr());
            if (bellTask != null) {
                bellTask.cancel();
                tasks.remove(bellController.getSequenceNbr());
            }
            if (broadcastTask != null) {
                broadcastTask.cancel();
                tasks.remove(broadcastController.getSequenceNbr());
            }
            batchOperation(bellController, broadcastController, doorController);
        }
        //所有大队操作
        else {
            //取消所有的历史任务
            tasks.values().forEach(TimerTask::cancel);
            tasks.clear();
            groups.forEach(group -> {
                ControllerDto bellController = group.get(ControllerTypeEnum.BELL.getCode());//警铃
                ControllerDto broadcastController = group.get(ControllerTypeEnum.BROADCAST.getCode());//广播
                ControllerDto doorController = group.get(ControllerTypeEnum.DOOR.getCode());//车库门
                //关闭所有(广播，车库门不能关闭)
                batchOperation(bellController, broadcastController, doorController);
            });
        }
    }

    /**
     * 批量操作
     *
     * @param bellController      警铃
     * @param broadcastController 广播
     * @param doorController      车库门
     */
    private void batchOperation(ControllerDto bellController, ControllerDto broadcastController, ControllerDto doorController) {
        //关闭所有(广播，车库门不能关闭)
        if ("0".equals(bellController.getState())) {
            controllerEquipServiceImpl.changeBell(bellController, "0");//关闭警铃
        }
        //开启所有
        else {
            //立即启动警铃，打开车库门
            bellStartTime.put(bellController.getSequenceNbr(), System.currentTimeMillis());
            controllerEquipServiceImpl.changeBell(bellController, "1");//开启警铃
            controllerEquipServiceImpl.changeDoor(doorController, "1");//开启车库门
            //15秒后关闭警铃
            TimerTask stopBellTask = new TimerTask() {
                @Override
                public void run() {
                    tasks.remove(bellController.getSequenceNbr());
                    controllerEquipServiceImpl.changeBell(bellController, "0");//关闭警铃
                }
            };
            //15秒后启动广播
            TimerTask startBroadcastTask = new TimerTask() {
                @Override
                public void run() {
                    tasks.remove(broadcastController.getSequenceNbr());
                    controllerEquipServiceImpl.changeBell(broadcastController, "1");//开启广播
                }
            };
            //保存新的任务至map中，同时开始调度新任务
            tasks.put(bellController.getSequenceNbr(), stopBellTask);
            tasks.put(broadcastController.getSequenceNbr(), startBroadcastTask);
            timer.schedule(stopBellTask, 15000);
            timer.schedule(startBroadcastTask, 16000);
        }
    }
}

