/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package org.jboss.cache.pojo.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
import org.jboss.cache.pojo.PojoCacheException;

/**
 * Handle the object graph management.
 *
 * @author Ben Wang
 *         Date: Aug 4, 2005
 * @version $Id: ObjectGraphHandler.java 6110 2008-06-27 22:06:51Z jason.greene@jboss.com $
 */
class ObjectGraphHandler extends AbstractHandler
{
   private PojoCacheImpl cache;
   private InternalHelper internal_;
   private final static Log log = LogFactory.getLog(ObjectGraphHandler.class);

   public ObjectGraphHandler(PojoCacheImpl cache, InternalHelper internal)
   {
      this.cache = cache;
      internal_ = internal;
   }

   protected Fqn<?> getFqn(Object obj)
   {
      return null;
   }

   protected boolean handles(Class<?> clazz)
   {
      return false;
   }

   @Override
   protected Object get(Fqn<?> fqn, Class<?> clazz, PojoInstance pojoInstance) throws CacheException
   {
      // Note this is actually the aliasFqn, not the real fqn!
      Object obj;

      obj = cache.find(fqn);
      if (obj == null)
         throw new PojoCacheException("ObjectGraphHandler.get(): null object from internal ref node." +
                                      " Internal ref node: " + fqn);

      return obj; // No need to set the instance under fqn. It is located in refFqn anyway.
   }

   @Override
   protected void put(Fqn<?> fqn, Fqn<?> referencingFqn, Object obj) throws CacheException
   {
      setupRefCounting(fqn, referencingFqn);
   }

   boolean isMultipleReferenced(Fqn<?> internalFqn)
   {
      // Note this is actually the aliasFqn, not the real fqn!
      PojoInstance pojoInstance = null;
      try
      {
         pojoInstance = internal_.getPojoInstance(internalFqn);
      }
      catch (CacheException e)
      {
         throw new PojoCacheException("Exception in isMultipleReferenced", e);
      }
      // check if this is a refernce
      return InternalHelper.isMultipleReferenced(pojoInstance);

   }

   @Override
   protected Object remove(Fqn<?> fqn, Fqn<?> referencingFqn, Object pojo)
         throws CacheException
   {
      if (log.isDebugEnabled())
      {
         log.debug("remove(): removing object fqn: " + referencingFqn
                   + " Will just de-reference it.");
      }
      removeFromReference(fqn, referencingFqn);

      return null;
   }

   /**
    * Remove the object from the the reference fqn, meaning just decrement the ref counter.
    */
   private void removeFromReference(Fqn<?> originalFqn, Fqn<?> referencingFqn) throws CacheException
   {
      synchronized (originalFqn)
      {  // we lock the internal fqn here so no one else has access.
         // Decrement ref counting on the internal node
         if (decrementRefCount(originalFqn, referencingFqn) == PojoInstance.INITIAL_COUNTER_VALUE)
         {
            // No one is referring it so it is safe to remove
            // TODO we should make sure the parent nodes are also removed they are empty as well.
            cache.detach(originalFqn);
         }
      }
   }

   /**
    * 1. increment reference counter
    * 2. put in refFqn so we can get it.
    *
    * @param fqn    The original fqn node
    * @param refFqn The new internal fqn node
    */
   private void setupRefCounting(Fqn<?> fqn, Fqn<?> referencingFqn) throws CacheException
   {
      synchronized (fqn)
      {
         // increment the reference counting
         incrementRefCount(fqn, referencingFqn);
      }
   }

   private int incrementRefCount(Fqn<?> originalFqn, Fqn<?> referencingFqn) throws CacheException
   {
      return internal_.incrementRefCount(originalFqn, referencingFqn);
   }

   private int decrementRefCount(Fqn<?> originalFqn, Fqn<?> referencingFqn) throws CacheException
   {
      return internal_.decrementRefCount(originalFqn, referencingFqn);
   }
}
