/*
* JBoss, Home of Professional Open Source
* Copyright 2009, 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.system.server.profileservice.profile.plugins;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import org.jboss.classloading.spi.metadata.ClassLoadingMetaData;
import org.jboss.classloading.spi.vfs.metadata.VFSClassLoaderFactory10;
import org.jboss.logging.Logger;
import org.jboss.profileservice.repository.ProfileDeploymentFactory;
import org.jboss.profileservice.spi.NoSuchDeploymentException;
import org.jboss.profileservice.spi.Profile;
import org.jboss.profileservice.spi.ProfileDeployment;
import org.jboss.profileservice.spi.ProfileKey;
import org.jboss.system.server.profileservice.profile.plugins.ClassPathProfileMetaData.ClassPathItem;
import org.jboss.vfs.VFS;
import org.jboss.vfs.VirtualFile;
import org.jboss.vfs.VirtualFileFilter;

/**
 * ClassPath adapter profile. 
 * 
 * FIXME - test if this actually works
 * 
 * TODO - does this really make sense as profile?
 * at least we need to move this out of the profileservice codebase. 
 * 
 * @author <a href="mailto:emuckenh@redhat.com">Emanuel Muckenhuber</a>
 * @version $Revision$
 */
public class ClassPathAdapterProfile implements Profile
{

   private static final Logger log = Logger.getLogger(ClassPathAdapterProfile.class);
   private final ProfileKey key;
   private final String deploymentName; 
   private final List<ClassPathItem> items;
   private final long lastModified;
   private ProfileDeployment classPathDeployment;
   
   public ClassPathAdapterProfile(ProfileKey key, List<ClassPathItem> items)
   {
      if(key == null)
      {
         throw new IllegalArgumentException("null profile key");
      }
      this.key = key;
      this.deploymentName = key.getName();
      this.items = items;
      this.lastModified = System.currentTimeMillis();
   }
   
   public void create() throws Exception
   {
      List<VirtualFile> classpath = determineRoots();
      List<String> roots = new ArrayList<String>();
      for(VirtualFile vf : classpath)
      {
         roots.add(vf.toURI().toString());
      }
      VFSClassLoaderFactory10 clMetaData = new VFSClassLoaderFactory10();
      clMetaData.setRoots(roots);
      // Create deployment
      classPathDeployment = ProfileDeploymentFactory.getInstance().createDeployment(deploymentName);
      classPathDeployment.getPredeterminedAttachments().putAttachment(ClassLoadingMetaData.class.getName(), clMetaData);
   }
   
   @Override
   public ProfileDeployment getDeployment(String name) throws NoSuchDeploymentException
   {
      if(deploymentName.equals(name))
      {
         return classPathDeployment;
      }
      throw new NoSuchDeploymentException(name);
   }

   @Override
   public Set<String> getDeploymentNames()
   {
      return Collections.singleton(deploymentName);
   }

   @Override
   public Collection<ProfileDeployment> getDeployments()
   {
      return Collections.singleton(classPathDeployment);
   }

   @Override
   public ProfileKey getKey()
   {
      return key;
   }

   @Override
   public long getLastModified()
   {
      return this.lastModified;
   }

   @Override
   public boolean hasDeployment(String name)
   {
      return false;
   }

   @Override
   public boolean isMutable()
   {
      return false;
   }
   
   protected List<VirtualFile> determineRoots() throws IOException, URISyntaxException
   {
      List<VirtualFile> classpath = new ArrayList<VirtualFile>();
      for(ClassPathItem item : items)
      {
         URI uri = new URI(item.getPath());
         
         String codebase = item.getPath();
         String archives = item.getArchives();

         log.debug("Processing classpath: codebase=" + codebase + " archives=" + archives);
         VirtualFile codebaseFile = VFS.getChild(uri);
         if (codebaseFile == null)
            throw new RuntimeException("Cannot use classpath without a root: " + uri);

         if (archives == null)
         {
            classpath.add(codebaseFile);
         }
         else
         {
            ClassPathFilter filter = new ClassPathFilter(archives);
            List<VirtualFile> archiveFiles = codebaseFile.getChildren(filter);
            classpath.addAll(archiveFiles);
         }
      }
      return classpath;
   }

   static class ClassPathFilter implements VirtualFileFilter
   {
      /** The patterns */
      private final Set<String> patterns;
      
      /** Whether there is the accept all wildcard */
      private final boolean allowAll;

      public ClassPathFilter(String patternsString)
      {
         if (patternsString == null)
            throw new IllegalArgumentException("Null patternsString");

         StringTokenizer tokens = new StringTokenizer (patternsString, ",");
         patterns = new HashSet<String>(tokens.countTokens());
         for (int i=0; tokens.hasMoreTokens (); ++i)
         {
            String token = tokens.nextToken();
            patterns.add(token.trim());
         }
         allowAll = patterns.contains("*");
      }
      
      public boolean accepts(VirtualFile file)
      {
         if (allowAll)
            return true;
         return patterns.contains(file.getName());
      }
   }
   

}

