/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file 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.system.server.profileservice.repository;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jboss.deployers.vfs.spi.structure.modified.StructureModificationChecker;
import org.jboss.profileservice.spi.DeploymentContentFlags;
import org.jboss.profileservice.spi.ModificationInfo;
import org.jboss.profileservice.spi.ProfileDeployment;
import org.jboss.profileservice.spi.ProfileKey;
import org.jboss.profileservice.spi.ModificationInfo.ModifyStatus;
import org.jboss.vfs.VirtualFile;

/**
 * A deployment repository, with hot deployment capabilities.
 * 
 * @author <a href="mailto:emuckenh@redhat.com">Emanuel Muckenhuber</a>
 * @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a>
 * @version $Revision: 102225 $
 */
public class HotDeploymentRepository extends BasicDeploymentRepository
{

   /** The structure modification checker */
   private StructureModificationChecker checker;
   
   public HotDeploymentRepository(ProfileKey key, URI[] uris)
   {
      super(key, uris);
   }

   /**
    * Get the structure modified checker.
    *
    * @return the checker
    */
   protected StructureModificationChecker getChecker()
   {
      if (checker == null)
         throw new IllegalArgumentException("Checker must be set");

      return checker;
   }

   /**
    * Set the checker.
    *
    * @param checker the checker
    */
   public void setChecker(StructureModificationChecker checker)
   {
      this.checker = checker;
   }

   @Override
   public synchronized Collection<ModificationInfo> getModifiedDeployments() throws Exception
   {
      boolean trace = log.isTraceEnabled();
      Collection<ProfileDeployment> apps = getDeployments();
      Map<String, ModificationInfo> modifications = new HashMap<String, ModificationInfo>();
      if (trace)
         log.trace("Checking applications for modifications");
      if (trace)
         log.trace("Aquiring content read lock");
      lockRead();
      try
      {
         if (apps != null)
         {
            Iterator<ProfileDeployment> iter = apps.iterator();
            while (iter.hasNext())
            {
               ProfileDeployment ctx = iter.next();
               VirtualFile root = ctx.getRoot();
               String pathName = ctx.getName();
               // Ignore locked or disabled applications
               if (this.hasDeploymentContentFlags(pathName, ignoreFlags))
               {
                  if (trace)
                     log.trace("Ignoring locked application: " + root);
                  continue;
               }
               // Check for removal
               if (root.exists() == false)
               {
                  long rootLastModified = root.getLastModified();
                  ModificationInfo info = new ModificationInfo(ctx, rootLastModified, ModifyStatus.REMOVED);
                  modifications.put(pathName, info);
                  iter.remove();
                  // Remove last modified cache
                  cleanUpRoot(root);
                  cleanupDeployment(pathName);
                  if (trace)
                     log.trace(pathName + " was removed");
               }
               // Check for modification
               else if (hasDeploymentContentFlags(pathName, DeploymentContentFlags.MODIFIED)
                     || getChecker().hasStructureBeenModified(pathName, root))
               {
                  long rootLastModified = root.getLastModified();
                  if (trace)
                     log.trace(pathName + " was modified: " + rootLastModified);
                  // Create the modification info
                  ModificationInfo info = new ModificationInfo(ctx, rootLastModified, ModifyStatus.MODIFIED);
                  modifications.put(pathName, info);
               }
            }
            // Now check for additions
            checkForAdditions(modifications);
         }
      }
      finally
      {
         unlockRead();
         if (trace)
            log.trace("Released content read lock");
      }

      if (modifications.size() > 0)
         updateLastModfied();
      return modifications.values();
   }

   /**
    * Check for additions.
    *
    * @param modified the modified list
    * @throws Exception for any error
    */
   protected void checkForAdditions(Map<String, ModificationInfo> modified) throws Exception
   {
      for (URI applicationDir : getRepositoryURIs())
      {
         VirtualFile deployDir = getCachedVirtualFile(applicationDir);
         List<VirtualFile> added = new ArrayList<VirtualFile>();
         addedDeployment(added, deployDir);         
         applyAddedDeployments(applicationDir, modified, added);
      }
   }

   /**
    * Apply added deployments.
    *
    * @param applicationDir the app dir
    * @param modified the modifed list
    * @param added the added deployments
    * @throws Exception for any error
    */
   protected void applyAddedDeployments(URI applicationDir, Map<String, ModificationInfo> modified, List<VirtualFile> added) throws Exception
   {
      for (VirtualFile vf : added)
      {
         String pathName = createDeploymentName(vf);
         if(modified.containsKey(pathName) == false)
         {
            // Create deployment
            ProfileDeployment ctx = createDeployment(vf);
            // Create modification info
            ModificationInfo info = new ModificationInfo(ctx, vf.getLastModified(), ModifyStatus.ADDED);
            // Add
            modified.put(pathName, info);
            internalAddDeployment(ctx.getName(), ctx);
            getChecker().addStructureRoot(vf);
         }
      }
   }

   @Override
   protected void cleanUpRoot(VirtualFile vf)
   {
      getChecker().removeStructureRoot(vf);
   }
}
