package org.jboss.cache.eviction;

import org.jboss.cache.Fqn;
import org.jboss.cache.Region;
import org.jboss.cache.RegionManager;
import org.jboss.cache.config.EvictionConfig;
import org.jboss.cache.config.EvictionPolicyConfig;
import org.jboss.cache.config.EvictionRegionConfig;
import org.jboss.cache.factories.XmlConfigurationParser;
import org.jboss.cache.xml.XmlHelper;
import static org.testng.AssertJUnit.*;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.w3c.dom.Element;

import java.util.List;

/**
 * Region Manager unit tests.
 *
 * @author Ben Wang, Feb 11, 2004
 * @author Daniel Huang (dhuang@jboss.org)
 * @version $Revision: 5478 $
 */
@Test(groups = "functional")
public class RegionManagerTest
{
   private final Fqn DEFAULT_REGION = Fqn.ROOT;
   Fqn A_B_C = Fqn.fromString("/a/b/c");
   Fqn A_B = Fqn.fromString("/a/b");
   Fqn A_BC = Fqn.fromString("/a/bc");
   Fqn AOP = Fqn.fromString("/aop");


   EvictionPolicy policy;
   EvictionPolicyConfig config;

   @BeforeMethod(alwaysRun = true)
   public void setUp() throws Exception
   {
      policy = new DummyEvictionPolicy();
      config = new DummyEvictionConfiguration();
   }

   public void testCreateRegion()
   {
      RegionManager regionManager = new RegionManager();
      regionManager.setUsingEvictions(true);
      regionManager.getRegion(DEFAULT_REGION, true).setEvictionPolicy(config);
      regionManager.getRegion(A_B_C, true).setEvictionPolicy(config);
      regionManager.getRegion(A_B, true).setEvictionPolicy(config);
      regionManager.getRegion(AOP, true).setEvictionPolicy(config);

      List<Region> regions = regionManager.getAllRegions(Region.Type.ANY);
      assertEquals("Region size ", 4, regions.size());
   }

   public void testCreateRegion2()
   {

      RegionManager regionManager = new RegionManager();
      regionManager.setUsingEvictions(true);
      regionManager.getRegion(A_B_C, true).setEvictionPolicy(config);
      regionManager.getRegion(A_B, true).setEvictionPolicy(config);
      regionManager.getRegion(DEFAULT_REGION, true).setEvictionPolicy(config);

      List<Region> regions = regionManager.getAllRegions(Region.Type.ANY);
      assertEquals("Region size ", 3, regions.size());
      assertEquals("Region 0", DEFAULT_REGION, regions.get(0).getFqn());
      assertEquals("Region 1 ", A_B, regions.get(1).getFqn());
      assertEquals("Region 2 ", A_B_C, regions.get(2).getFqn());
      Region region = regionManager.getRegion("/a/b/c/d", false);
      assertNotNull("Region ", region);
      assertEquals("Region ", A_B_C, region.getFqn());
      region = regionManager.getRegion(A_B, false);
      assertNotNull("Region ", region);
      assertEquals("Region ", A_B, region.getFqn());
      region = regionManager.getRegion("/a", false);
      // Should be default.
      assertNotNull("Region ", region);
      assertEquals("Region ", DEFAULT_REGION, region.getFqn());
   }

   public void testNoDefaultRegion()
   {
      RegionManager regionManager = new RegionManager();
      regionManager.setUsingEvictions(true);
      regionManager.getRegion(A_B_C, true).setEvictionPolicy(config);
      regionManager.getRegion(A_B, true).setEvictionPolicy(config);

      regionManager.getRegion(Fqn.fromString("/a"), Region.Type.EVICTION, false);
   }

   public void testGetRegion()
   {
      RegionManager regionManager = new RegionManager();
      regionManager.setUsingEvictions(true);
      regionManager.getRegion(DEFAULT_REGION, true).setEvictionPolicy(config);
      regionManager.getRegion(A_BC, true).setEvictionPolicy(config);
      regionManager.getRegion(A_B, true).setEvictionPolicy(config);

      Region region = regionManager.getRegion(A_BC, true);
      assertNotSame("Region ", DEFAULT_REGION, region.getFqn());
   }

   public void testConfigureWithXML() throws Exception
   {
      // test the new style configuration
      String xml = "<region name=\"/test/\" policyClass=\"org.jboss.cache.eviction.LFUPolicy\">" +
            "<attribute name=\"minNodes\">10</attribute>" +
            "<attribute name=\"maxNodes\">20</attribute>" +
            "</region>";
      Element element = XmlHelper.stringToElement(xml);
      RegionManager regionManager = new RegionManager();
      regionManager.setUsingEvictions(true);
      EvictionRegionConfig erc = XmlConfigurationParser.parseEvictionRegionConfig(element, null, EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT);
      Region region = regionManager.getRegion(erc.getRegionFqn(), true);
      region.setEvictionPolicy(erc.getEvictionPolicyConfig());

      assertTrue(region.getEvictionPolicy() instanceof LFUPolicy);
      assertTrue(region.getEvictionPolicyConfig() instanceof LFUConfiguration);
      LFUConfiguration config = (LFUConfiguration) region.getEvictionPolicyConfig();
      assertEquals(20, config.getMaxNodes());
      assertEquals(10, config.getMinNodes());
      assertEquals(Fqn.fromString("/test/"), region.getFqn());

      // test the 1.2.x style configuration
      xml = "<region name=\"abc\">" +
            "<attribute name=\"minNodes\">10</attribute>" +
            "<attribute name=\"maxNodes\">20</attribute>" +
            "</region>";
      element = XmlHelper.stringToElement(xml);
      erc = XmlConfigurationParser.parseEvictionRegionConfig(element, "org.jboss.cache.eviction.LFUPolicy", EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT);
      regionManager = new RegionManager();
      regionManager.setUsingEvictions(true);
      region = regionManager.getRegion(erc.getRegionFqn(), true);
      region.setEvictionPolicy(erc.getEvictionPolicyConfig());

      assertTrue(region.getEvictionPolicy() instanceof LFUPolicy);
      assertTrue(region.getEvictionPolicyConfig() instanceof LFUConfiguration);
      config = (LFUConfiguration) region.getEvictionPolicyConfig();
      assertEquals(20, config.getMaxNodes());
      assertEquals(10, config.getMinNodes());
      assertEquals(Fqn.fromString("/abc/"), region.getFqn());
   }

   public void testRegionOrdering() throws Exception
   {
      Fqn A_B_C_D_E = Fqn.fromString("/a/b/c/d/e/");
      Fqn A_B_C_D = Fqn.fromString("/a/b/c/d/");

      RegionManager rm = new RegionManager();
      rm.setUsingEvictions(true);
      rm.getRegion(DEFAULT_REGION, true).setEvictionPolicy(config);
      rm.getRegion(A_B_C_D_E, true).setEvictionPolicy(config);
      rm.getRegion(A_B_C_D, true).setEvictionPolicy(config);
      rm.getRegion(A_B_C, true).setEvictionPolicy(config);

      Region region = rm.getRegion("/a/b/c/d/e/f", false);
      Region region2 = rm.getRegion("/e/f/g", false);

      assertEquals(A_B_C_D_E, region.getFqn());
      assertEquals(DEFAULT_REGION, region2.getFqn());

      List<Region> regions = rm.getAllRegions(Region.Type.ANY);
      for (int i = 0; i < regions.size(); i++)
      {
         switch (i)
         {
            case 0:
               assertEquals(DEFAULT_REGION, regions.get(i).getFqn());
               break;
            case 1:
               assertEquals(A_B_C, regions.get(i).getFqn());
               break;
            case 2:
               assertEquals(A_B_C_D, regions.get(i).getFqn());
               break;
            case 3:
               assertEquals(A_B_C_D_E, regions.get(i).getFqn());
               break;
            default:
               fail("This error condition should never be reached");
               break;
         }
      }
   }
}
