/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.cache;


import org.jboss.cache.config.Configuration;
import org.jboss.cache.marshall.CacheMarshaller210;
import org.jboss.cache.marshall.Marshaller;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.HashMap;

/**
 * Tests {@link Fqn}.
 *
 * @author <a href="mailto:bela@jboss.org">Bela Ban</a> May 9, 2003
 * @version $Revision: 5938 $
 */
@Test(groups = "unit")
public class FqnTest
{
   private Cache<Object, Object> cache;
   private Marshaller marshaller;

   @BeforeTest
   protected void setUp()
   {
      cache = null;
      marshaller = new CacheMarshaller210();
   }

   @AfterTest
   protected void tearDown()
   {
      if (cache != null)
      {
         cache.stop();
         cache = null;
      }
   }

   public void testNull()
   {
      Fqn fqn = Fqn.ROOT;
      log("null fqn is " + fqn);
      assert 0 == fqn.size();
      int hcode = fqn.hashCode();
      assert hcode != -1;
   }

   public void testOne()
   {
      Fqn<Integer> fqn = Fqn.fromElements(22);
      log("one fqn is " + fqn);
      assert 1 == fqn.size();
      int hcode = fqn.hashCode();
      assert hcode != -1;
   }

   public void testEmptyFqn()
   {
      Fqn f1 = Fqn.ROOT;
      Fqn f2 = Fqn.ROOT;
      assert f1.equals(f2);
   }

   public void testFqn()
   {
      Fqn<String> fqn = Fqn.fromString("/a/b/c");
      log("fqn is " + fqn);
      assert 3 == fqn.size();

      Fqn<String> fqn2 = Fqn.fromElements("a", "b", "c");
      log("fqn2 is " + fqn2);
      assert 3 == fqn.size();
      assert fqn.equals(fqn2);
      assert fqn.hashCode() == fqn2.hashCode();
   }

   public void testHereogeneousNames()
   {
      Fqn fqn = Fqn.fromElements("string", 38, true);
      log("fqn is " + fqn);
      assert 3 == fqn.size();

      Fqn fqn2 = Fqn.fromElements("string", 38, true);
      assert fqn.equals(fqn2);
      assert fqn.hashCode() == fqn2.hashCode();
   }

   public void testHashcode()
   {
      Fqn fqn1, fqn2;
      fqn1 = Fqn.fromElements("a", "b", "c");
      fqn2 = Fqn.fromString("/a/b/c");
      log("fqn is " + fqn1);
      assert fqn1.equals(fqn2);

      HashMap<Fqn, Integer> map = new HashMap<Fqn, Integer>();
      map.put(fqn1, 33);
      map.put(fqn2, 34);
      assert map.size() == 1;
      assert map.get(fqn1).equals(34);
   }

   public void testHashcode2()
   {
      Fqn<Integer> fqn = Fqn.fromElements(-1);
      log("one fqn is " + fqn);
      assert fqn.size() == 1;
      int hcode = fqn.hashCode();
      assert hcode == -1;
   }

   public void testEquals()
   {
      Fqn<String> fqn1 = Fqn.fromElements("person/test");

      Fqn f1, f2, f3;

      f1 = Fqn.fromRelativeElements(fqn1, "0");
      f2 = Fqn.fromRelativeElements(fqn1, "1");
      f3 = Fqn.fromRelativeElements(fqn1, "2");

      HashMap<Fqn, String> map = new HashMap<Fqn, String>();
      map.put(f1, "0");
      map.put(f2, "1");
      map.put(f3, "2");

      assert map.get(Fqn.fromRelativeElements(fqn1, "0")) != null;
      assert map.get(Fqn.fromRelativeElements(fqn1, "1")) != null;
      assert map.get(Fqn.fromRelativeElements(fqn1, "2")) != null;

   }

   public void testEquals2()
   {
      Fqn<String> f1;
      Fqn<String> f2;
      f1 = Fqn.fromString("/a/b/c");
      f2 = Fqn.fromString("/a/b/c");
      assert f1.equals(f2);

      f2 = Fqn.fromString("/a/b");
      assert !f1.equals(f2);

      f2 = Fqn.fromString("/a/b/c/d");
      assert !f1.equals(f2);
   }

   public void testEquals2WithMarshalling() throws Exception
   {
      Fqn<String> f1, f2;
      f1 = Fqn.fromString("/a/b/c");
      f2 = marshalAndUnmarshal(f1);
      assert f1.equals(f2);
   }

   public void testEquals3()
   {
      Fqn f1;
      Fqn<?> f2;
      f1 = Fqn.fromElements("a", 322649, Boolean.TRUE);
      f2 = Fqn.ROOT;
      assert !f1.equals(f2);
      assert !f2.equals(f1);

      f2 = Fqn.fromString("a/322649/TRUE");
      assert !f1.equals(f2);

      f2 = Fqn.fromElements("a", 322649, Boolean.FALSE);
      assert !f1.equals(f2);

      f2 = Fqn.fromElements("a", 322649, Boolean.TRUE);
      assert f1.equals(f2);
   }

   public void testEquals3WithMarshalling() throws Exception
   {
      Fqn f1, f2;
      f1 = Fqn.fromElements("a", 322649, Boolean.TRUE);
      f2 = marshalAndUnmarshal(f1);
      assert f1.equals(f2);
      assert f2.equals(f1);

      Fqn<String> f3 = Fqn.fromString("a/322649/TRUE");
      f3 = marshalAndUnmarshal(f3);
      assert !f1.equals(f3);

      f2 = Fqn.fromElements("a", 322649, Boolean.FALSE);
      f2 = marshalAndUnmarshal(f2);
      assert !f1.equals(f2);

      f2 = Fqn.fromElements("a", 322649, Boolean.TRUE);
      f2 = marshalAndUnmarshal(f2);
      assert f1.equals(f2);
   }

   public void testEquals4()
   {
      Fqn<String> fqn = Fqn.fromString("X");
      // Check casting
      assert !fqn.equals("X");
      // Check null
      assert !fqn.equals(null);
   }

   public void testClone() throws CloneNotSupportedException
   {
      Fqn<String> fqn1 = Fqn.fromString("/a/b/c");
      Fqn<String> fqn2 = fqn1.clone();
      assert fqn1.equals(fqn2);
      assert fqn1.hashCode() == fqn2.hashCode();
   }

   public void testNullElements() throws CloneNotSupportedException
   {
      Fqn<Object> fqn0 = Fqn.fromElements((Object) null);
      assert 1 == fqn0.size();

      Fqn fqn1 = Fqn.fromElements("NULL", null, 0);
      assert 3 == fqn1.size();

      Fqn fqn2 = Fqn.fromElements("NULL", null, 0);
      assert fqn1.hashCode() == fqn2.hashCode();
      assert fqn1.equals(fqn2);
      assert fqn1.equals(fqn1.clone());
   }

   public void testIteration()
   {
      Fqn<String> fqn = Fqn.fromString("/a/b/c");
      assert 3 == fqn.size();
      Fqn<Object> tmp_fqn = Fqn.ROOT;
      assert 0 == tmp_fqn.size();
      for (int i = 0; i < fqn.size(); i++)
      {
         String s = fqn.get(i);
         tmp_fqn = Fqn.fromRelativeElements(tmp_fqn, s);
         assert tmp_fqn.size() == i + 1;
      }
      assert 3 == tmp_fqn.size();
      assert fqn.equals(tmp_fqn);
   }

   public void testIsChildOf()
   {
      Fqn<String> child = Fqn.fromString("/a/b");
      Fqn<String> parent = Fqn.fromString("/a");
      assert child.isChildOf(parent);
      assert !parent.isChildOf(child);
      assert child.isChildOrEquals(child);

      parent = Fqn.fromString("/a/b/c");
      child = Fqn.fromString("/a/b/c/d/e/f/g/h/e/r/e/r/t/tt/");
      assert child.isChildOf(parent);
   }

   public void testIsChildOf2()
   {
      Fqn<String> child = Fqn.fromString("/a/b/c/d");
      assert "/b/c/d".equals(child.getSubFqn(1, child.size()).toString());
   }

   public void testParentage()
   {
      Fqn<String> fqnRoot = Fqn.ROOT;
      Fqn<String> parent = fqnRoot.getParent();
      assert parent.equals(fqnRoot);

      Fqn<String> fqnOne = Fqn.fromString("/one");
      parent = fqnOne.getParent();
      assert parent.equals(fqnRoot);
      assert fqnOne.isChildOf(parent);

      Fqn<String> fqnTwo = Fqn.fromString("/one/two");
      parent = fqnTwo.getParent();
      assert parent.equals(fqnOne);
      assert fqnTwo.isChildOf(parent);

      Fqn<String> fqnThree = Fqn.fromString("/one/two/three");
      parent = fqnThree.getParent();
      assert parent.equals(fqnTwo);
      assert fqnThree.isChildOf(parent);

   }

   public void testRoot()
   {
      Fqn<String> fqn = Fqn.ROOT;
      assert fqn.isRoot();

      fqn = Fqn.fromString("/one/two");
      assert !fqn.isRoot();

      Fqn f = Fqn.fromString("/");

      assert f.isRoot();
      assert f.equals(Fqn.ROOT);
   }

   public void testGetName()
   {
      Fqn<Integer> integerFqn = Fqn.fromElements(1);
      assert "1".equals(integerFqn.getLastElementAsString());

      Object object = new Object();
      Fqn<Object> objectFqn = Fqn.fromElements(object);
      assert object.toString().equals(objectFqn.getLastElementAsString());
   }

   public void testCloningString() throws CloneNotSupportedException
   {
      Fqn<String> f = Fqn.fromString("/a/b/c");
      assert f.equals(f.clone());
   }

   public void testCloningOtherTypes() throws CloneNotSupportedException
   {
      Fqn f = Fqn.fromElements("blah", 10, Boolean.TRUE);
      assert f.equals(f.clone());
   }

   public void testRemovalNonString() throws Exception
   {
      Fqn f = Fqn.fromElements("test", 1);

      Configuration c = new Configuration();
      c.setCacheMode("LOCAL");
      cache = new DefaultCacheFactory().createCache(c);

      cache.put(f, "key", "value");

      assert "value".equals(cache.get(f, "key"));
      assert cache.getRoot().hasChild(f);

      cache.removeNode(f);

      assert cache.get(f, "key") == null;
      assert !cache.getRoot().hasChild(f);
   }

   @SuppressWarnings("unchecked")
         <T> Fqn<T> marshalAndUnmarshal(Fqn<T> fqn) throws Exception
   {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream out = new ObjectOutputStream(baos);
      marshaller.objectToObjectStream(fqn, out);
      out.close();
      baos.close();
      ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
      return (Fqn<T>) marshaller.objectFromObjectStream(in);
   }

   // testing generics

   public void testGenerics()
   {
      Fqn<String> f = Fqn.fromString("/blah/blah");
      Fqn<String> f2 = Fqn.fromString("/blah/blah");

      assert f.equals(f2);

      Fqn<Integer> f3 = Fqn.fromElements(1, 2, 3, 5);

      Fqn<Integer> f4 = Fqn.fromElements(1, 2, 3);

      assert f3.getParent().equals(f4);
   }

   public void testSize()
   {
      Fqn f = Fqn.ROOT;
      assert f.size() == 0;
      assert f.isRoot();

      f = Fqn.fromString("/");
      assert f.size() == 0;
      assert f.isRoot();

      f = Fqn.fromString("/hello");
      assert f.size() == 1;
      assert !f.isRoot();
   }

//   public void testIntermediateFqns()
//   {
//      Fqn lastCreated = Fqn.fromString("/a/b");
//      Fqn target = Fqn.fromString("/a/b/c/d/e/f");
//
//      List<Fqn> intermediates = new ArrayList<Fqn>();
//      intermediates.add(Fqn.fromString("/a/b/c"));
//      intermediates.add(Fqn.fromString("/a/b/c/d"));
//      intermediates.add(Fqn.fromString("/a/b/c/d/e"));
//
//      System.out.println("Expecting " + intermediates);
//      System.out.println("Was: " + target.getIntermediateFqns(lastCreated));
//
//      assert target.getIntermediateFqns(lastCreated).equals(intermediates);
//   }

   //

   public void testGenerations()
   {
      Fqn<Integer> f = Fqn.fromElements(1, 2, 3, 4, 5, 6, 7);

      assert f.equals(f.getAncestor(f.size()));
      assert f.getParent().equals(f.getAncestor(f.size() - 1));
      assert Fqn.ROOT.equals(f.getAncestor(0));
      assert Fqn.fromElements(1).equals(f.getAncestor(1));
      assert Fqn.fromElements(1, 2).equals(f.getAncestor(2));
      assert Fqn.fromElements(1, 2, 3).equals(f.getAncestor(3));
      assert Fqn.fromElements(1, 2, 3, 4).equals(f.getAncestor(4));
      assert Fqn.fromElements(1, 2, 3, 4, 5).equals(f.getAncestor(5));

      try
      {
         f.getAncestor(-1);
         // should fail
         assert false;
      }
      catch (IllegalArgumentException good)
      {
         // expected
      }

      try
      {
         f.getAncestor(f.size() + 1);
         // should fail
         assert false;
      }
      catch (IndexOutOfBoundsException good)
      {
         // expected
      }
   }

   void log(String msg)
   {
      System.out.println("-- " + msg);
   }

   public void testDifferentFactories()
   {
      Fqn[] fqns = new Fqn[7];
      int i = 0;
      fqns[i++] = Fqn.fromString("/a/b/c");
      fqns[i++] = Fqn.fromRelativeElements(Fqn.ROOT, "a", "b", "c");
      fqns[i++] = Fqn.fromElements("a", "b", "c");
      fqns[i++] = Fqn.fromList(Arrays.asList(new String[]{"a", "b", "c"}));
      fqns[i++] = Fqn.fromRelativeList(Fqn.ROOT, Arrays.asList(new String[]{"a", "b", "c"}));
      fqns[i++] = Fqn.fromRelativeFqn(Fqn.ROOT, Fqn.fromString("/a/b/c"));
      fqns[i] = new Fqn("a", "b", "c"); // "old-style" Fqn

      // all of the above should be equal to each other.
      for (i = 0; i < fqns.length; i++)
      {
         for (int j = 0; j < fqns.length; j++)
         {
            assert fqns[i].equals(fqns[j]) : "Error on equals comparing " + i + " and " + j;
            assert fqns[j].equals(fqns[i]) : "Error on equals comparing " + i + " and " + j;
            assert fqns[i].hashCode() == fqns[j].hashCode() : "Error on hashcode comparing " + i + " and " + j;
         }
      }
   }
}
