package com.its.pis.xnettcp.vds.protocol;

import com.google.common.primitives.Bytes;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Getter
public class VdsReqFramePacket {

    private ByteOrder byteOrder = VdsProtocol.byteOrder;

    protected VdsReqFrameHead head;
    protected byte[]          body;
    protected VdsReqFrameTail tail;

    public VdsReqFramePacket() {
        this.head = new VdsReqFrameHead();
        this.body = null;
        this.tail = new VdsReqFrameTail();
    }

    public VdsReqFramePacket(short groupNo, short controllerNo, byte opCode) {
        this.head = new VdsReqFrameHead(groupNo, controllerNo, opCode);
        this.body = null;
        this.tail = new VdsReqFrameTail();
    }

    public VdsReqFramePacket(VdsReqFrameHead head, byte[] body, VdsReqFrameTail tail) {
        this.head = head;
        this.body = body;
        this.tail = tail;
    }

    public void setByteOrder(ByteOrder byteOrder) {
        this.byteOrder = byteOrder;
    }

    /**
     * 요청 패킷을 ByteBuffer 로 변경하여 리턴
     * @return
     */
    public ByteBuffer getByteBuffer() {
        if (VdsProtocol.isDleStuffing) {
            return getDleBuffer();
        }
        return getBuffer();
    }

    private ByteBuffer getDleBuffer() {
        List<Byte> list = new ArrayList<Byte>();
        int bodySize = 0;
        if (this.body != null) {
            bodySize = this.body.length;
        }

        // HEAD (패킷 분석결과 DLE Stuffing 하지 않음)
        list.add(this.head.getDle());
        list.add(this.head.getStx());

        byte groupNo1 = (byte)((this.head.getGroupNo() >> 8 ) & 0xFF);
        byte groupNo2 = (byte)((this.head.getGroupNo()      ) & 0xFF);
        if (VdsProtocol.byteOrder == ByteOrder.LITTLE_ENDIAN) {
            groupNo2 = (byte)((this.head.getGroupNo() >> 8 ) & 0xFF);
            groupNo1 = (byte)((this.head.getGroupNo()      ) & 0xFF);
        }
        list.add(groupNo1);
//        if (groupNo1 == VdsProtocol.vds_DLE) {
//            list.add(groupNo1);
//        }
        list.add(groupNo2);
//        if (groupNo2 == VdsProtocol.vds_DLE) {
//            list.add(groupNo2);
//        }

        byte controllerNo1 = (byte)((this.head.getControllerNo() >> 8 ) & 0xFF);
        byte controllerNo2 = (byte)((this.head.getControllerNo()      ) & 0xFF);
        if (VdsProtocol.byteOrder == ByteOrder.LITTLE_ENDIAN) {
            controllerNo2 = (byte)((this.head.getControllerNo() >> 8 ) & 0xFF);
            controllerNo1 = (byte)((this.head.getControllerNo()      ) & 0xFF);
        }
        list.add(controllerNo1);
//        if (controllerNo1 == VdsProtocol.vds_DLE) {
//            list.add(controllerNo1);
//        }
        list.add(controllerNo2);
//        if (controllerNo2 == VdsProtocol.vds_DLE) {
//            list.add(controllerNo2);
//        }

        list.add(this.head.getOpCode());

        // BODY
        if (bodySize > 0) {
            for (byte b: this.body) {
                list.add(b);
                if (b == VdsProtocol.vds_DLE) {
                    list.add(b);
                }
            }
        }

        // TAIL
        list.add(this.tail.getDle());
        list.add(this.tail.getEtx());
        byte crc1 = (byte)((this.tail.getCrc() & 0xFF00) >> 8);
        byte crc2 = (byte)((this.tail.getCrc() & 0x00FF));
        if (VdsProtocol.byteOrder == ByteOrder.LITTLE_ENDIAN) {
            crc2 = (byte)((this.tail.getCrc() >> 8 ) & 0xFF);
            crc1 = (byte)((this.tail.getCrc()      ) & 0xFF);
        }
        list.add(crc1);
        list.add(crc2);

        byte[] arr = Bytes.toArray(list);
        ByteBuffer byteBuffer = ByteBuffer.allocate(arr.length);
        byteBuffer.order(this.byteOrder);
        byteBuffer.put(arr);
        return byteBuffer;
    }
    private ByteBuffer getBuffer() {
        int bodySize = 0;
        if (this.body != null) {
            bodySize = this.body.length;
        }
        ByteBuffer byteBuffer = ByteBuffer.allocate(VdsReqFrameHead.SIZE + bodySize + VdsReqFrameTail.SIZE);
        byteBuffer.order(this.byteOrder);

        // HEAD
        byteBuffer.put(this.head.getDle());
        byteBuffer.put(this.head.getStx());
        byteBuffer.putShort(this.head.getGroupNo());
        byteBuffer.putShort(this.head.getControllerNo());
        byteBuffer.put(this.head.getOpCode());

        // BODY
        if (bodySize > 0) {
            byteBuffer.put(this.body);
        }

        // TAIL
        byteBuffer.put(this.tail.getDle());
        byteBuffer.put(this.tail.getEtx());
        byteBuffer.putShort(this.tail.getCrc());

        return byteBuffer;
    }

    public void makeCRC() {
        // DLE Stuffing 이전 데이터에 대하여 CRC 수행
        ByteBuffer byteBuffer = getBuffer();
        short crc = VdsProtocol.getCRC16ARC(byteBuffer.array(), 2, byteBuffer.array().length - 6);
        //byte crc1 = (byte)((crc >> 8) & 0xFF);
        //byte crc2 = (byte)((crc     ) & 0xFF);
        //byteBuffer.put(crc1); // CRC1
        //byteBuffer.put(crc2); // CRC2
        this.tail.setCrc(crc);
    }
}
