/*
 * Copyright (C) 2009 eXo Platform SAS.
 *
 * 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.exoplatform.services.jcr.core;

import org.exoplatform.commons.utils.SecurityHelper;
import org.exoplatform.services.jcr.core.security.JCRRuntimePermissions;
import org.exoplatform.services.jcr.impl.WorkspaceContainer;
import org.exoplatform.services.jcr.impl.WorkspaceResumer;
import org.exoplatform.services.jcr.impl.backup.ResumeException;
import org.exoplatform.services.jcr.impl.backup.SuspendException;
import org.exoplatform.services.jcr.impl.backup.Suspendable;

import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.jcr.RepositoryException;

/**
 * Created by The eXo Platform SAS .<br> An entry point to the implementation, used for extending
 * functionality
 * 
 * @author Gennady Azarenkov
 * @version $Id:$
 */

public final class WorkspaceContainerFacade
{

   private final String workspaceName;

   private final WorkspaceContainer container;

   private AtomicBoolean workspaceResumerInit = new AtomicBoolean();

   private WorkspaceResumer workspaceResumer;

   /**
    * @param workspaceName
    * @param container
    */
   public WorkspaceContainerFacade(String workspaceName, WorkspaceContainer container)
   {
      this.workspaceName = workspaceName;
      this.container = container;
   }

   private WorkspaceResumer getWorkspaceResumer()
   {
      if (workspaceResumerInit.get())
         return workspaceResumer;
      // The WorkspaceResumer can be null so to prevent being affected by the auto registration
      // we disable it as we know that it is not needed in that case
      this.workspaceResumer = container.getComponentInstanceOfType(WorkspaceResumer.class, false);
      workspaceResumerInit.set(true);
      return workspaceResumer;
   }

   /**
    * @return workspace name
    */
   public final String getWorkspaceName()
   {
      return this.workspaceName;
   }

   /**
    * Returns list of components of specific type.
    * 
    * @param componentType
    *          component type
    * @return List of Object
    */
   public <T> List<T> getComponentInstancesOfType(Class<T> componentType)
   {
      return container.getComponentInstancesOfType(componentType);
   }

   /**
    * @param key
    *          - an internal key of internal component
    * @return the component
    */
   public Object getComponent(Object key)
   {
      if (key instanceof Class)
         return container.getComponentInstanceOfType((Class<?>)key);
      else
         return container.getComponentInstance(key);
   }

   public void addComponent(Object component)
   {
      if (component instanceof Class)
         container.registerComponentImplementation((Class<?>)component);
      else
         container.registerComponentInstance(component);
   }

   public void addComponent(Object key, Object component)
   {
      container.registerComponentInstance(key, component);
   }

   /**
    * Returns current workspace state.
    */
   public int getState()
   {
      boolean hasSuspendedComponents = false;
      boolean hasResumedComponents = false;
      List<Suspendable> suspendableComponents = getComponentInstancesOfType(Suspendable.class);
      for (Suspendable component : suspendableComponents)
      {
         if (component.isSuspended())
         {
            hasSuspendedComponents = true;
         }
         else
         {
            hasResumedComponents = true;
         }
      }

      if (hasSuspendedComponents && !hasResumedComponents)
      {
         return ManageableRepository.SUSPENDED;
      }
      else if (!hasSuspendedComponents)
      {
         return ManageableRepository.ONLINE;
      }
      else
      {
         return ManageableRepository.UNDEFINED;
      }
   }

   /**
    * Set new workspace state.
    * 
    * @param state
    * @throws RepositoryException
    */
   public void setState(final int state) throws RepositoryException
   {
      // Need privileges to manage repository.
      SecurityManager security = System.getSecurityManager();
      if (security != null)
      {
         security.checkPermission(JCRRuntimePermissions.MANAGE_REPOSITORY_PERMISSION);
      }

      try
      {
         SecurityHelper.doPrivilegedExceptionAction(new PrivilegedExceptionAction<Void>()
         {
            public Void run() throws RepositoryException
            {
               switch (state)
               {
                  case ManageableRepository.ONLINE :
                     resume();
                     break;
                  case ManageableRepository.OFFLINE :
                     suspend();
                     break;
                  case ManageableRepository.SUSPENDED :
                     suspend();
                     break;
                  default :
                     return null;
               }
               return null;
            }
         });
      }
      catch (PrivilegedActionException e)
      {
         Throwable cause = e.getCause();
         if (cause instanceof RepositoryException)
         {
            throw new RepositoryException(cause);
         }
         else
         {
            throw new RuntimeException(cause);
         }
      }
   }

   /**
    * Suspend all components in workspace.
    * 
    * @throws RepositoryException
    */
   private void suspend() throws RepositoryException
   {
      WorkspaceResumer workspaceResumer = getWorkspaceResumer();
      if (workspaceResumer != null)
      {
         workspaceResumer.onSuspend();
      }

      List<Suspendable> components = getComponentInstancesOfType(Suspendable.class);

      Comparator<Suspendable> c = new Comparator<Suspendable>()
      {
         public int compare(Suspendable s1, Suspendable s2)
         {
            return s2.getPriority() - s1.getPriority();
         };
      };
      Collections.sort(components, c);

      for (Suspendable component : components)
      {
         try
         {
            if (!component.isSuspended())
            {
               component.suspend();
            }
         }
         catch (SuspendException e)
         {
            throw new RepositoryException("Can't suspend component", e);
         }
      }
   }

   /**
    * Set all components online.
    * 
    * @throws RepositoryException
    */
   private void resume() throws RepositoryException
   {
      WorkspaceResumer workspaceResumer = getWorkspaceResumer();
      if (workspaceResumer != null)
      {
         workspaceResumer.onResume();
      }

      // components should be resumed in reverse order
      List<Suspendable> components = getComponentInstancesOfType(Suspendable.class);
      Comparator<Suspendable> c = new Comparator<Suspendable>()
      {
         public int compare(Suspendable s1, Suspendable s2)
         {
            return s1.getPriority() - s2.getPriority();
         };
      };
      Collections.sort(components, c);

      for (Suspendable component : components)
      {
         try
         {
            if (component.isSuspended())
            {
               component.resume();
            }
         }
         catch (ResumeException e)
         {
            throw new RepositoryException("Can't set component online", e);
         }
      }
   }
}
