package org.gnunet.util;

import com.google.common.collect.Lists;
import org.gnunet.testing.TestingFixture;
import org.gnunet.testing.TestingServer;
import org.junit.Assert;
import org.junit.Test;

import java.net.*;

/**
 * ...
 *
 * @author Florian Dold
 */
public class ClientServerTest extends TestingFixture {

    @Test
    public void test_start_stop() {
        Program.configureLogging("DEBUG", null);
        final TestingServer srv = new TestingServer();
        srv.server.stopListening();
    }

    /**
     * Test if the server receives a message sent by a client.
     */
    @Test
    public void test_testing_server() {
        Program.configureLogging("DEBUG", null);

        final TestingServer srv = new TestingServer();

        final Wrapper<Boolean> gotMessage = new Wrapper<Boolean>(false);

        srv.server.setHandler(new Server.MessageRunabout() {
            public void visit(TestMessage tm) {
                gotMessage.set(true);
                srv.server.destroy();
            }
        });

        Scheduler.run(new Scheduler.Task() {
            @Override
            public void run(Scheduler.RunContext ctx) {
                final Client cli = srv.createClient();
                cli.notifyTransmitReady(RelativeTime.FOREVER,true, 0, new MessageTransmitter() {
                    @Override
                    public void transmit(Connection.MessageSink sink) {
                        System.out.println("ntr!");
                        sink.send(new TestMessage());
                    }

                    @Override
                    public void handleError() {
                        Assert.fail();
                    }
                });
                System.out.println("done");
            }
        });

        Assert.assertTrue(gotMessage.get());
    }


    /**
     * Test what happens when a client calls notifyTransmitReady, but does not send
     * a message in the callback and disconnects.
     */
    @Test(timeout = 1000)
    public void test_premature_disconnect() {
        Program.configureLogging("DEBUG", null);
        final TestingServer srv = new TestingServer();

        srv.server.notifyDisconnect(new Server.DisconnectHandler() {
            @Override
            public void onDisconnect(Server.ClientHandle clientHandle) {
                srv.server.destroy();
            }
        });

        Scheduler.run(new Scheduler.Task() {
            @Override
            public void run(Scheduler.RunContext ctx) {
                final Client cli = srv.createClient();
                cli.notifyTransmitReady(RelativeTime.FOREVER,true, 0, new MessageTransmitter() {
                    @Override
                    public void transmit(Connection.MessageSink sink) {
                        sink.send(new TestMessage());
                        cli.disconnect();
                    }

                    @Override
                    public void handleError() {
                        Assert.fail();
                    }
                });
            }
        });
    }


    @Test
    public void test_receiveDone() {
        Program.configureLogging("DEBUG", null);
        final TestingServer srv = new TestingServer();

        final Wrapper<Integer> msgCount = new Wrapper<Integer>(0);

        srv.server.setHandler(new Server.MessageRunabout() {
            public void visit(TestMessage tm) {
                msgCount.set(msgCount.get() + 1);
                if (msgCount.get() == 3) {
                    getSender().receiveDone(false);
                    srv.server.stopListening();
                } else {
                    getSender().receiveDone(true);
                }
            }
        });

        Scheduler.run(new Scheduler.Task() {
            @Override
            public void run(Scheduler.RunContext ctx) {
                final Client cli = srv.createClient();

                cli.transmitWhenReady(RelativeTime.FOREVER, new TestMessage(), new Continuation() {
                    @Override
                    public void cont(boolean success) {
                        cli.transmitWhenReady(RelativeTime.FOREVER, new TestMessage(), new Continuation() {
                            @Override
                            public void cont(boolean success) {
                                cli.transmitWhenReady(RelativeTime.FOREVER, new TestMessage(), null);
                            }
                        });
                    }
                });
            }
        });
    }

    @Test
    public void test_acceptFromAddresses() {
        Program.configureLogging("DEBUG", null);

        InetAddress localhost = null;
        try {
           localhost = Inet4Address.getLocalHost();
        } catch (UnknownHostException e) {
            Assert.fail();
        }

        // does this work on all operating systems?
        SocketAddress addr = new InetSocketAddress(localhost, 0);

        Server server = new Server(Lists.newArrayList(addr), RelativeTime.FOREVER, false);

        server.destroy();

    }


    @Test
    public void test_keep_drop() {
        Program.configureLogging("DEBUG", null);
        final TestingServer srv = new TestingServer();

        final Wrapper<Integer> msgCount = new Wrapper<Integer>(0);



        srv.server.setHandler(new Server.MessageRunabout() {
            public void visit(TestMessage tm) {
                srv.server.stopListening();
                if (msgCount.get() == 0) {
                    getSender().keep();
                    getSender().drop();
                    getSender().receiveDone(true);
                } else if (msgCount.get() == 1) {
                    getSender().receiveDone(false);

                } else {
                    Assert.fail();
                }

                msgCount.set(msgCount.get() + 1);
            }
        });

        Scheduler.run(new Scheduler.Task() {
            @Override
            public void run(Scheduler.RunContext ctx) {
                final Client cli = srv.createClient();
                cli.transmitWhenReady(new TestMessage(), new Continuation() {
                    @Override
                    public void cont(boolean success) {
                        cli.transmitWhenReady(new TestMessage(), null);
                    }
                });
            }
        });
    }



    /**
    * test if markMonitor / soft shutdown works.
    */
    @Test
    public void test_monitor_clients() {
        Program.configureLogging("DEBUG", null);
        final TestingServer srv = new TestingServer();

        final Wrapper<Integer> msgCount = new Wrapper<Integer>(0);

        srv.server.setHandler(new Server.MessageRunabout() {
            public void visit(TestMessage tm) {
                if (msgCount.get() == 0) {
                    getSender().markMonitor();
                    getSender().receiveDone(true);
                } else if (msgCount.get() == 1) {
                    srv.server.stopListening();
                    getSender().receiveDone(false);
                } else {
                    Assert.fail();
                }

                msgCount.set(msgCount.get() + 1);
            }
        });

        Scheduler.run(new Scheduler.Task() {
            @Override
            public void run(Scheduler.RunContext ctx) {
                final Client cli1 = srv.createClient();
                final Client cli2 = srv.createClient();

                cli1.transmitWhenReady(new TestMessage(), null);
                cli2.transmitWhenReady(new TestMessage(), null);
            }
        });
    }
}
