package org.jgroups.tests;

import org.jgroups.*;
import org.jgroups.util.Util;
import org.testng.annotations.Test;

import java.io.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.List;
import java.util.ArrayList;

/**
 * 
 * coord.stopFlush() and coord.close() called in sequence cause flush lockup
 * See https://jira.jboss.org/jira/browse/JGRP-959
 * 
 * 
 * @author vladimir
 * @since 2.8
 */
@Test(groups = Global.FLUSH, sequential = true)
public class FlushCloseOpenTest extends ChannelTestBase {

   @Test
   public void testAndLoop() throws Exception {

      for (int i = 1; i <= 4; i++) {
         Channel channel = createChannel(true, 2);
         ReceiverImpl receiver = new ReceiverImpl();
         channel.setReceiver(receiver);
         channel.setName("A");
         channel.connect("testClust");

         Channel channel2 = createChannel((JChannel) channel);
         ReceiverImpl receiver2 = new ReceiverImpl();
         channel2.setReceiver(receiver2);
         channel.setName("B");
         channel2.connect("testClust");

         sendMessage(channel, "msg1");
         sendMessage(channel2, "msg2");

         assert Util.startFlush(channel);
         assertCount(receiver, 2, receiver2, 2);
         channel.stopFlush();

         channel.close();
         channel = createChannel((JChannel) channel2);
         channel.setReceiver(receiver);
         channel.setName("A");
         channel.connect("testClust");

         sendMessage(channel2, "msg3");

         assert Util.startFlush(channel2);
         assertCount(receiver, 3, receiver2, 3);
         channel.stopFlush();
         channel2.close();
         channel2 = createChannel((JChannel) channel);
         channel2.setReceiver(receiver2);
         channel.setName("B");
         channel2.connect("testClust");

         sendMessage(channel2, "msg4");
         assert Util.startFlush(channel2);
         assertCount(receiver, 4, receiver2, 4);
         channel2.stopFlush();
         
         channel.close();
         channel2.close();
         receiver.receiveCount.set(0);
         receiver2.receiveCount.set(0);
         System.out.println("***** Round " + i + " done *****");
      }
   }

   private  void sendMessage(Channel channel, Serializable obj) throws Exception {
      if (!channel.isConnected()) {
         log.warn("Channel disconnected in send, discarding msg");
         return;
      }
      Message msg = new Message(null, null, obj);
      log.debug("Sending message: " + msg);
      channel.send(msg);
      log.debug("Sent message: " + msg);
   }

   private void assertCount(ReceiverImpl srv1, long srv1Count, ReceiverImpl srv2, long srv2Count)
            throws InterruptedException {
      long start = System.currentTimeMillis();
      for (int i = 0; i < 1000; i++) {
         if (srv1.receiveCount.get() == srv1Count && srv2.receiveCount.get() == srv2Count) {
            break;
         }
         Thread.sleep(10L);
      }
      assert srv1Count == srv1.receiveCount.get() : "expected " + srv1Count + " but got "
               + srv1.receiveCount;
      assert srv2Count == srv2.receiveCount.get() : "expected " + srv2Count + " but got "
               + srv2.receiveCount;
      log.info("assert OK in " + (System.currentTimeMillis() - start) + "ms");
   }

   private class ReceiverImpl extends ReceiverAdapter {
      final List<Object> msgs = new ArrayList<Object>();
      public final AtomicLong receiveCount = new AtomicLong();

      public List<Object> getMsgs() {
         return msgs;
      }

      @Override
      public void receive(Message msg) {

         try {
            Object data = msg.getObject();
            msgs.add(data);
            receiveCount.incrementAndGet();
            log.debug("Received msg: " + data);
         } catch (Exception e) {
            log.error("Receive failed", e);
         }
      }

      @Override
      public void viewAccepted(View new_view) {
      }
   }
}