/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.cache.interceptors;

import org.jboss.cache.DataContainer;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.commands.read.GetKeyValueCommand;
import org.jboss.cache.commands.write.EvictCommand;
import org.jboss.cache.commands.write.PutDataMapCommand;
import org.jboss.cache.commands.write.PutForExternalReadCommand;
import org.jboss.cache.commands.write.PutKeyValueCommand;
import org.jboss.cache.factories.annotations.Inject;
import org.jboss.cache.interceptors.base.CommandInterceptor;

import java.util.HashMap;
import java.util.Map;

/**
 * Captures cache management statistics
 *
 * @author Jerry Gauthier
 * @version $Id: CacheMgmtInterceptor.java 6000 2008-06-17 23:43:54Z manik.surtani@jboss.com $
 */
public class CacheMgmtInterceptor extends CommandInterceptor implements CacheMgmtInterceptorMBean
{
   private long m_hit_times = 0;
   private long m_miss_times = 0;
   private long m_store_times = 0;
   private long m_hits = 0;
   private long m_misses = 0;
   private long m_stores = 0;
   private long m_evictions = 0;
   private long m_start = System.currentTimeMillis();
   private long m_reset = m_start;

   private DataContainer dataContainer;

   @Inject
   public void setDependencies(DataContainer dataContainer)
   {
      this.dataContainer = dataContainer;
   }

   @Override
   public Object visitEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable
   {
      Object returnValue = invokeNextInterceptor(ctx, command);
      m_evictions++;
      return returnValue;
   }

   @Override
   public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable
   {
      long t1 = System.currentTimeMillis();
      Object retval = invokeNextInterceptor(ctx, command);
      long t2 = System.currentTimeMillis();
      if (retval == null)
      {
         m_miss_times = m_miss_times + (t2 - t1);
         m_misses++;
      }
      else
      {
         m_hit_times = m_hit_times + (t2 - t1);
         m_hits++;
      }
      return retval;
   }

   @Override
   public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable
   {
      Map data = command.getData();
      long t1 = System.currentTimeMillis();
      Object retval = invokeNextInterceptor(ctx, command);
      long t2 = System.currentTimeMillis();

      if (data != null && data.size() > 0)
      {
         m_store_times = m_store_times + (t2 - t1);
         m_stores = m_stores + data.size();
      }
      return retval;
   }


   @Override
   public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable
   {
      return visitPutKeyValueCommand(ctx, command);
   }

   @Override
   public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable
   {
      long t1 = System.currentTimeMillis();
      Object retval = invokeNextInterceptor(ctx, command);
      long t2 = System.currentTimeMillis();
      m_store_times = m_store_times + (t2 - t1);
      m_stores++;
      return retval;
   }

   public long getHits()
   {
      return m_hits;
   }

   public long getMisses()
   {
      return m_misses;
   }

   public long getStores()
   {
      return m_stores;
   }

   public long getEvictions()
   {
      return m_evictions;
   }

   public double getHitMissRatio()
   {
      double total = m_hits + m_misses;
      if (total == 0)
         return 0;
      return (m_hits / total);
   }

   public double getReadWriteRatio()
   {
      if (m_stores == 0)
         return 0;
      return (((double) (m_hits + m_misses) / (double) m_stores));
   }

   public long getAverageReadTime()
   {
      long total = m_hits + m_misses;
      if (total == 0)
         return 0;
      return (m_hit_times + m_miss_times) / total;
   }

   public long getAverageWriteTime()
   {
      if (m_stores == 0)
         return 0;
      return (m_store_times) / m_stores;
   }

   public int getNumberOfAttributes()
   {
      return dataContainer.getNumberOfAttributes();
   }

   public int getNumberOfNodes()
   {
      return dataContainer.getNumberOfNodes();
   }

   public long getElapsedTime()
   {
      return (System.currentTimeMillis() - m_start) / 1000;
   }

   public long getTimeSinceReset()
   {
      return (System.currentTimeMillis() - m_reset) / 1000;
   }

   @Override
   public Map<String, Object> dumpStatistics()
   {
      Map<String, Object> retval = new HashMap<String, Object>();
      retval.put("Hits", m_hits);
      retval.put("Misses", m_misses);
      retval.put("Stores", m_stores);
      retval.put("Evictions", m_evictions);
      retval.put("NumberOfAttributes", dataContainer.getNumberOfAttributes());
      retval.put("NumberOfNodes", dataContainer.getNumberOfNodes());
      retval.put("ElapsedTime", getElapsedTime());
      retval.put("TimeSinceReset", getTimeSinceReset());
      retval.put("AverageReadTime", getAverageReadTime());
      retval.put("AverageWriteTime", getAverageWriteTime());
      retval.put("HitMissRatio", getHitMissRatio());
      retval.put("ReadWriteRatio", getReadWriteRatio());
      return retval;
   }

   @Override
   public void resetStatistics()
   {
      m_hits = 0;
      m_misses = 0;
      m_stores = 0;
      m_evictions = 0;
      m_hit_times = 0;
      m_miss_times = 0;
      m_store_times = 0;
      m_reset = System.currentTimeMillis();
   }
}

