package org.jboss.cache.util.internals;

import org.jboss.cache.Cache;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.Region;
import org.jboss.cache.RegionManager;
import org.jboss.cache.util.TestingUtil;
import org.jboss.cache.config.EvictionConfig;
import org.jboss.cache.config.EvictionRegionConfig;
import org.jboss.cache.eviction.EvictionTimerTask;
import org.jboss.cache.eviction.LRUConfiguration;

import java.lang.reflect.Method;
import java.util.Timer;

/**
 * when used on a cache will disable defaul eviction behavior and it will supply means of kicking off evction
 * programmatically. It is intended for replcaing Thread.sleep(xyz) - like statements in which the executing tests wait
 * untill eviction finishes.
 *
 * @author Mircea.Markus@jboss.com
 * @since 2.2
 */
public class EvictionController
{
   CacheSPI cache;
   EvictionTimerTask timerTask;

   public EvictionController(Cache cache)
   {
      this.cache = (CacheSPI) cache;
      RegionManager regionManager = this.cache.getRegionManager();
      if (regionManager == null)
      {
         throw new IllegalStateException("Null region manager; is the cache started?");
      }
      timerTask = (EvictionTimerTask) TestingUtil.extractField(regionManager, "evictionTimerTask");
      if (timerTask == null)
      {
         throw new IllegalStateException("No timer task!!!");
      }
      Timer evictionThread = (Timer) TestingUtil.extractField(timerTask, "evictionThread");
      evictionThread.cancel();
   }

   public void startEviction()
   {
      try
      {
         Method method = EvictionTimerTask.class.getDeclaredMethod("processRegions", new Class[]{});
         method.setAccessible(true);
         method.invoke(timerTask);
      }
      catch (Exception e)
      {
         e.printStackTrace();
         throw new IllegalStateException(e);
      }
   }

   /**
    * Evicts the given region but only after ensuring that region's TTL passed.
    */
   public void evictRegionWithTimeToLive(String region) throws Exception
   {
      EvictionConfig evConfig = cache.getConfiguration().getEvictionConfig();
      EvictionRegionConfig erConfig = evConfig.getEvictionRegionConfig(region);
      if (erConfig == null)
      {
         throw new IllegalStateException("No such region!");
      }
      int ttl = 0;
      if (erConfig.getEvictionPolicyConfig() instanceof LRUConfiguration)
      {
         LRUConfiguration configuration = (LRUConfiguration) erConfig.getEvictionPolicyConfig();
         ttl = configuration.getTimeToLiveSeconds();
      }
      else
      {
         throw new IllegalArgumentException("Only LRU being handled for now; please add other implementations here");
      }
      TestingUtil.sleepThread(ttl * 1000 + 500);
      evictRegion(region);
   }

   /**
    * Only evicts the given region.
    */
   public void evictRegion(String regionStr) throws Exception
   {
      for (Region region : timerTask.getProcessedRegions())
      {
         if (region.getFqn().equals(Fqn.fromString(regionStr)))
         {
            Method method = EvictionTimerTask.class.getDeclaredMethod("handleRegion", Region.class);
            method.setAccessible(true);
            method.invoke(timerTask, region);
         }
      }
   }
}
