package com.tykj.dev.rfid.service.impl;

import com.tykj.dev.device.library.service.DeviceLibraryService;
import com.tykj.dev.device.library.subject.domin.DeviceLibrary;
import com.tykj.dev.misc.exception.ApiException;
import com.tykj.dev.misc.utils.ResultUtil;
import com.tykj.dev.rfid.entity.vo.PrintVo;
import com.tykj.dev.rfid.entity.vo.RfidCreateVo;
import com.tykj.dev.rfid.entity.vo.RfidPrintVo;
import com.tykj.dev.rfid.entity.vo.SplitStringInfo;
import com.tykj.dev.rfid.service.RfidService;
import com.zebra.sdk.comm.Connection;
import com.zebra.sdk.comm.ConnectionException;
import com.zebra.sdk.comm.TcpConnection;
import com.zebra.sdk.printer.PrinterLanguage;
import com.zebra.sdk.printer.ZebraPrinter;
import com.zebra.sdk.printer.ZebraPrinterFactory;
import com.zebra.sdk.printer.ZebraPrinterLanguageUnknownException;
import com.zebra.sdk.printer.discovery.DiscoveredUsbPrinter;
import com.zebra.sdk.printer.discovery.UsbDiscoverer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 描述：用于调用RFID打印机打印标签
 *
 * @author HuangXiahao
 * @version V1.0
 **/
@Service
@Slf4j
public class RfidServiceImpl implements RfidService {

    private static final int RFID_MAX_LENGTH= 24;
    private static final int RFID_LINE_LENGTH = 24;
    private static final Integer DEFAULT_USB_INDEX = 0;
    private static ZebraPrinter printer;
    @Resource
    private DeviceLibraryService deviceLibraryService;
    /**
     * 描述：设置打印机网络端口
     *
     * @param ip   IP
     * @param port 端口号
     */
    @Override
    public void setNetWorkConnection(String ip, Integer port) {
        try {
            //根据IP和PORT获取Connection
            Connection connection = new TcpConnection(ip, port);
            connection.open();
            //获取打印机实力
            printer = ZebraPrinterFactory.getInstance(connection);
        } catch (ConnectionException | ZebraPrinterLanguageUnknownException e) {
            throw new ApiException(ResultUtil.failed("未找到对应的端口，请检查网络"));
        }
    }

    /**
     * 描述：自动扫描本机的打印机接口
     */
    @Override
    public void scanUsbConnection() {
        try {
            //根据IP和PORT获取Connection
            DiscoveredUsbPrinter[] zebraUsbPrinters = UsbDiscoverer.getZebraUsbPrinters();
            Connection connection = zebraUsbPrinters[DEFAULT_USB_INDEX].getConnection();
            //获取打印机实力
            printer = ZebraPrinterFactory.getInstance(connection);
        } catch (ConnectionException | ZebraPrinterLanguageUnknownException e) {
            throw new ApiException(ResultUtil.failed("未找到对应的端口，请检查网络"));
        }
    }

    /**
     * 描述：调用打印机打印内容（如出现打印不对齐的情况请联系管理员）
     *
     * @return
     */
    @Override
    public boolean printString( List<PrintVo> printVos)  {
        if (printVos==null&&printVos.size()<=0){
            throw new ApiException("打印列表为空");
        }
        try {
            Connection connection = new TcpConnection("10.153.4.16", 6101);
            connection.open();
            for (PrintVo printVo : printVos) {
                printer = ZebraPrinterFactory.getInstance(connection);
                String demoFile = createDemoFile(printer.getPrinterControlLanguage(),printVo.getRfidContent(),printVo.getStr1(), printVo.getStr2(),printVo.getStr3(), 55, 50);
                printer.sendFileContents(demoFile);
            }
            connection.close();
        } catch (ConnectionException e) {
            throw new ApiException("未找到对应的打印机");
        } catch (ZebraPrinterLanguageUnknownException e) {
            throw new ApiException("打印机内容错误");
        } catch (IOException e) {
            throw new ApiException("生成打印文件失败");
        }
        return true;
    }


    /**
     * @param pl         打印机语言
     * @param str1       空格一
     * @param str2       空格二
     * @param rfidContent    标签数据区内容
     * @param marginLeft 标签左边距
     * @param marginTop  标签顶部边距
     * @return 拼接成功的打印机ZPL
     * @throws IOException IO错误
     */


    private String createDemoFile(PrinterLanguage pl,String rfidContent ,String str1,String str2,String str3, int marginLeft, int marginTop) throws IOException {
        if (!checkRFID(rfidContent)){
            throw new ApiException("");
        }

        File tmpFile = File.createTempFile("TEST_ZEBRA", "LBL");
        FileOutputStream os = new FileOutputStream(tmpFile);
        byte[] configLabel = null;
        SplitStringInfo deviceNumberSplit = spiltLineString("型 号:"+str1, null);
        SplitStringInfo contentSplit = spiltLineString("名 称:"+str2, deviceNumberSplit.getStringLineNumber());
        SplitStringInfo str3Split = spiltLineString("序列号:"+str3, contentSplit.getStringLineNumber());
        //补零
        rfidContent = completeRfidStr(rfidContent);
        if (pl == PrinterLanguage.ZPL) {
            configLabel = ("^XA\n" +
//                    "~SD25\n" +
                    "^CI28"+
                    "^CW1,E:SIMSUN.TTF"+
                    "^RS,B30\n"+
                    "^RFW,H\n" +
                    "^FD"+rfidContent+"\n" +
                    "^FS\n" +
                    "^LH" + marginLeft + "," + marginTop + "\n" +
                    deviceNumberSplit.getSplitString()+
                    contentSplit.getSplitString()+
                    str3Split.getSplitString()+
                    "^PQ1,0,1,Y\n" +
                    "^XZ").getBytes();
        }
        os.write(configLabel);
        os.flush();
        os.close();
        return tmpFile.getAbsolutePath();
    }

    /**
     * @param rfidStr 主键id
     * @return rfid拼接字符串
     * 补全核心装备rfid卡号，首位为0
     */
    public static String completeRfidStr(String rfidStr){
        int needCompleteLength = RFID_MAX_LENGTH - rfidStr.length();
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < needCompleteLength; i++) {
            stringBuilder.append("0");
        }
        return stringBuilder.append(rfidStr).toString().toUpperCase();
    }

    /**
     * @param rfidStr 主键id
     * @return rfid拼接字符串
     * 补全配套设备rfid卡号，首位为1
     */
    public static String completeRfidStr2(String rfidStr){
        int needCompleteLength = RFID_MAX_LENGTH - rfidStr.length();
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("1");
        for (int i = 0; i < needCompleteLength-1; i++) {
            stringBuilder.append("0");
        }
        return stringBuilder.append(rfidStr).toString().toUpperCase();
    }

    /**
     * 计算RFID每行的ZPL
     * @param content
     * @return
     */
    public static SplitStringInfo spiltLineString(String content, Integer firstLine){
        //中文占两个字符,使用正则匹配所有的非字符并将lenght*2
        StringBuilder resultStringBuild = new StringBuilder();
        Integer line = firstLine!=null?firstLine:0;
        int totalLine = getLineNum(content,RFID_LINE_LENGTH);
        for (int i = 0; i < totalLine; i++) {
            String cutedStr = cutStr(content, RFID_LINE_LENGTH);
            resultStringBuild.append("^A1N,25,^FR^FD\n^FO0,"+line*30+"^FD"+cutedStr+"^FS\n");
            content = content.substring(cutedStr.length());
            line = line+1;
        }
        return new SplitStringInfo(resultStringBuild.toString(),line);
    }

    public static int getLineNum(String str, int cutLength){
        int changedStrLenght = str.replaceAll("[^\\x00-\\xff]","**").length();
        int num = changedStrLenght/cutLength;
        if (changedStrLenght%cutLength>0){
            num++;
        }
        return num;
    }

    public static String cutStr(String str , int cutLength){
        int strlen = str.length();
        int chrlen = str.replaceAll("[^\\x00-\\xff]","**").length();
        if(chrlen<=cutLength){
            return str;
        }
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0, j=0; i < strlen; i++) {
            Character chr = str.charAt(i);
            if(chr<=255&&chr>=0){
                j++; // ascii码为0-255，一个字符就是一个字节的长度
            }else {
                j+=2;
            }
            if(j<=cutLength){ // 当加上当前字符以后，如果总字节长度小于等于L，则将当前字符真实的+在result后
                stringBuilder.append(chr);
            }else{ // 反之则说明result已经是不拆分字符的情况下最接近L的值了，直接返回
                return stringBuilder.toString();
            }
        }
        return "";
    }

    /**
     * 校验RFID的格式
     * @param rfidStr 写入标签的数据
     * @return
     */
    public static Boolean checkRFID(String rfidStr){
        int length = rfidStr.length();
        if (length>24){
            return false;
        }
        String pattern = "^[A-Z0-9]+";
        return Pattern.matches(pattern, rfidStr);
    }

    public static void main(String[] args) throws ConnectionException, ZebraPrinterLanguageUnknownException, IOException {
//        Connection connection = new TcpConnection("192.168.0.222", 6101);
//        connection.open();
//        printer = ZebraPrinterFactory.getInstance(connection);
//        String demoFile = createDemoFile(printer.getPrinterControlLanguage(),"1234","装备号：xxxxxxxxxxxxxxxxxxxxxxxx", "内容：ABCD","序列号：123", 60, 80);
//        printer.sendFileContents(demoFile);

    }


    /**
     * 描述：生成装备的RFID表面号
     *
     * @param list RFID表面号生成类
     * @return
     */
    @Override
    public List<RfidPrintVo> getRfidNumber(List<RfidCreateVo> list) {
        List<RfidPrintVo> rfidPrintVos = new ArrayList<>();
        //遍历List为每一个创建元素生成对应的RFID表面号
        for (RfidCreateVo r : list) {
            //如果传入的创建元素中不包含装备ID的话，则生成新的表面号
            //否则根据将已经装备的表面号插入LIST
            RfidPrintVo rfidPrintVo = new RfidPrintVo();
            if (r.getDeviceTId() == null) {
                rfidPrintVo.setMakeNumber(r.getProduceNumber());
                rfidPrintVo.setDeviceNumber(r.getDeviceNumber());
//                rfidPrintVo.setContent(makeRfidNumber(r.getPartType(), r.getDeviceNumber(), r.getProduceNumber(), r.getDeviceType()));
                rfidPrintVo.setContent(r.getDeviceNumber());
                rfidPrintVos.add(rfidPrintVo);
            } else {
                DeviceLibrary device = deviceLibraryService.getOne(r.getDeviceTId());
                rfidPrintVo.setDeviceNumber(device.getSeqNumber());
                rfidPrintVo.setMakeNumber(device.getProdNumber());
                rfidPrintVo.setContent(device.getRfidSurfaceId());
                rfidPrintVos.add(rfidPrintVo);
            }
        }
        return rfidPrintVos;
    }

    /**
     * 描述：生成装备的RFID表面号
     *
     * @param deviceType    装备类型 共10位
     * @param partType      配件类型 主体为0 共1位
     * @param deviceNumber  装备序列号 由用户填写
     * @param produceNumber 生产序列号 用户填写
     * @return 生成的RFID表面号
     */
    public String makeRfidNumber(String partType, String deviceNumber, String produceNumber, String deviceType) {
        //配件类型的长度只能为1
        if (partType.length() > 1) {
            throw new IllegalArgumentException("配件类型字段过长");
        }
        StringBuilder stringBuffer = new StringBuilder();
        if (deviceType.length() <= 10) {
            for (int i = 0; i < 10 - deviceType.length(); i++) {
                stringBuffer.append("0");
            }
            stringBuffer.append(deviceType);
        } else {
            stringBuffer.append(deviceType.substring(deviceType.length() - 10));
        }
        stringBuffer.append("p");
        stringBuffer.append(partType);

        if (deviceNumber.length() < 4) {
            for (int i = 0; i < 4 - deviceNumber.length(); i++) {
                stringBuffer.append("0");
            }
            stringBuffer.append(deviceNumber);
        } else {
            stringBuffer.append(deviceNumber.substring(deviceNumber.length() - 4));
        }
        if (produceNumber==null){
            stringBuffer.append("0000");
        }else {
            if (produceNumber.length() < 4) {
                for (int i = 0; i < 4 - produceNumber.length(); i++) {
                    stringBuffer.append("0");
                }
                stringBuffer.append(produceNumber);
            } else {
                stringBuffer.append(produceNumber.substring(produceNumber.length() - 4));
            }
        }
        return stringBuffer.toString();
    }



}
