package com.pwt.netty.client;

import HslCommunication.Authorization;
import HslCommunication.Core.Types.OperateResult;
import HslCommunication.Core.Types.OperateResultExOne;
import HslCommunication.Profinet.Siemens.SiemensPLCS;
import HslCommunication.Profinet.Siemens.SiemensS7Net;
import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.core.thread.ThreadUtil;
import com.pwt.Globals;
import com.pwt.cloud.utils.CloudUtil;
import com.pwt.common.enums.ChangePowerStep;
import com.pwt.netty.MessageSender;
import com.pwt.netty.common.plc.PlcCommand;
import com.pwt.netty.common.plc.PlcStateInfo;
import com.pwt.netty.conponents.AlarmCenter;
import com.pwt.netty.handler.ChargerServerHandler;
import com.pwt.netty.message2.Alarm;
import com.pwt.netty.message2.ExchangeProcess;
import com.pwt.netty.message2.RobotInfo;
import com.pwt.web.domain.PwtRobotFault;
import com.pwt.web.service.IPwtRobotFaultService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

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

import static com.pwt.netty.MessageSender.*;
import static com.pwt.netty.common.plc.PlcCommand.R_BatteryExists;

/**
 * 换电站PLC客户端
 *
 * @author zk
 */
@Slf4j
public class PlcClient {

    private static final String CODE = "e46237b8-f461-4861-8433-926f938cbc43";

    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("plcClientWorker", true));


    private String host = "192.168.3.2";
    private int port = 102;

    private List<PwtRobotFault> robotFaultList;

    private IPwtRobotFaultService robotFaultService;

    @Autowired
    public void setRobotFaultService(IPwtRobotFaultService robotFaultService) {
        this.robotFaultService = robotFaultService;
    }

    private final SiemensS7Net client;

    public PlcClient(String host, int port) {
        client = new SiemensS7Net(SiemensPLCS.S1200);
        client.setIpAddress(host);
        client.setPort(port);
        client.setRack((byte) 0);
        client.setSlot((byte) 0);
        client.setConnectTimeOut(1000);
        client.SetPersistentConnection();
    }

    private boolean isRunning;

    public synchronized void start() throws Exception {
        if (isRunning) {
            return;
        }
        if (!Authorization.SetAuthorizationCode(CODE)) {
            throw new Exception("授权失败！");
        }
//        this.client.setIpAddress(host);
//        this.client.setPort(port);
        isRunning = true;
        this.robotFaultList = robotFaultService.selectPwtRobotFaultList(new PwtRobotFault());
        scheduler.scheduleWithFixedDelay(new Task(), 1, 500, TimeUnit.MILLISECONDS);

    }

    public synchronized void stop() {
        if (!isRunning) {
            return;
        }
        scheduler.shutdown();
        isRunning = false;
    }


    public boolean sendCommand(PlcCommand command, boolean value) {
        OperateResult operateResult = client.Write(command.getAddress(), value);
        return operateResult.IsSuccess;
    }

    /**
     * 发送换仓命令
     *
     * @param source 取仓位
     * @param target 换仓位
     * @return true 发送成功, false 发送失败
     */
    public boolean sendChangeHolderCommand(short source, short target) {
        OperateResult operateResult = client.Write(PlcCommand.W_GetBatteryHolderIndex.getAddress(), new short[]{source, target});
        return operateResult.IsSuccess;
    }

    /**
     * 判断远程操作是否允许
     *
     * @return -1 获取信号失败 0 不允许  1 允许
     */
    public int isRemoteEnabled() {
        OperateResultExOne<Boolean> result = client.ReadBool(PlcCommand.R_RemoteOrLocal.getAddress());
        if (!result.IsSuccess) {
            return -1;
        }
        return result.Content ? 1 : 0;
    }

    /**
     * 获取换电步骤
     *
     * @return -1 获取信号失败 0 不允许  1 允许
     */
    public int getChargeStep() {
        OperateResultExOne<Integer> result = client.ReadUInt16(PlcCommand.R_ChargeStep.getAddress());
        if (!result.IsSuccess) {
            return -1;
        }
        return result.Content;
    }

    /**
     * 判断车是到位
     *
     * @return -1 获取信号失败 0 不允许  1 允许
     */
    public int isVehicleInPosition() {
        OperateResultExOne<Boolean> result = client.ReadBool(PlcCommand.R_VehicleInPosition.getAddress());
        if (!result.IsSuccess) {
            return -1;
        }
        return result.Content ? 1 : 0;
    }

    public boolean[] getHolderState() {
        OperateResultExOne<boolean[]> result = client.ReadBool(R_BatteryExists.getAddress(), (short) Globals.ACTUAL_HOLDER_COUNT);
        if (result.IsSuccess) {
            return result.Content;
        }
        return null;
    }

    /**
     * 是否换电完成
     *
     * @return -1 获取信号失败 0 不允许  1 允许
     */
    public int isChargeDone() {
        OperateResultExOne<Boolean> result = client.ReadBool(PlcCommand.R_ChargeDone.getAddress());
        if (!result.IsSuccess) {
            return -1;
        }
        return result.Content ? 1 : 0;
    }

    /**
     * 0:运行，1：待机
     */
    public int getRobotStatus() {
        OperateResultExOne<Boolean> result = client.ReadBool(PlcCommand.R_DeviceRunning.getAddress());
        if (!result.IsSuccess) {
            return 1;
        }
        return result.Content ? 0 : 1;
    }

    private static final String FAULT_START_ADDRESS = "DB11.36";
    private static final int FAULT_DATA_LENGTH = 352;

    private boolean[] faultData = new boolean[FAULT_DATA_LENGTH];

    private class Task implements Runnable {
        @Override
        public void run() {
            try {
                if (!readData()) {
                    Globals.plcStateInfo = null;
                }
            } catch (Exception e) {
                log.error("读取PLC数据失败，原因", e);
            }
        }

        private boolean readData() {

            // 读取故障信息
            OperateResultExOne<boolean[]> faultDataResult = client.ReadBool(FAULT_START_ADDRESS, (short) FAULT_DATA_LENGTH);

            boolean[] faults = faultDataResult.Content;
            if (!faultDataResult.IsSuccess || faults == null) {
                return false;
            }
            List<Alarm> addAlarms = new LinkedList<>();
            List<Alarm> removeAlarms = new LinkedList<>();
            if (robotFaultList != null && robotFaultList.size() > 0) {
                for (int i = 0; i < faults.length && i < robotFaultList.size(); i++) {
                    if (faults[i] != faultData[i]) {
                        PwtRobotFault fault = robotFaultList.get(i);
                        Alarm alarm = new Alarm(fault.getCode(), fault.getSourceDeviceType(), "1");
                        alarm.setLevel(fault.getFaultLevel());
                        alarm.setDescription(fault.getFault());
                        if (faults[i]) {
                            // 新增
                            addAlarms.add(alarm);
                            ThreadUtil.execute(() -> CloudUtil.alarmStart(alarm));
                        } else {
                            // 消除
                            alarm.setIsDelete(true);
                            removeAlarms.add(alarm);
                            ThreadUtil.execute(() -> CloudUtil.alarmFinish(alarm));
                        }
                    }
                }
            }

            faultData = faults;
            AlarmCenter.getInstance().addAlarms(addAlarms);
            AlarmCenter.getInstance().removeAlarms(removeAlarms);

            // 读取状态信息
            OperateResultExOne<PlcStateInfo> stateResult = client.ReadCustomer(R_BatteryExists.getAddress(), PlcStateInfo.class);

            if (!stateResult.IsSuccess) {
                return false;
            }

            refreshStateInfo(stateResult.Content);

            return true;
        }

        private void refreshStateInfo(PlcStateInfo info) {
            if (info == null) {
                return;
            }
            final CompletableFuture<Void> moveBatteryFuture = Globals.moveBatteryFuture;

            if (moveBatteryFuture != null) {
                ChangePowerStep step = info.getChangePowerStep();
                if (step.equals(ChangePowerStep.STEP_12)) {
                    // 通知future完成
                    moveBatteryFuture.complete(null);
                }
            }

            Globals.plcStateInfo = info;
            boolean[] batteryExists = info.getBatteryExists();
            for (int i = 0; i < Globals.ACTUAL_HOLDER_COUNT; i++) {
                if (!batteryExists[i]) {
                    int holderIndex = i + 1;
                    // 移除缓存
                    ChargerServerHandler.removeBatteryData(holderIndex);
                }
            }
            RobotInfo robotInfo = new RobotInfo();
            ExchangeProcess process = new ExchangeProcess();
            int robotState = info.getRobotState();
            if (robotState == 2) {//如果是故障，以故障优先
                robotInfo.setState(info.getRobotState());
            } else {
                robotInfo.setState(getRobotStatus());
            }
            robotInfo.setX(info.getXPosition());
            robotInfo.setY(info.getYPosition());
            robotInfo.setZ(info.getZPosition());
            process.setExchangeProcess(info.getChangePowerStep());

            Globals.uiData.setRobot(robotInfo);
            Globals.uiData.setExchangeState(process);

            Map<String, Object> data = new HashMap<>(2);
            data.put(MSG_ROBOT, robotInfo);
            data.put(MSG_EXCHANGE_PROCESS, process);
            MessageSender.send(data);
        }
    }
}
