/*
 This file is part of GNUnet.
 (C) 2011, 2012 Christian Grothoff (and other contributing authors)

 GNUnet is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published
 by the Free Software Foundation; either version 3, or (at your
 option) any later version.

 GNUnet is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with GNUnet; see the file COPYING.  If not, write to the
 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.
 */

package org.gnunet.dht;

import org.gnunet.testing.TestingFixture;
import org.gnunet.testing.TestingSubsystem;
import org.gnunet.util.*;
import org.junit.Assert;
import org.junit.Test;

import java.util.EnumSet;
import java.util.List;

import static org.junit.Assert.assertArrayEquals;

public class DHTTest extends TestingFixture  {
    @Test(timeout = 1000)
    public void test_dht_put() {
        Program.configureLogging();

        final Wrapper<Boolean> putFinished = new Wrapper<Boolean>(true);

        TestingSubsystem ts = new TestingSubsystem("dht");

        final DistributedHashTable dht = new DistributedHashTable(ts.getConfiguration());
        dht.put(new HashCode("gnj-test"), new byte[]{1, 2, 3}, 1, EnumSet.noneOf(RouteOption.class),
                BlockType.TEST.val, RelativeTime.HOUR.toAbsolute(), new Continuation() {
            @Override
            public void cont(boolean success) {
                putFinished.set(true);
                dht.destroy();
            }
        });

        Scheduler.run();
        Assert.assertTrue(putFinished.get());
    }

    @Test
    public void test_dht_put_get() {
        Program.configureLogging();

        final Wrapper<Boolean> getFinished = new Wrapper<Boolean>(true);

        TestingSubsystem ts = new TestingSubsystem("dht");

        final HashCode hash1 = new HashCode("gnj-test");
        final byte[] data = new byte[]{1, 2, 3};

        final DistributedHashTable dht = new DistributedHashTable(ts.getConfiguration());
        dht.put(hash1, data, 1, EnumSet.noneOf(RouteOption.class),
                BlockType.TEST.val, RelativeTime.HOUR.toAbsolute(), new Continuation() {
            @Override
            public void cont(boolean success) {
                dht.startGet(RelativeTime.FOREVER, BlockType.TEST.val, hash1, 1, EnumSet.noneOf(RouteOption.class), null, new ResultCallback() {
                    @Override
                    public void handleResult(AbsoluteTime expiration, HashCode key, List<PeerIdentity> getPath, List<PeerIdentity> putPath, BlockType type, byte[] recData) {
                        assertArrayEquals(data, recData);
                        getFinished.set(true);
                        dht.destroy();
                    }
                });
            }
        });

        Scheduler.run();
        Assert.assertTrue(getFinished.get());
    }


    @Test(timeout = 500)
    public void test_dht_monitor_put() {
        Program.configureLogging();

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

        TestingSubsystem ts = new TestingSubsystem("dht");

        final HashCode hash = new HashCode("gnj-test");
        final byte[] data1 = new byte[]{1, 2, 3};

        final byte[] data2 = new byte[]{5, 4, 1, 2, 6};

        final DistributedHashTable dht = new DistributedHashTable(ts.getConfiguration());

        dht.startMonitor(BlockType.TEST.val, hash, null, null, new MonitorPutHandler() {
            @Override
            public void onPut(int options, int type, int hop_count, AbsoluteTimeMessage expirationTime, PeerIdentity[] putPath, HashCode key, byte[] data) {
                putMonitorCount.set(putMonitorCount.get() + 1);
                if (putMonitorCount.get() == 2) {
                    dht.destroy();
                }
            }
        });

        Scheduler.addDelayed(new RelativeTime(50), new Scheduler.Task() {
            @Override
            public void run(Scheduler.RunContext ctx) {
                dht.put(hash, data1, 1, EnumSet.noneOf(RouteOption.class), BlockType.TEST.val, RelativeTime.HOUR.toAbsolute(), null);
            }
        });


        Scheduler.addDelayed(new RelativeTime(100), new Scheduler.Task() {
            @Override
            public void run(Scheduler.RunContext ctx) {
                dht.put(hash, data2, 1, EnumSet.noneOf(RouteOption.class), BlockType.TEST.val, RelativeTime.HOUR.toAbsolute(), null);
            }
        });


        Scheduler.run();
        Assert.assertTrue(putMonitorCount.get() == 2);
    }

    @Test(timeout = 500)
    public void test_dht_monitor_get() {
        Program.configureLogging("debug");

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

        TestingSubsystem ts = new TestingSubsystem("dht");

        final HashCode hash = new HashCode("gnj-test");

        final DistributedHashTable dht1 = new DistributedHashTable(ts.getConfiguration());
        final DistributedHashTable dht2 = new DistributedHashTable(ts.getConfiguration());

        dht1.startMonitor(BlockType.TEST.val, hash, new MonitorGetHandler() {
            @Override
            public void onGet(int options, int type, int hopCount, int desiredReplicationLevel, PeerIdentity[] getPath, HashCode key) {
                System.out.println("here!");
                ok.set(true);
                dht2.destroy();
                dht1.destroy();
            }
        }, null, null);

        Scheduler.addDelayed(RelativeTime.fromMilliseconds(50), new Scheduler.Task() {
            @Override
            public void run(Scheduler.RunContext ctx) {
                dht2.startGet(RelativeTime.FOREVER, BlockType.TEST.val, hash, 1, EnumSet.noneOf(RouteOption.class), null, null);
            }
        });


        Scheduler.run();
        Assert.assertTrue(ok.get());
    }


}

