/*
 * Copyright (C) 2003-2009 eXo Platform SAS.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see<http://www.gnu.org/licenses/>.
 */
package org.exoplatform.commons.testing;

import java.util.EnumMap;
import java.util.HashSet;
import java.util.Set;

import org.exoplatform.component.test.ConfigurationUnit;
import org.exoplatform.component.test.ConfiguredBy;
import org.exoplatform.component.test.ContainerScope;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.container.PortalContainer;
import org.junit.Before;

/**
 * A base test class that allows to load an exo container with a selected set of
 * components. It is aimed at running lighter test cases than the
 * StandaloneContainer. <u>Example usage</u> :
 * 
 * <pre>
 *  <code>
 *    {@literal @}ConfiguredBy({{@literal @}ConfigurationUnit(scope = ContainerScope.ROOT, path = "conf/custom.xml")
 *               ,@ConfigurationUnit(scope = ContainerScope.PORTAL, path = "conf/some.xml")
 *               ,@ConfigurationUnit(scope = ContainerScope.PORTAL, path = "conf/other.xml")})
 *  </code>
 * </pre>
 * 
 * @author <a href="mailto:patrice.lamarque@exoplatform.com">Patrice
 *         Lamarque</a>
 * @version $Revision$
 */
public abstract class AbstractExoContainerTestCase{

  private static boolean isInit = false;
  
  @Before
  public final void startContainer(){    
    if (!isInit){          
      beforeContainerStart();
      initExoContainer();
      afterContainerStart();
      isInit = true;
    }        
  }   

  protected void afterContainerStart() {
  }

  protected void beforeContainerStart() {
  }

  private void initExoContainer() {
    Set<String> rootConfigPaths = new HashSet<String>();
    rootConfigPaths.add("conf/test-root-configuration.xml");

    Set<String> portalConfigPaths = new HashSet<String>();
    portalConfigPaths.add("conf/test-portal-configuration.xml");

    portalConfigPaths.add("conf/" + getClass().getSimpleName() + ".xml");

    EnumMap<ContainerScope, Set<String>> configs = new EnumMap<ContainerScope, Set<String>>(ContainerScope.class);
    configs.put(ContainerScope.ROOT, rootConfigPaths);
    configs.put(ContainerScope.PORTAL, portalConfigPaths);

    ConfiguredBy cfBy = getClass().getAnnotation(ConfiguredBy.class);

    if (cfBy != null) {
      for (ConfigurationUnit src : cfBy.value()) {
        configs.get(src.scope()).add(src.path());
      }
    }

    ContainerBuilder builder = new ContainerBuilder();
    Set<String> rootConfs = configs.get(ContainerScope.ROOT);
    for (String rootConf : rootConfs) {
      builder.withRoot(rootConf);
    }
    Set<String> portalConfs = configs.get(ContainerScope.PORTAL);
    for (String portalConf : portalConfs) {
      builder.withPortal(portalConf);
    }

    builder.build();
  }

  /**
   * Register a component to the container
   * 
   * @param key component key
   * @param instance component instance to register
   */
  protected <T, I extends T> void registerComponent(Class<T> key, I instance) {
    ExoContainerContext.getCurrentContainer().registerComponentInstance(key, instance);
  }

  /**
   * Get a component from current container
   * 
   * @param <T> type of component (key)
   * @param <U> type of component implementation (type)
   * @param key class of the registered component
   * @return
   */
  @SuppressWarnings("unchecked")
  protected <T, U extends T> U getComponent(Class<T> key) {
    ExoContainer container = PortalContainer.getInstance();
    return (U) container.getComponentInstanceOfType(key);
  }

  /**
   * Replace a component implementation by registering it against the current
   * container
   * 
   * @param key component key
   * @param instance component instance to register
   */
  protected <T, I extends T> void replaceComponent(Class<T> key, I instance) {
    ExoContainerContext.getCurrentContainer().unregisterComponent(key);
    registerComponent(key, instance);
  }

}
