package org.jboss.cache.pojo.integrated;

import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.config.Configuration.CacheMode;
import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
import org.jboss.cache.pojo.PojoCache;
import org.jboss.cache.pojo.PojoCacheFactory;
import org.jboss.cache.pojo.test.NetworkAdmin;
import org.jboss.cache.pojo.test.NetworkDomain;
import org.jboss.cache.pojo.test.NetworkElement;
import org.jboss.cache.pojo.test.NetworkNode;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * Sample example for network management that consists of fine-grained replication and object graph (including
 * Collection)
 * <p>Specifically, it illustrates the following object relationship:
 * <ul>
 * <li>Multiple referece, e.g., shared sub-objects (Domain may share DataNode)</li>
 * <li>Recursive, e.g., parent-child relationship (DataNode and Element)</li>
 * </ul>
 *
 * @author Ben Wang
 */

@Test(groups = {"functional"})
public class ReplicatedNetworkManagementTest 
{
   Log log = LogFactory.getLog(ReplicatedNetworkManagementTest.class);
   PojoCache cache1;
   PojoCache cache2;


   @BeforeMethod(alwaysRun = true)
   protected void setUp() throws Exception
   {
      log.info("setUp() ....");
      cache1 = createCache("CacheGroup");
      cache2 = createCache("CacheGroup");
   }

   @AfterMethod(alwaysRun = true)
   protected void tearDown() throws Exception
   {
      cache1.getCache().removeNode(Fqn.fromString("/"));
      cache1.stop();
      cache2.stop();
   }

   private PojoCache createCache(String name) throws Exception
   {
      boolean toStart = false;
      PojoCache cache = PojoCacheFactory.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), toStart);
      cache.start();
      return cache;
   }

   /**
    * Domain shares Admin object.
    */
   public void testDomainAndAdmin() throws Exception
   {
      NetworkAdmin admin = new NetworkAdmin();
      admin.setId("Benny");

      NetworkDomain tempSensorDomain = new NetworkDomain();
      tempSensorDomain.setName("Temperature sensors");
      tempSensorDomain.setAdmin(admin);


      NetworkDomain vibSensorDomain = new NetworkDomain();
      vibSensorDomain.setName("Vibration sensors");
      vibSensorDomain.setAdmin(admin);

      cache1.attach("/temp", tempSensorDomain);
      cache1.attach("/vib", vibSensorDomain);

      NetworkDomain d1 = (NetworkDomain) cache2.find("/temp");
      NetworkDomain d2 = (NetworkDomain) cache2.find("/vib");

      assertEquals("Admin should be the same", d1.getAdmin(), d2.getAdmin());

      d2.getAdmin().setId("Wen");
      assertEquals("New admin id is ", "Wen", d1.getAdmin().getId());
      assertEquals("New admin id is ", "Wen", admin.getId());

      d1 = (NetworkDomain) cache2.find("/vib");
      d2 = (NetworkDomain) cache2.find("/temp");
      assertEquals("Admin should be the same", d1.getAdmin(), d2.getAdmin());

      d2.getAdmin().setId("Wen");
      assertEquals("New admin id is ", "Wen", d1.getAdmin().getId());
      assertEquals("New admin id is ", "Wen", admin.getId());

      NetworkDomain temp1 = (NetworkDomain) cache1.detach("/temp");
      NetworkDomain vib1 = (NetworkDomain) cache1.detach("/vib");

      assertEquals("Vibration sensor ", vibSensorDomain, vib1);
      assertEquals("Temperature sensor ", tempSensorDomain, temp1);

   }

   /**
    * Domain shares Admin object, domain also contains a list of Nodes, and each DataNode has sensor elements
    */
   public void testNodeAndElement() throws Exception
   {
      // Construct nodes
      NetworkNode taipei = new NetworkNode();
      taipei.setName("Taipei machine");
      taipei.setIpAddress("192.168.1.100");

      NetworkNode tainan = new NetworkNode();
      tainan.setName("Tainan machine");
      tainan.setIpAddress("192.168.1.200");

      // Contruct sensor elements
      NetworkElement vibSensor1 = new NetworkElement();
      vibSensor1.setName("Vibration sensor V10");

      NetworkElement vibSensor2 = new NetworkElement();
      vibSensor2.setName("Vibration sensor V20");

      NetworkElement tempSensor1 = new NetworkElement();
      tempSensor1.setName("Temperature sensor T10");

      NetworkElement tempSensor2 = new NetworkElement();
      tempSensor2.setName("Temperature sensor T20");

      // Add sensor element to DataNode
      taipei.addElement(vibSensor1);
      taipei.addElement(tempSensor1);
      tainan.addElement(vibSensor2);
      tainan.addElement(tempSensor2);

      assertEquals("Element size ", 2, taipei.getElements().size());

      // ask cache to manage the domains
      cache1.attach("/taipei", taipei);
      cache1.attach("/tainan", tainan);

      NetworkNode taipei1 = (NetworkNode) cache2.find("/taipei");
      NetworkNode tainan1 = (NetworkNode) cache2.find("/tainan");

      taipei1.setIpAddress("192.168.10.100");
      assertEquals("New admin id is ", "192.168.10.100", taipei.getIpAddress());

      tainan1.setIpAddress("192.168.10.200");
      assertEquals("New admin id is ", "192.168.10.200", tainan.getIpAddress());

      List<NetworkElement> l2 = taipei1.getElements();
      assertEquals("Size is ", 2, l2.size());
      NetworkElement vibSens1 = (NetworkElement) l2.get(0);
      NetworkElement tempSens1 = (NetworkElement) l2.get(1);

      int SUSPENDED = 2;
      vibSens1.setStatus(SUSPENDED);
      tempSens1.setStatus(SUSPENDED);

      assertEquals("Status ", vibSensor1.getStatus(), vibSens1.getStatus());
      assertEquals("Status ", tempSensor1.getStatus(), tempSens1.getStatus());
   }

   /**
    * Domain shares Admin object, domain also contains a list of Nodes, and each DataNode has sensor elements
    */
   public void testAll() throws Exception
   {
      NetworkAdmin admin = new NetworkAdmin();
      admin.setId("Benny");

      // Construct domains
      NetworkDomain tempSensorDomain = new NetworkDomain();
      tempSensorDomain.setName("Temperature sensors");
      tempSensorDomain.setAdmin(admin);

      NetworkDomain vibSensorDomain = new NetworkDomain();
      vibSensorDomain.setName("Vibration sensors");
      vibSensorDomain.setAdmin(admin);

      // Construct nodes
      NetworkNode taipei = new NetworkNode();
      taipei.setName("Taipei machine");
      taipei.setIpAddress("192.168.1.100");

      NetworkNode tainan = new NetworkNode();
      tainan.setName("Tainan machine");
      tainan.setIpAddress("192.168.1.200");

      // Contruct sensor elements
      NetworkElement vibSensor1 = new NetworkElement();
      vibSensor1.setName("Vibration sensor V10");

      NetworkElement vibSensor2 = new NetworkElement();
      vibSensor2.setName("Vibration sensor V20");

      NetworkElement tempSensor1 = new NetworkElement();
      tempSensor1.setName("Temperature sensor T10");

      NetworkElement tempSensor2 = new NetworkElement();
      tempSensor2.setName("Temperature sensor T20");

      // Add sensor element to DataNode
      taipei.addElement(vibSensor1);
      taipei.addElement(tempSensor1);
      tainan.addElement(vibSensor2);
      tainan.addElement(tempSensor2);

      assertEquals("Element size ", 2, taipei.getElements().size());

      // Add elements to domains
      vibSensorDomain.addElement(vibSensor1);
      vibSensorDomain.addElement(vibSensor2);

      tempSensorDomain.addElement(tempSensor1);
      tempSensorDomain.addElement(tempSensor2);

      // ask cache to manage the domains
      cache1.attach("/temp", tempSensorDomain);
      cache1.attach("/vib", vibSensorDomain);

      NetworkDomain temp1 = (NetworkDomain) cache2.find("/temp");
      NetworkDomain vib1 = (NetworkDomain) cache2.find("/vib");

      NetworkNode taipei1 = (NetworkNode) temp1.getNodes().get(0);
//      NetworkNode taipei1 = (NetworkNode)temp1.getNodes().get(1);   this would fail on equality
      NetworkNode taipei2 = (NetworkNode) vib1.getNodes().get(0);

      List<NetworkNode> l1 = temp1.getNodes();
      assertEquals("Size is ", 2, l1.size());

      List<NetworkElement> l2 = taipei.getElements();
      assertEquals("Size is ", 2, l1.size());

      assertEquals("IPAddress ", taipei.getIpAddress(), taipei1.getIpAddress());
      assertTrue("DataNode should be the same", (taipei1 == taipei2));

      taipei2.setIpAddress("192.168.10.100");
      assertEquals("New admin id is ", "192.168.10.100", taipei.getIpAddress());

      l2 = taipei1.getElements();
      assertEquals("Size is ", 2, l2.size());
      NetworkElement vibSens1 = (NetworkElement) l2.get(0);
      NetworkElement tempSens1 = (NetworkElement) l2.get(1);

      int SUSPENDED = 2;
      vibSens1.setStatus(SUSPENDED);
      tempSens1.setStatus(SUSPENDED);

      assertEquals("Status ", vibSensor1.getStatus(), vibSens1.getStatus());
      assertEquals("Status ", tempSensor1.getStatus(), tempSens1.getStatus());
   }
}