package net.vtstar.zhongtong.avi.monitoring.job;

import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import lombok.extern.slf4j.Slf4j;
import net.vtstar.protocol.modbus.tcp.netty.packet.ModbusResponse;
import net.vtstar.protocol.modbus.tcp.netty.service.ModbusTemplate;
import net.vtstar.protocol.modbus.tcp.netty.utils.NettyUtils;
import net.vtstar.protocol.modbus.tcp.netty.utils.PoolKey;
import net.vtstar.scada.base.equipmgt.domain.*;
import net.vtstar.scada.base.equipmgt.service.EquipInfoService;
import net.vtstar.scada.base.gather.job.GatherJob;
import net.vtstar.scada.base.global.service.ModbusService;
import net.vtstar.zhongtong.avi.equipment.domain.PaintshopStation;
import net.vtstar.zhongtong.avi.equipment.mapper.PaintshopStationMapper;
import net.vtstar.zhongtong.avi.global.constant.Constant;
import net.vtstar.zhongtong.avi.monitoring.domain.Skid;
import net.vtstar.zhongtong.avi.monitoring.domain.Vehicle;
import net.vtstar.zhongtong.avi.monitoring.domain.enums.Direction;
import net.vtstar.zhongtong.avi.monitoring.domain.enums.StationState;
import net.vtstar.zhongtong.avi.monitoring.service.StationPassInfoService;
import net.vtstar.zhongtong.avi.mqtt.handle.RfidMessageHandler;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class AVIMonitorJob extends GatherJob {

    private static final String EQUIPMENT_TYPE = "PLC";

    @Autowired
    private EquipInfoService equipInfoService;
    @Autowired
    private PaintshopStationMapper stationMapper;
    @Autowired
    private ModbusService modbusService;
    @Autowired
    private StationPassInfoService passInfoService;

    @Resource(name = "stationCache")
    private Cache stationCache;
    @Resource(name = "vehicleCache")
    private Cache vehicleCache;
    @Resource(name = "laneStationCache")
    private Cache laneStationCache;
    @Resource(name = "redisTemplate")
    protected HashOperations hashOperations;
    @Resource(name = "redisTemplate")
    private ValueOperations valueOperations;
    @Resource(name = "redisTemplate")
    private ListOperations listOperations;
    @Resource(name = "redisTemplate")
    private RedisTemplate redisTemplate;

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {

        List<EquipInfo> equipInfoList = equipInfoService.getEquipInfoList(EQUIPMENT_TYPE);
        if (CollectionUtils.isEmpty(equipInfoList)) {
            return;
        }
        //处理工位正反转用
        List<PaintshopStation> stations = new ArrayList<>();
        for (EquipInfo equipInfo : equipInfoList) {

            List<EquipCommunication> commList = equipInfoService.getCommunicationListByEquipType(equipInfo.getEquipCode(), 1);
            if (CollectionUtils.isEmpty(commList)) {
                return;
            }
            for (EquipCommunication communication : commList) {
                EquipDataChannel dataChannel = communication.getDataChannel();
                String ip = dataChannel.getIp();
                Integer port = dataChannel.getPort();
                PoolKey poolKey = NettyUtils.wrapPoolKey(ip, port);
                Integer slaveId = dataChannel.getNo();
                EquipDataTemplate template = communication.getDataTemplate();
                if (template == null) {
                    log.info("无法获取设备的数据模板，设备编号：{}", dataChannel.getEquipCode());
                    continue;
                }
                List<EquipDataTemplateItem> templateItemList = template.getTemplateItemList();
                if (CollectionUtils.isEmpty(templateItemList)) {
                    log.info("无法获取数据模板的条目，设备编号：{},模板名称：{}", dataChannel.getEquipCode(), template.getName());
                    continue;
                }

                Map<Integer, List<EquipDataTemplateItem>> listMap = modbusService.groupByFuntionCode(templateItemList);
                for (Map.Entry<Integer, List<EquipDataTemplateItem>> entry : listMap.entrySet()) {
                    List<EquipDataTemplateItem> value = entry.getValue();
                    if (CollectionUtils.isEmpty(value)) {
                        continue;
                    }
                    Integer start = value.get(0).getAddress();
                    Integer end = value.get(value.size() - 1).getAddress();
                    Integer size = value.get(value.size() - 1).getLength();
                    Integer quantity = end + size - start;

                    ModbusResponse response = null;
                    Integer functionCode = entry.getKey();
                    try {
                        if (1 == functionCode) {
                            response = modbusService.readCoilRegister(slaveId, poolKey, start, quantity);
                        } else if (2 == functionCode) {
                            response = modbusService.readInputDiscretes(slaveId, poolKey, start, quantity);
                        } else if (3 == functionCode) {
                            response = modbusService.readMultipleRegister(slaveId, poolKey, start, quantity);
                        } else if (4 == functionCode) {
                            response = modbusService.readInputRegister(slaveId, poolKey, start, quantity);
                        }
                    } catch (Exception e) {
                        log.error("读取失败。ip{}， 端口{}, slave{}, start{}, size{}", poolKey.getHost(), poolKey.getPort(), slaveId, start, quantity);
                        continue;
                    }
                    if (null == response) {
                        log.error("读取失败！ response 为空！");
                        continue;
                    }
                    ByteBuf body = response.getBody();
                    if (null == body) {
                        log.error("response.body is null!!");
                        continue;
                    }
                    int length = body.readableBytes();
                    byte[] bytes = new byte[length];
                    body.getBytes(body.readerIndex(), bytes);

                    log.info("读取读取数据。ip{}， 端口{}, slave{}, start{}, size{}", poolKey.getHost(), poolKey.getPort(), slaveId, start, quantity);
                    log.info("data:{}", Arrays.toString(bytes));

                    for (EquipDataTemplateItem templateItem : value) {
                        Integer address = templateItem.getAddress();
                        int quality = address - start;
                        int register = quality / 8;
                        int offset = quality % 8;
                        byte state = bytes[register];

                        String fieldName = templateItem.getFieldName();
                        if (fieldName.startsWith("station:ve")) {
                            String stationCode = fieldName.split(":")[2];
                            PaintshopStation station = (PaintshopStation) valueOperations.get(Constant.STATION_PREFIX + stationCode);
                            if (null == station) {
                                station = stationCache.get(stationCode, PaintshopStation.class);
                            }
                            if ((state & (0x01 << offset)) == 0) {
                                state = 0;
                            } else {
                                state = 1;
                            }
                            station.setOldState(station.getState());
                            station.setArrive(state == 1 ? true : false);
                            stations.add(station);
                            valueOperations.set(Constant.STATION_PREFIX + stationCode, station);
                        } else if (fieldName.startsWith("station:turn")) {
                            String[] split = fieldName.split(":");
                            String stationCode = fieldName.split(":")[2];

                            PaintshopStation station = (PaintshopStation) valueOperations.get(Constant.STATION_PREFIX + stationCode);
                            if (null == station) {
                                station = stationCache.get(stationCode, PaintshopStation.class);
                            }
                            if ((state & (0x01 << offset)) == 0) {
                                state = 0;
                            } else {
                                state = 1;
                            }
                            station.setOldState(station.getState());
                            String turn = split[1];
                            if (turn.endsWith("F")) {
                                station.setTurnF(state == 1 ? true : false);
                            }
                            if (turn.endsWith("R")) {
                                station.setTurnR(state == 1 ? true : false);
                            }
                            valueOperations.set(Constant.STATION_PREFIX + stationCode, station);
                        } else if (fieldName.startsWith("warning")) {
                            String[] split = fieldName.split(":");
                            String areaNo = split[1];
                            String warningCode = split[2];
                            if ((state & (0x01 << offset)) == 0) {
                                state = 0;
                            } else {
                                state = 1;
                            }
                            valueOperations.set(Constant.WARNING_PREFIX + areaNo + ":" + warningCode, state);
                        }
                    }
                }
            }
        }

        if (CollectionUtils.isEmpty(stations)) {
            log.error("没有维护工位信息!");
            return;
        }

        List<Vehicle> vehicles = new ArrayList<>();
        Set<String> veKeys = redisTemplate.keys(Constant.VEHICLE_PREFIX + ":*");
        for (String veKey : veKeys) {
            Vehicle ve = (Vehicle) valueOperations.get(Constant.VEHICLE_PREFIX + veKey);
            if (null == ve) {
                ve = vehicleCache.get(veKey, Vehicle.class);
            }
            if (ve.getState() == false){
                vehicles.add(ve);
            }

        }

        Map<String, List<Vehicle>> veMap = vehicles.stream().filter(a -> a.getState() != false).collect(Collectors.groupingBy(Vehicle::getLane));

        Map<String, List<PaintshopStation>> listMap = stations.stream().filter(a -> false != a.getTurnR() || false != a.getTurnF()).
                collect(Collectors.groupingBy(PaintshopStation::getVehicleLaneCode));
        for (Map.Entry<String, List<Vehicle>> ve : veMap.entrySet()) {
            String key = ve.getKey();
            if (Integer.valueOf(key) > 4){
                continue;
            }
            List<Vehicle> value = ve.getValue();
            List<PaintshopStation> turnStation = new ArrayList<>();
            List<PaintshopStation> landStations = listMap.get(key);
            for (PaintshopStation landStation : landStations) {
                //获得 西侧加正转=下车 西+反=上 东+反=下 东+正=上
                if (landStation.getTurnF() && landStation.getDirection().equals(Direction.WEST)) {
                    turnStation.add(landStation);
                }
                if (landStation.getTurnR() || landStation.getDirection().equals(Direction.EAST)) {
                    turnStation.add(landStation);
                }
            }
            int stationSize = 0;

            Map<String, PaintshopStation> entries = hashOperations.entries(Constant.STATION_OUT_PREFIX + key);
            for (PaintshopStation station : turnStation) {
                //若当前工位已进车的方式旋转, 且之前一分钟内没有进过车, 则此工位可再次进车
                if (station.getInTime() != null && ((System.currentTimeMillis() - station.getInTime().getTime()) > 60 * 1000)){
                    entries.put(station.getCode(), station);
                }
            }

            if (CollectionUtils.isEmpty(entries)) {
                stationSize = turnStation.size();
            }
            //TODO: 临时校验错误, 每条平移车道存在其旋转工位数, 若超过此值, 则按照当前下车数进行依次下车, 多余旋转工位舍弃;
            int maxStationSize = 0;
            switch (key){
                case "1":
                    maxStationSize = 1;
                    break;
                case "2":
                    maxStationSize = 4;
                    break;
                case "3":
                    maxStationSize = 3;
                    break;
                case "4":
                    maxStationSize = 7;
                    break;
            }
            //若平移车下车信号与与工位下车信号不等，存入工位信息返回
            if (value.size() != stationSize && stationSize <= maxStationSize) {
                hashOperations.putAll(Constant.STATION_OUT_PREFIX + key, entries);
                log.error("当前旋转的工位数量为: " + stationSize);
            } else {
                value.sort(Comparator.comparing(v -> v.getSequence()));
                List<PaintshopStation> collect = entries.values().stream().sorted(Comparator.comparing(e -> e.getVehicleLaneCode())).collect(Collectors.toList());
                for (int i = 0; i < value.size(); i++) {

                    //找到滑撬上绑定平移车，找到工位对应车道上所有工位信息
                    Vehicle vehicle = value.get(i);
                    PaintshopStation station = collect.get(i);
                    Skid skid = vehicle.getSkid();
                    Long laneId = station.getLaneId();

                    List<PaintshopStation> lands = new ArrayList<>();
                    List<PaintshopStation> temStation = laneStationCache.get(laneId, ArrayList.class);
                    for (PaintshopStation land : temStation) {
                        PaintshopStation s = (PaintshopStation) valueOperations.get(Constant.STATION_PREFIX + land.getCode());
                        lands.add(s);
                    }

                    lands.sort(Comparator.comparing(v -> v.getSequence()));

                    PaintshopStation start = lands.get(0);
                    PaintshopStation end = lands.get(lands.size() - 1);
                    //下车时, 记录进去车道的开始时间, 并将记录ID绑定在工位上
                    Long passInfoId = passInfoService.createPassInfo(station.getWorkOrderNo(), station.getSkid().getRfid(), station.getAreaName(), station.getLandNo());
                    if (start.getCode().equals(station.getCode())) {
                        for (int j = lands.size() - 1; j > 0; j--) {
                            PaintshopStation tem = lands.get(j);
                            PaintshopStation tem2 = lands.get(j + 1);
                            if (tem.getSkid() != null) {
                                tem2.setSkid(tem.getSkid());
                                tem2.setState(StationState.USE);
                            } else {
                                tem2.setSkid(null);
                                tem2.setState(StationState.FREE);
                            }
                        }
                        start.setSkid(skid);
                        start.setState(StationState.USE);
                        start.setInTime(new Date());
                        start.setPassInfoId(passInfoId);
                    } else if (end.getCode().equals(station.getCode())) {

                        for (int j = 0; j < lands.size() - 1; j++) {
                            PaintshopStation tem = lands.get(j);
                            PaintshopStation tem2 = lands.get(j + 1);
                            if (null != tem2.getSkid()) {
                                tem.setState(StationState.USE);
                                tem.setSkid(tem2.getSkid());
                            } else {
                                tem2.setSkid(null);
                                tem2.setState(StationState.FREE);
                            }
                        }
                        end.setState(StationState.USE);
                        end.setSkid(skid);
                        end.setInTime(new Date());
                        end.setPassInfoId(passInfoId);
                    }

                    String today = RfidMessageHandler.dateFormat.format(new Date());
                    //记录过站信息
                    String areaCode = station.getAreaId().toString();
                    String areaKey = Constant.BUS_SIZE_IN + areaCode + ":" + today;
                    Integer size = (Integer) valueOperations.get(areaKey);
                    if (null == size) {
                        size = 0;
                    } else {
                        size++;
                    }
                    valueOperations.set(areaKey, size);

                    Map<String, PaintshopStation> map = new HashMap<>();
                    hashOperations.putAll(Constant.STATION_OUT_PREFIX + key, map);

                    for (PaintshopStation land : lands) {
                        valueOperations.set(Constant.STATION_PREFIX + land.getCode(), land);
                    }
                    vehicle.setSkid(null);
                    vehicle.setCurrentRfid(null);
                    vehicle.setState(null);
                    valueOperations.set(Constant.VEHICLE_PREFIX + vehicle.getCode(), value);
                }
            }
        }
    }
}