package zutil.net.mqtt.packet;

import org.junit.Test;
import zutil.converter.Converter;
import zutil.parser.binary.BinaryStructInputStream;
import zutil.parser.binary.BinaryStructOutputStream;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import static org.junit.Assert.*;

public class MqttPacketPublishTest {

    @Test
    public void decode() throws IOException {
        char[] data = new char[]{
                // Fixed Header
                0b0011_0000, // Packet Type(4) + Reserved(4)
                0xFF & 4, // Variable Header + Payload Length
                // Variable Header
                0b0000_0000, // length
                0xFF & 2,    // length
                'a',         // Topic Name
                'b',         // Topic Name
                // Payload
        };

        MqttPacketPublish obj = (MqttPacketPublish) MqttPacket.read(
                new BinaryStructInputStream(new ByteArrayInputStream(Converter.toBytes(data))));

        assertEquals(4, obj.variableHeaderAndPayloadLength);
        assertEquals("ab", obj.topicName);
        assertEquals(0, obj.packetId);
        assertArrayEquals(new byte[0], obj.payload);
    }

    @Test
    public void decodeFlags() throws IOException {
        // Test getter and setter

        MqttPacketPublish obj = new MqttPacketPublish();
        assertFalse(obj.getFlagDup());
        obj.setFlagDup(true);
        assertTrue(obj.getFlagDup());
        assertEquals(0b1_00_0, obj.flags);
        obj.setFlagDup(false);
        assertFalse(obj.getFlagDup());
        assertEquals(0b0_00_0, obj.flags);

        assertEquals(0, obj.getFlagQoS());
        obj.setFlagQoS(1);
        assertEquals(1, obj.getFlagQoS());
        assertEquals(0b0_01_0, obj.flags);
        obj.setFlagQoS(2);
        assertEquals(2, obj.getFlagQoS());
        assertEquals(0b0_10_0, obj.flags);
        obj.setFlagQoS(3);
        assertEquals(3, obj.getFlagQoS());
        assertEquals(0b0_11_0, obj.flags);
        obj.setFlagQoS(0);
        assertEquals(0, obj.getFlagQoS());
        assertEquals(0b0_00_0, obj.flags);

        assertFalse(obj.getFlagRetain());
        obj.setFlagRetain(true);
        assertTrue(obj.getFlagRetain());
        assertEquals(0b0_00_1, obj.flags);
        obj.setFlagRetain(false);
        assertFalse(obj.getFlagRetain());
        assertEquals(0b0_00_0, obj.flags);

        // Test decoding

        char[] data = new char[]{
                // Fixed Header
                0b0011_1000, // Packet Type(4) + Reserved(4)
                0xFF & 4, // Variable Header + Payload Length
                // Variable Header
                0b0000_0000, // length
                0xFF & 2,    // length
                'a',         // Topic Name
                'b',         // Topic Name
                // Payload
        };

        obj = (MqttPacketPublish) MqttPacket.read(
                new BinaryStructInputStream(new ByteArrayInputStream(Converter.toBytes(data))));

        assertTrue(obj.getFlagDup());
        assertEquals(MqttPacketPublish.PUBLISH_QOS_0, obj.getFlagQoS());
        assertFalse(obj.getFlagRetain());

        data = new char[]{
                // Fixed Header
                0b0011_0101, // Packet Type(4) + Reserved(4)
                0xFF & 4, // Variable Header + Payload Length
                // Variable Header
                0b0000_0000, // length
                0xFF & 2,    // length
                'a',         // Topic Name
                'b',         // Topic Name
                // Payload
        };

         obj = (MqttPacketPublish) MqttPacket.read(
                new BinaryStructInputStream(new ByteArrayInputStream(Converter.toBytes(data))));

        assertFalse(obj.getFlagDup());
        assertEquals(MqttPacketPublish.PUBLISH_QOS_2, obj.getFlagQoS());
        assertTrue(obj.getFlagRetain());
    }

    @Test
    public void decodePayload() throws IOException {
        char[] data = new char[]{
                // Fixed Header
                0b0011_0000, // Packet Type(4) + Reserved(4)
                0xFF & 7, // Variable Header + Payload Length
                // Variable Header
                0b0000_0000, // length
                0xFF & 2,    // length
                'a',         // Topic Name
                'b',         // Topic Name
                // Payload
                0x00, 0x01, 0x02,
        };

        MqttPacketPublish obj = (MqttPacketPublish) MqttPacket.read(
                new BinaryStructInputStream(new ByteArrayInputStream(Converter.toBytes(data))));

        assertEquals(7, obj.variableHeaderAndPayloadLength);
        assertEquals("ab", obj.topicName);
        assertEquals(0, obj.packetId);
        assertArrayEquals(new byte[]{0x00, 0x01, 0x02}, obj.payload);
    }

    @Test
    public void encode() throws IOException {
        char[] data = new char[]{
                // Fixed Header
                0b0011_0000, // Packet Type(4) + Reserved(4)
                0xFF & 4, // Variable Header + Payload Length
                // Variable Header
                0b0000_0000, // length
                0xFF & 2,    // length
                'a',         // Topic Name
                'b',         // Topic Name
                // Payload
        };

        MqttPacketPublish obj = new MqttPacketPublish();
        obj.topicName = "ab";
        obj.packetId = 5;

        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        BinaryStructOutputStream binOut = new BinaryStructOutputStream(buffer);
        MqttPacket.write(binOut, obj);
        assertArrayEquals(Converter.toBytes(data), buffer.toByteArray());
    }
}