package org.jvirtanen.nassau.moldudp64;

import static java.util.Arrays.*;
import static java.util.Collections.*;
import static org.junit.Assert.*;
import static org.jvirtanen.nassau.moldudp64.MoldUDP64ClientState.*;
import static org.jvirtanen.nassau.moldudp64.MoldUDP64ClientStatus.*;
import static org.jvirtanen.nassau.util.Strings.*;

import java.nio.channels.DatagramChannel;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.Timeout;
import org.jvirtanen.nassau.Messages;
import org.jvirtanen.nassau.util.FixedClock;
import org.jvirtanen.nassau.util.Strings;

public class MoldUDP64ServerTest {

    @Rule
    public Timeout timeout = new Timeout(1000);

    @Rule
    public ExpectedException exception = ExpectedException.none();

    private FixedClock clock;

    private DatagramChannel serverRequestChannel;

    private MoldUDP64DownstreamPacket packet;

    private Messages<String> clientMessages;

    private MoldUDP64ClientStatus clientStatus;

    private MoldUDP64Client client;
    private MoldUDP64Server server;

    @Before
    public void setUp() throws Exception {
        DatagramChannel clientChannel = DatagramChannels.openClientChannel();
        DatagramChannel serverChannel = DatagramChannels.openServerChannel(clientChannel);

        clock = new FixedClock();

        serverRequestChannel = DatagramChannels.openServerRequestChannel();

        packet = new MoldUDP64DownstreamPacket();

        clientMessages = new Messages<>(Strings.MESSAGE_PARSER);

        clientStatus = new MoldUDP64ClientStatus();

        client = new MoldUDP64Client(clientChannel, serverRequestChannel.getLocalAddress(),
                clientMessages, clientStatus);

        server = new MoldUDP64Server(clock, serverChannel, "nassau");
    }

    @After
    public void tearDown() throws Exception {
        server.close();
        client.close();

        serverRequestChannel.close();
    }

    @Test
    public void packetWithSingleMessage() throws Exception {
        packet.clear();
        packet.put(wrap("foo"));

        server.send(packet);

        packet.clear();
        packet.put(wrap("bar"));

        server.send(packet);

        while (clientMessages.collect().size() != 2)
            client.receive();

        assertEquals(asList("foo", "bar"), clientMessages.collect());
        assertEquals(asList(new State(SYNCHRONIZED), new Downstream(), new Downstream()),
                clientStatus.collect());
    }

    @Test
    public void packetWithMultipleMessages() throws Exception {
        packet.clear();
        packet.put(wrap("foo"));
        packet.put(wrap("bar"));

        server.send(packet);

        packet.clear();
        packet.put(wrap("baz"));
        packet.put(wrap("quux"));

        server.send(packet);

        while (clientMessages.collect().size() != 4)
            client.receive();

        assertEquals(asList("foo", "bar", "baz", "quux"), clientMessages.collect());
        assertEquals(asList(new State(SYNCHRONIZED), new Downstream(), new Downstream()),
                clientStatus.collect());
    }

    @Test
    public void heartbeat() throws Exception {
        server.sendHeartbeat();

        while (clientStatus.collect().size() != 2)
            client.receive();

        assertEquals(emptyList(), clientMessages.collect());
        assertEquals(asList(new State(SYNCHRONIZED), new Downstream()),
                clientStatus.collect());
    }

    @Test
    public void endOfSession() throws Exception {
        server.sendEndOfSession();

        while (clientStatus.collect().size() != 3)
            client.receive();

        assertEquals(emptyList(), clientMessages.collect());
        assertEquals(asList(new State(SYNCHRONIZED), new EndOfSession(), new Downstream()),
                clientStatus.collect());
    }

    @Test
    public void keepAlive() throws Exception {
        clock.setCurrentTimeMillis(500);

        server.keepAlive();

        clock.setCurrentTimeMillis(1000);

        server.keepAlive();

        clock.setCurrentTimeMillis(1500);

        server.keepAlive();

        clock.setCurrentTimeMillis(2000);

        server.keepAlive();

        while (clientStatus.collect().size() != 3)
            client.receive();

        assertEquals(emptyList(), clientMessages.collect());
        assertEquals(asList(new State(SYNCHRONIZED), new Downstream(), new Downstream()),
                clientStatus.collect());
    }

    @Test
    public void maximumMessageLength() throws Exception {
        String message = repeat('X', 1398);

        packet.put(wrap(message));

        server.send(packet);

        while (clientMessages.collect().size() != 1)
            client.receive();

        assertEquals(asList(message), clientMessages.collect());
        assertEquals(asList(new State(SYNCHRONIZED), new Downstream()),
                clientStatus.collect());
    }

    @Test
    public void maximumMessageLengthExceeded() throws Exception {
        exception.expect(MoldUDP64Exception.class);

        packet.put(wrap(repeat('X', 1399)));
    }

}

@@ -877,59 +877,8 @@
 k;%0A%0A
-    private DatagramChannel serverRequestChannel;%0A%0A
     
@@ -1088,16 +1088,67 @@
 erver;%0A%0A
+    private MoldUDP64RequestServer requestServer;%0A%0A
     @Bef
@@ -1376,42 +1376,23 @@
     
-clock = new FixedClock();%0A%0A       
+DatagramChannel
  ser
@@ -1453,24 +1453,59 @@
 Channel();%0A%0A
+        clock = new FixedClock();%0A%0A
         pack
@@ -1871,16 +1871,91 @@
 assau%22);
+%0A%0A        requestServer = new MoldUDP64RequestServer(serverRequestChannel);
 %0A    %7D%0A%0A
@@ -2003,32 +2003,64 @@
 ows Exception %7B%0A
+        requestServer.close();%0A%0A
         server.c
@@ -2094,47 +2094,8 @@
 e();
-%0A%0A        serverRequestChannel.close();
 %0A   
@@ -2249,32 +2249,59 @@
 .send(packet);%0A%0A
+        client.receive();%0A%0A
         packet.c
@@ -2376,65 +2376,8 @@
 );%0A%0A
-        while (clientMessages.collect().size() != 2)%0A    
     
@@ -2794,32 +2794,59 @@
 .send(packet);%0A%0A
+        client.receive();%0A%0A
         packet.c
@@ -2955,65 +2955,8 @@
 );%0A%0A
-        while (clientMessages.collect().size() != 4)%0A    
     
@@ -3295,63 +3295,8 @@
 );%0A%0A
-        while (clientStatus.collect().size() != 2)%0A    
     
@@ -3599,63 +3599,8 @@
 );%0A%0A
-        while (clientStatus.collect().size() != 3)%0A    
     
@@ -4631,65 +4631,8 @@
 );%0A%0A
-        while (clientMessages.collect().size() != 1)%0A    
     

Clean up MoldUDP64 server test