package ttk;

import tlv.TlvItem;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;

public class Buffer {
    public enum MessageTypes {CLIENT_REQUEST, SERVER_ANSWER, UNKNOWN}

    private MessageTypes messageType = MessageTypes.UNKNOWN;
    private byte protocolVersion = (byte) 0xf2;

    public ArrayList<tlv.TlvItem> tagContainer = new ArrayList<>();

    public Buffer(MessageTypes type, ArrayList<tlv.TlvItem> tagContainer) {
        messageType = type;
        this.tagContainer = tagContainer;
    }

    public Buffer(byte[] data) {
        if (data.length > 4) {
            //get message type
            switch (data[2]) {
                case (byte) 0x96:
                    this.messageType = Buffer.MessageTypes.CLIENT_REQUEST;
                    break;
                case (byte) 0x97:
                    this.messageType = Buffer.MessageTypes.SERVER_ANSWER;
                    break;
                default:
                    break;
            }
            //get protocol version
            this.protocolVersion = data[3];

            //get tlv items
            this.tagContainer = TlvItem.parseBinaryBuffer(Arrays.copyOfRange(data, 4, data.length));

            //TODO: verify length
        }
    }

    public void print() {
        String controlFlow;

        switch (messageType) {
            case CLIENT_REQUEST:
                controlFlow = ">>";
                break;
            case SERVER_ANSWER:
                controlFlow = "<<";
                break;
            case UNKNOWN:
            default:
                controlFlow = "??";
        }
        System.out.println(controlFlow);
        for (tlv.TlvItem it : tagContainer) {
            it.print();
        }

        System.out.println();
    }

    byte[] makeFrame() {
        ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream();

        try {
            //make frame body;
            for (tlv.TlvItem it : tagContainer) {
                byteArrayOS.write(it.getFullTLV());
            }
            byte[] frameBody = byteArrayOS.toByteArray();

            //make full frame
            byteArrayOS.reset();

            //len is calculated for frameBody + protocolVersion + messageType
            byteArrayOS.write(Serializer.serializeFrameSize(frameBody.length + 2));

            byteArrayOS.write(Serializer.serializeControlChar(messageType));
            byteArrayOS.write(protocolVersion);
            byteArrayOS.write(frameBody);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return byteArrayOS.toByteArray();
    }

    private static class Serializer {
        static byte[] serializeFrameSize(int size) {
            byte[] sizeArray = new byte[2];

            sizeArray[0] = (byte) ((size >> 8) & 0xff);
            sizeArray[1] = (byte) (size & 0xff);

            return sizeArray;
        }

        static byte serializeControlChar(Buffer.MessageTypes messageType) {
            byte controlChar;

            switch (messageType) {
                case CLIENT_REQUEST:
                    controlChar = (byte) 0x96;
                    break;
                case SERVER_ANSWER:
                    controlChar = (byte) 0x97;
                    break;
                case UNKNOWN:
                default:
                    controlChar = (byte) 0x00;
                    break;
            }

            return controlChar;
        }
    }

    static short deserializeTTKLength(byte[] data) {
        java.nio.ByteBuffer byte_buffer = java.nio.ByteBuffer.allocate(Long.BYTES);
        byte_buffer.put(data, 0, 2);
        byte_buffer.order(ByteOrder.BIG_ENDIAN);
        byte_buffer.flip();
        return byte_buffer.getShort();
    }
}


