/*******************************************************************************
 * Copyright (c) 2010, 2015 Sonatype, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Stuart McCulloch (Sonatype, Inc.) - initial API and implementation
 *******************************************************************************/
package org.eclipse.sisu.plexus;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.repository.ComponentDescriptor;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.context.Context;
import org.eclipse.sisu.bean.BeanManager;
import org.eclipse.sisu.wire.EntryListAdapter;
import org.eclipse.sisu.wire.EntryMapAdapter;

import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;

/**
 * Delegating {@link PlexusContainer} wrapper that doesn't require an actual container instance.
 */
@Singleton
@SuppressWarnings( { "unchecked", "rawtypes" } )
final class PseudoPlexusContainer
    implements PlexusContainer
{
    // ----------------------------------------------------------------------
    // Implementation fields
    // ----------------------------------------------------------------------

    final PlexusBeanLocator locator;

    final BeanManager manager;

    final Context context;

    // ----------------------------------------------------------------------
    // Constructors
    // ----------------------------------------------------------------------

    @Inject
    PseudoPlexusContainer( final PlexusBeanLocator locator, final BeanManager manager, final Context context )
    {
        this.locator = locator;
        this.manager = manager;
        this.context = context;
    }

    // ----------------------------------------------------------------------
    // Context methods
    // ----------------------------------------------------------------------

    public Context getContext()
    {
        return context;
    }

    // ----------------------------------------------------------------------
    // Lookup methods
    // ----------------------------------------------------------------------

    public Object lookup( final String role )
        throws ComponentLookupException
    {
        return lookup( role, "" );
    }

    public Object lookup( final String role, final String hint )
        throws ComponentLookupException
    {
        return lookup( null, role, hint );
    }

    public <T> T lookup( final Class<T> role )
        throws ComponentLookupException
    {
        return lookup( role, "" );
    }

    public <T> T lookup( final Class<T> role, final String hint )
        throws ComponentLookupException
    {
        return lookup( role, null, hint );
    }

    public <T> T lookup( final Class<T> type, final String role, final String hint )
        throws ComponentLookupException
    {
        try
        {
            return locate( role, type, hint ).iterator().next().getValue();
        }
        catch ( final RuntimeException e )
        {
            throw new ComponentLookupException( e, null != role ? role : type.getName(), hint );
        }
    }

    public List<Object> lookupList( final String role )
        throws ComponentLookupException
    {
        return new EntryListAdapter<Object>( locate( role, null ) );
    }

    public <T> List<T> lookupList( final Class<T> role )
        throws ComponentLookupException
    {
        return new EntryListAdapter<T>( locate( null, role ) );
    }

    public Map<String, Object> lookupMap( final String role )
        throws ComponentLookupException
    {
        return new EntryMapAdapter<String, Object>( locate( role, null ) );
    }

    public <T> Map<String, T> lookupMap( final Class<T> role )
        throws ComponentLookupException
    {
        return new EntryMapAdapter<String, T>( locate( null, role ) );
    }

    // ----------------------------------------------------------------------
    // Query methods
    // ----------------------------------------------------------------------

    public boolean hasComponent( final String role )
    {
        return hasComponent( role, "" );
    }

    public boolean hasComponent( final String role, final String hint )
    {
        return hasComponent( null, role, hint );
    }

    public boolean hasComponent( final Class role )
    {
        return hasComponent( role, "" );
    }

    public boolean hasComponent( final Class role, final String hint )
    {
        return hasComponent( role, null, hint );
    }

    public boolean hasComponent( final Class type, final String role, final String hint )
    {
        return hasPlexusBeans( locate( role, type, hint ) );
    }

    // ----------------------------------------------------------------------
    // Component descriptor methods
    // ----------------------------------------------------------------------

    public void addComponent( final Object component, final String role )
    {
        throw new UnsupportedOperationException();
    }

    public <T> void addComponent( final T component, final Class<?> role, final String hint )
    {
        throw new UnsupportedOperationException();
    }

    public <T> void addComponentDescriptor( final ComponentDescriptor<T> descriptor )
    {
        throw new UnsupportedOperationException();
    }

    public ComponentDescriptor<?> getComponentDescriptor( final String role, final String hint )
    {
        throw new UnsupportedOperationException();
    }

    public <T> ComponentDescriptor<T> getComponentDescriptor( final Class<T> type, final String role, final String hint )
    {
        throw new UnsupportedOperationException();
    }

    public List getComponentDescriptorList( final String role )
    {
        throw new UnsupportedOperationException();
    }

    public <T> List<ComponentDescriptor<T>> getComponentDescriptorList( final Class<T> type, final String role )
    {
        throw new UnsupportedOperationException();
    }

    public Map getComponentDescriptorMap( final String role )
    {
        throw new UnsupportedOperationException();
    }

    public <T> Map<String, ComponentDescriptor<T>> getComponentDescriptorMap( final Class<T> type, final String role )
    {
        throw new UnsupportedOperationException();
    }

    public List<ComponentDescriptor<?>> discoverComponents( final ClassRealm realm )
    {
        throw new UnsupportedOperationException();
    }

    // ----------------------------------------------------------------------
    // Class realm methods
    // ----------------------------------------------------------------------

    public ClassRealm getContainerRealm()
    {
        throw new UnsupportedOperationException();
    }

    public ClassRealm setLookupRealm( final ClassRealm realm )
    {
        throw new UnsupportedOperationException();
    }

    public ClassRealm getLookupRealm()
    {
        throw new UnsupportedOperationException();
    }

    public ClassRealm createChildRealm( final String id )
    {
        throw new UnsupportedOperationException();
    }

    // ----------------------------------------------------------------------
    // Shutdown methods
    // ----------------------------------------------------------------------

    public void release( final Object component )
    {
        manager.unmanage( component );
    }

    public void releaseAll( final Map<String, ?> components )
    {
        for ( final Object o : components.values() )
        {
            release( o );
        }
    }

    public void releaseAll( final List<?> components )
    {
        for ( final Object o : components )
        {
            release( o );
        }
    }

    public void dispose()
    {
        manager.unmanage();
    }

    // ----------------------------------------------------------------------
    // Implementation methods
    // ----------------------------------------------------------------------

    private <T> Iterable<PlexusBean<T>> locate( final String role, final Class<T> type, final String... hints )
    {
        final String[] canonicalHints = Hints.canonicalHints( hints );
        if ( null == role || null != type && type.getName().equals( role ) )
        {
            return locator.locate( TypeLiteral.get( type ), canonicalHints );
        }
        try
        {
            final Class clazz = Thread.currentThread().getContextClassLoader().loadClass( role );
            final Iterable beans = locator.locate( TypeLiteral.get( clazz ), canonicalHints );
            if ( hasPlexusBeans( beans ) )
            {
                return beans;
            }
        }
        catch ( final Exception e )
        {
            // drop through...
        }
        catch ( final LinkageError e )
        {
            // drop through...
        }
        return Collections.EMPTY_SET;
    }

    private static <T> boolean hasPlexusBeans( final Iterable<PlexusBean<T>> beans )
    {
        final Iterator<PlexusBean<T>> i = beans.iterator();
        return i.hasNext() && i.next().getImplementationClass() != null;
    }
}
