package  org.ts.ddcs.dataStream;

import lombok.extern.slf4j.Slf4j;
import org.ts.ddcs.common.constant.GBConstants;
import org.ts.ddcs.net.DataChannelHolder;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;

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


/**
 * 适配器，用于连接网络接口与数据处理器
 *
 * @author dylan
 */
@Slf4j
public abstract class DataStreamAdapter {

    protected LinkedBlockingQueue<byte[]> blockQueue = new LinkedBlockingQueue<>();

    private byte[] remainBuff = new byte[0];

    protected ChannelHandlerContext ctx;


    /**
     * 连接创建时间
     */
    private Date createTime;

 //   private String channelType;

    public Map<String, Object> keyStore = new ConcurrentHashMap<>();

    protected String remoteIp;
    protected String rtuCode;
    protected String adapterKey;
    protected String dataStreamLineKey;
    private boolean isClose = false;
    private int waitingProcessBlocks = 0;

    public DataStreamAdapter() {
      //  this.channelType = dataChannelType;
        this.adapterKey = UUID.randomUUID().toString().replace("-", "").toLowerCase();
    }

    public void init(String remoteIp) {
        log.info("{} 初始化连接 ip {}",this.adapterKey,remoteIp);
        this.remoteIp = remoteIp;
        this.createTime = new Date();
        DataStreamManageHolder.newInstance().dataStreamManager.regConnect(this);
         DataChannelHolder.newInstance().dataChannel.online(this);
    }

    public boolean isClose() {
        return this.isClose;
    }

    public String getCode() {
        return this.rtuCode;
    }

    public void setCode(String code) {
        this.rtuCode = code;
    }

    public String getAdapterKey() {
        return this.adapterKey;
    }

    boolean processComplete() {
        if (remainBuff != null && remainBuff.length > 0) {
            return false;
        }
        return true;
    }

    void setDataStreamLineKey(String dataStreamLineKey) {
        this.dataStreamLineKey = dataStreamLineKey;
    }

    String getDataStreamLineKey() {
        return this.dataStreamLineKey;
    }

    public byte[] getBlock() {
        try {
            if (null == blockQueue) {
                return new byte[0];
            }
            byte[] blockBuff = new byte[0];
            if (remainBuff.length > 0) {
                blockBuff = new byte[remainBuff.length];
                System.arraycopy(remainBuff, 0, blockBuff, 0, remainBuff.length);
            }
            if (!blockQueue.isEmpty()) {
                byte[] block = blockQueue.poll();
                if (blockBuff.length > 0) {
                    byte[] tmpBuff = new byte[blockBuff.length + block.length];
                    System.arraycopy(blockBuff, 0, tmpBuff, 0, blockBuff.length);
                    System.arraycopy(block, 0, tmpBuff, blockBuff.length, block.length);
                    blockBuff = tmpBuff;
                } else {
                    blockBuff = block;
                }
            }
            remainBuff = new byte[blockBuff.length];
            System.arraycopy(blockBuff, 0, remainBuff, 0, blockBuff.length);
            return blockBuff;
        } catch (Exception e) {
            log.error(this.getClass().getName(),e);
            return new byte[0];
        }
    }

    public byte[] getBlock(int index) {
        try {
            if (null == remainBuff) {
                return new byte[0];
            }
            byte[] blockBuff = new byte[0];
            if (remainBuff.length >= index) {
                blockBuff = new byte[index];
                System.arraycopy(remainBuff, 0, blockBuff, 0, index);
            }
            return blockBuff;
        } catch (Exception e) {
            log.error(this.getClass().getName(),e);
            return new byte[0];
        }
    }

    void readIndex(int index) {
        if (index >= remainBuff.length) {
            remainBuff = new byte[0];
        } else {
            byte[] tmpBuff = new byte[remainBuff.length - index];
            System.arraycopy(remainBuff, index, tmpBuff, 0, remainBuff.length - index);
            remainBuff = tmpBuff;
        }
    }

    public Date getCreateTime() {
        return this.createTime;
    }


    public void close() {
        log.info("{} 连接关闭",this.adapterKey );
        DataChannelHolder.newInstance().dataChannel.offline(this);
        this.ctx = null;
        this.isClose = true;
        if (null == this.remainBuff || this.remainBuff.length == 0) {
            DataStreamManageHolder.newInstance().dataStreamManager.closeConnect(this);
        }
    }


    int getWaitingProcessBlocksCount() {
        return this.waitingProcessBlocks;
    }

    void resetWaitingProcessBlocksCount() {
        this.waitingProcessBlocks = 0;
    }

    void addWaitingProcessBlocksCount() {
        this.waitingProcessBlocks++;
    }

    int moveToNext(String agreement) {
//        if (null != agreement && agreement.equals(GBConstants.AGREEMENT_KIND_SW)) {
//            for (int i = 3; i < this.remainBuff.length; i++) {
//                if (this.remainBuff[i - 1] == 0x7E && this.remainBuff[i] == 0x7E) {
//                    return i - 1;
//                }
//            }
//        } else if (null != agreement && agreement.equals(GBConstants.AGREEMENT_KIND_SZY)) {
//            for (int i = 5; i < this.remainBuff.length; i++) {
//                if (this.remainBuff[i - 2] == 0x68 && this.remainBuff[i] == 0x68) {
//                    return i - 2;
//                }
//            }
//        }
        return 0;
    }


    /**
     * 输出到数据连接通道
     *
     * @param respBody
     */
    public abstract void output(byte[] respBody);

    /**
     * UDP数据包
     *
     * @param buf
     * @param cx
     * @param datagramPacket
     */
    public abstract void pushData(ByteBuf buf, ChannelHandlerContext cx, DatagramPacket datagramPacket);

    /**
     * TCP数据包
     *
     * @param buf
     * @param cx
     */
    public abstract void pushData(ByteBuf buf, ChannelHandlerContext cx);

}
