package nearenough.protocol;

import io.netty.buffer.ByteBuf;
import org.junit.Test;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertArrayEquals;

public final class RtWireTest {

  @Test
  public void sizeOfEmptyMessage() {
    int size = RtWire.computeEncodedSize(Collections.emptyMap());
    // Empty message is 4 bytes, a single num_tags value
    assertThat(size, equalTo(4));
  }

  @Test
  public void sizeOfSingleTagMessage() {
    Map<RtTag, byte[]> map = Collections.singletonMap(RtTag.NONC, new byte[]{1, 2, 3, 4});
    int size = RtWire.computeEncodedSize(map);
    // Single tag message is 4 (num_tags) + 4 (NONC) + 4 (value)
    assertThat(size, equalTo(12));
  }

  @Test
  public void sizeOfTwoTagMessage() {
    Map<RtTag, byte[]> map = new HashMap<>();
    map.put(RtTag.NONC, new byte[4]);
    map.put(RtTag.PAD, new byte[4]);

    int size = RtWire.computeEncodedSize(map);
    // Two tag message
    //   4 num_tags
    //   8 (NONC, PAD) tags
    //   4 PAD offset
    //   8 values
    assertThat(size, equalTo(24));
  }

  @Test
  public void encodeEmptyMessage() {
    RtMessage msg = RtMessage.fromBytes(new byte[]{0, 0, 0, 0});
    ByteBuf onWire = RtWire.toWire(msg);

    // Empty message will be a single uint32
    assertThat(onWire.readableBytes(), equalTo(4));
    assertThat(onWire.readIntLE(), equalTo(0));
  }

  @Test
  public void encodeSingleTagMessage() {
    byte[] value = new byte[64];
    Arrays.fill(value, (byte) 'a');

    RtMessage msg = RtMessage.builder()
        .add(RtTag.CERT, value)
        .build();

    // Wire encoding is 4 (num_tags) + 4 (CERT) + 64 (CERT value)
    ByteBuf onWire = RtWire.toWire(msg);
    assertThat(onWire.readableBytes(), equalTo(72));

    // num_tags
    assertThat(onWire.readIntLE(), equalTo(1));

    // CERT tag
    assertThat(onWire.readInt(), equalTo(RtTag.CERT.wireEncoding()));

    // CERT value
    assertThat(onWire.readableBytes(), equalTo(value.length));
    byte[] readValue = new byte[onWire.readableBytes()];
    onWire.readBytes(readValue);
    assertArrayEquals(value, readValue);

    // Message was completely read
    assertThat(onWire.readableBytes(), equalTo(0));
  }

  @Test
  public void encodeTwoTagMessage() {
    byte[] tag1Value = new byte[24];
    byte[] tag2Value = new byte[32];
    Arrays.fill(tag1Value, (byte) '1');
    Arrays.fill(tag2Value, (byte) '2');

    RtMessage msg = RtMessage.builder()
        .add(RtTag.INDX, tag1Value)
        .add(RtTag.MAXT, tag2Value)
        .build();

    // Wire encoding
    //   4 num_tags
    //   8 (INDX, MAXT) tags
    //   4 MAXT offset
    //  24 INDX value
    //  32 MAXT value
    ByteBuf onWire = RtWire.toWire(msg);
    assertThat(onWire.readableBytes(), equalTo(4 + 8 + 4 + 24 + 32));

    // num_tags
    assertThat(onWire.readIntLE(), equalTo(2));

    // Offset past INDX value to start of MAXT value
    assertThat(onWire.readIntLE(), equalTo(tag1Value.length));

    // INDX tag
    assertThat(onWire.readInt(), equalTo(RtTag.INDX.wireEncoding()));
    // MAXT tag
    assertThat(onWire.readInt(), equalTo(RtTag.MAXT.wireEncoding()));

    // INDX value
    byte[] readTag1Value = new byte[tag1Value.length];
    onWire.readBytes(readTag1Value);
    assertArrayEquals(tag1Value, readTag1Value);

    // MAXT value
    byte[] readTag2Value = new byte[tag2Value.length];
    onWire.readBytes(readTag2Value);
    assertArrayEquals(tag2Value, readTag2Value);

    // Message was completely read
    assertThat(onWire.readableBytes(), equalTo(0));
  }
}

@@ -2421,20 +2421,20 @@
  byte%5B%5D 
-tag1
+indx
 Value = 
@@ -2458,20 +2458,20 @@
  byte%5B%5D 
-tag2
+maxt
 Value = 
@@ -2500,20 +2500,20 @@
 ys.fill(
-tag1
+indx
 Value, (
@@ -2540,20 +2540,20 @@
 ys.fill(
-tag2
+maxt
 Value, (
@@ -2630,20 +2630,20 @@
 g.INDX, 
-tag1
+indx
 Value)%0A 
@@ -2666,20 +2666,20 @@
 g.MAXT, 
-tag2
+maxt
 Value)%0A 
@@ -2753,18 +2753,18 @@
  8 (
-INDX, MAXT
+MAXT, INDX
 ) ta
@@ -2777,20 +2777,20 @@
  //   4 
-MAXT
+INDX
  offset%0A
@@ -2800,20 +2800,20 @@
  //  24 
-INDX
+MAXT
  value%0A 
@@ -2822,20 +2822,20 @@
  //  32 
-MAXT
+INDX
  value%0A 
@@ -3029,20 +3029,20 @@
 et past 
-INDX
+MAXT
  value t
@@ -3052,20 +3052,20 @@
 tart of 
-MAXT
+INDX
  value%0A 
@@ -3106,20 +3106,20 @@
 equalTo(
-tag1
+maxt
 Value.le
@@ -3134,20 +3134,20 @@
 %0A    // 
-INDX
+MAXT
  tag%0A   
@@ -3190,20 +3190,20 @@
 o(RtTag.
-INDX
+MAXT
 .wireEnc
@@ -3212,36 +3212,36 @@
 ing()));%0A    // 
-MAXT
+INDX
  tag%0A    assertT
@@ -3268,36 +3268,36 @@
 , equalTo(RtTag.
-MAXT
+INDX
 .wireEncoding())
@@ -3307,20 +3307,20 @@
 %0A    // 
-INDX
+MAXT
  value%0A 
@@ -3354,20 +3354,20 @@
 ew byte%5B
-tag1
+maxt
 Value.le
@@ -3436,12 +3436,12 @@
 als(
-tag1
+maxt
 Valu
@@ -3459,36 +3459,36 @@
 Value);%0A%0A    // 
-MAXT
+INDX
  value%0A    byte%5B
@@ -3514,20 +3514,20 @@
 ew byte%5B
-tag2
+indx
 Value.le
@@ -3596,12 +3596,12 @@
 als(
-tag2
+indx
 Valu

Update RtWriteTest after correcting tree map comparator in RtMessageBuilder