package org.nl.acs.auto.run;

import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.nl.acs.AcsConfig;
import org.nl.acs.agv.server.NDCAgvService;
import org.nl.acs.device.domain.Device;
import org.nl.acs.device.service.DeviceService;
import org.nl.acs.device_driver.basedriver.agv.ndcone.AgvNdcOneDeviceDriver;
import org.nl.acs.ext.wms.service.AcsToWmsService;
import org.nl.acs.ext.wms.service.impl.AcsToWmsServiceImpl;
import org.nl.acs.instruction.domain.Instruction;
import org.nl.acs.instruction.service.InstructionService;
import org.nl.acs.instruction.service.impl.InstructionServiceImpl;
import org.nl.acs.log.service.DeviceExecuteLogService;
import org.nl.acs.opc.DeviceAppService;
import org.nl.config.SpringContextHolder;
import org.nl.system.service.dict.ISysDictService;
import org.nl.system.service.lucene.LuceneExecuteLogService;
import org.nl.system.service.param.ISysParamService;
import org.nl.system.service.param.impl.SysParamServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import static org.nl.acs.agv.server.impl.NDCAgvServiceImpl.Bytes2HexString;


@Slf4j
@Component
@Lazy
public class OneNDCSocketConnectionAutoRun extends AbstractAutoRunnable {

    Socket s;
    String ip = "192.168.10.34";
    int port = 1234;
    static DataOutputStream dos;
    static DataInputStream dis;
    private int recordTimeOut = 10000;
    private Date recordTime;
    String[] ERROR = new String[]{
            "货叉尖部传感器触发", "S300传感器触发", "载货状态改变", "急停按钮触发", "触边开关出发", "需要复位",
            "停在充电位", "取货失败", "放货失败", "轮子打滑", "没有动作码不能进入站点", "取货时有货", "丢失定位",
            "抬叉停止"};
    boolean bConnected = true;

    boolean isReConnect = false;

    @Autowired
    ISysParamService paramService;
    @Autowired
    AutoRunService autoRunService;
    @Autowired
    LuceneExecuteLogService luceneExecuteLogService;
    @Autowired
    ISysDictService dictService;


    public OneNDCSocketConnectionAutoRun() {
        this.recordTime = new Date((new Date()).getTime() - (long) this.recordTimeOut);
    }

    public String getCode() {
        return OneNDCSocketConnectionAutoRun.class.getSimpleName();
    }

    public String getName() {
        return "NDC在线连接";
    }

    //todo NDC相关逻辑
    public void autoRun() throws IOException {
        System.out.println("NDCAgv链接开始");
        ISysParamService ISysParamService = SpringContextHolder.getBean(SysParamServiceImpl.class);
        InstructionService instructionService = SpringContextHolder.getBean(InstructionServiceImpl.class);
        AcsToWmsService acsToWmsService = SpringContextHolder.getBean(AcsToWmsServiceImpl.class);
        NDCAgvService NDCAgvService = SpringContextHolder.getBean(NDCAgvService.class);
        DeviceAppService deviceAppService = SpringContextHolder.getBean(DeviceAppService.class);
        DeviceService deviceService = SpringContextHolder.getBean(DeviceService.class);
        DeviceExecuteLogService logServer = SpringContextHolder.getBean(DeviceExecuteLogService.class);
        ip = paramService.findByCode(AcsConfig.AGVURL).getValue();
        port = Integer.parseInt(paramService.findByCode(AcsConfig.AGVPORT).getValue());


        try {
            byte[] b = new byte[1024];
            s = new Socket(ip, port);
            dos = new DataOutputStream(s.getOutputStream());
            dis = new DataInputStream(s.getInputStream());
            System.out.println("NDCAgv链接成功");
            log.info("NDCAgv链接成功");
            while (bConnected) {
                int count = dis.read(b);

                if (count == -1) {
                    break;
                }

                int[] packetData = new int[count];
                StringBuffer bs = new StringBuffer();

                for (int i = 0; i < count; i++) {
                    int temp = b[i];
                    if (temp < 0)
                        temp += 256;
                    packetData[i] = temp;
                    StringBuffer bs1 = new StringBuffer("0");
                    bs.append(temp < 16 ? bs1.append(Integer.toHexString(temp)) : Integer.toHexString(temp));
                }
                // 从字节流的开头开始读取
                int offset = 0;
                // 循环处理字节流，逐个拆包 按照S消息解析
                while (offset < packetData.length) {
                    if (offset + 6 > packetData.length) {
                        System.out.println("错误：没有足够的字节来读取长度字段。");
                        break;
                    }
                    int messageHeader = packetData[offset] * 256 + packetData[offset+1];
                    //检测消息头是否有效
                    if (messageHeader == 0x87CD){
                        //消息头长度
                        int messageHeaderLength = packetData[offset+2] * 256 + packetData[offset+3];
                        //消息数据长度
                        int messageDataLength = packetData[offset+4] * 256 + packetData[offset+5];
                        //消息总长度
                        int messageLength = messageHeaderLength + messageDataLength;
                        // 检查剩余字节是否足够处理完整的数据包
                        if (offset + messageLength > packetData.length) {
                            System.out.println("错误：检测到不完整的数据包。");
                            // 数据包不完整，停止处理
                            break;
                        }
                        int[] arr =  Arrays.copyOfRange(packetData, offset, offset+messageLength);
                        boolean flag = false;
                        if (arr[8] * 256 + arr[9] == 0x73) {
                            byte[] data = null;
                            System.out.println("接收agv上报信息：" + bs);
                            //执行阶段
                            int phase = arr[16] * 256 + arr[17];
                            // agv任务号
                            int index = arr[12] * 256 + arr[13];
                            //任务号
                            int ikey = arr[26] * 256 + arr[27];
                            //站点号
                            int agvaddr = arr[18] * 256 + arr[19];
                            //车号
                            int carno = arr[20];
                            //充电桩站点号
                            int station = arr[25];
                            Instruction link_inst = null;
                            Instruction inst = null;
                            List<Instruction> insts = null;
                            boolean link_flag = false;
                            Device agv_device = null;
                            if (carno != 0) {
                                agv_device = deviceAppService.findDeviceByCode(String.valueOf(carno));
                            }
                            if (ikey != 0) {
                                inst = instructionService.findByCodeFromCache(String.valueOf(ikey));
                                if (ObjectUtil.isEmpty(inst)) {
                                    inst = instructionService.findByCode(String.valueOf(ikey));
                                }
                            }
                            if (!ObjectUtil.isEmpty(link_inst)) {
                                link_flag = true;
                            }
                            log.info("接收agv上报信息：" + bs);
                            log.info("接收agv上报信息：" + "phase--" + phase + "  index--" + index + "  ikey--" + ikey + "  agvaddr--" + agvaddr + "  Car--" + carno);
                            Device device = null;
                            String device_code = null;


                            //
                            AgvNdcOneDeviceDriver agvNdcOneDeviceDriver;

                            //开始任务/上报订单号
                            if (phase == 0x01) {
                                if (!ObjectUtil.isEmpty(inst)) {
                                    inst.setInstruction_status("1");
                                    inst.setAgv_jobno(String.valueOf(index));
                                    inst.setSend_status("1");
                                    instructionService.update(inst);
                                    data = NDCAgvService.sendAgvOneModeInst(phase, index, 0, 0, 0, 0, 0);
                                }
                            }
                            //任务完毕
                            //（无车id及状态）
                            else if (phase == 0x0A) {
                                if (!ObjectUtil.isEmpty(inst)) {
                                    instructionService.finish(inst.getInstruction_id());
                                } else {
                                    log.warn("指令号:{},未反馈wms任务完成，因为agv上报指令号查询指令为空!");
                                }
                                data = NDCAgvService.sendAgvOneModeInst(phase, index, 0, 0, 0, 0, 0);
                                log.info("指令号:{},已反馈agv任务完成!");
                            }
                            //todo 检查协议里删除任务流程有很多个步骤，在哪体现的
                            //(需要WCS反馈)
                            else if (phase == 0x30) {
                                data = NDCAgvService.sendAgvOneModeInst(0x8F, index, 0, 0, 0, 0, 0);
                            }
                            //任务删除确认
                            //(需要WCS反馈)
                            else if (phase == 0xFF) {

                                if (!ObjectUtil.isEmpty(inst)) {
                                    if (!ObjectUtil.isEmpty(inst)) {
                                        instructionService.cancelNOSendAgv(inst.getInstruction_id());
                                    }
                                }
                                data = NDCAgvService.sendAgvOneModeInst(phase, index, 0, 0, 0, 0, 0);
                            } else {
                                //上报异常信息
                                //（不需要WCS反馈）
                                if (phase == 0x70 || phase == 0x71 || phase == 0x72 || phase == 0x73 || phase == 0x74 || phase == 0x75) {
                                    device = deviceAppService.findDeviceByCode(String.valueOf(agvaddr));
                                } else {
                                    //todo NDC根据车辆编号123456进行查找特定车辆
                                    device = deviceAppService.findDeviceByCode(Integer.toString(arr[20]));
                                }
                                if (ObjectUtil.isNotEmpty(device)) {
                                    if (device.getDeviceDriver() instanceof AgvNdcOneDeviceDriver) {
                                        agvNdcOneDeviceDriver = (AgvNdcOneDeviceDriver) device.getDeviceDriver();
                                        agvNdcOneDeviceDriver.processSocket(arr);
                                    }
                                }

                            }
                            if (!ObjectUtil.isEmpty(data)) {
                                write(data);
                            }

                        } else {
                            System.out.println("agv上报不是0073类型动作，不处理");
                        }
                        // 更新偏移量，继续处理下一个数据包
                        offset+=messageLength;
                    }else {
                        System.out.println("消息头无效，不处理。");
                        break;
                    }
                }
            }

        } catch (Exception e) {
            System.out.println("Agv链接异常");
            log.info("Agv链接异常");
            log.error("agv连接出现异常:{}", e);
            if (ObjectUtil.isNotEmpty(s)) {
                s.close();
            }
            System.out.println(e.getMessage());
            e.printStackTrace();

        } finally {


        }

    }


    @Override
    public void stop() {
        super.after();
        try {
            s.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static void write(byte[] b) {
        try {
            log.info("下发agv数据:" + Bytes2HexString(b));
            System.out.println("下发agv数据:" + Bytes2HexString(b));
            dos.write(b);
            dos.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            try {
                Thread.sleep(5000L);
                log.error("tofix下发agv数据失败，再次下发，再次下发agv数据:" + Bytes2HexString(b) + "失败原因：" + e.getMessage());
                System.out.println("再次下发agv数据:" + Bytes2HexString(b));
                dos.write(b);
                dos.flush();
            } catch (Exception e1) {
                log.error("tofix下发agv数据失败，再次下发，下发agv数据:" + Bytes2HexString(b) + "失败原因：" + e1.getMessage());
                e1.printStackTrace();
            }

        }
    }
}

