/*******************************************************************************
 * 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.space;

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.inject.Provider;
import javax.inject.Qualifier;

import com.google.inject.Key;
import com.google.inject.TypeLiteral;

/**
 * Binding {@link Key} for implementations that act as "wild-cards", meaning they match against any assignable type.
 * <p>
 * Since the wild-card type is {@link Object} and the associated qualifier may not be unique between implementations,
 * the qualifier is saved and replaced with a unique (per-implementation) pseudo-qualifier. The original qualifier is
 * available by casting the pseudo-qualifier to {@link Provider} and calling {@code get()}.
 */
final class WildcardKey
{
    // ----------------------------------------------------------------------
    // Constants
    // ----------------------------------------------------------------------

    private static final TypeLiteral<Object> OBJECT_TYPE_LITERAL = TypeLiteral.get( Object.class );

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

    private WildcardKey()
    {
        // static utility class, not allowed to create instances
    }

    // ----------------------------------------------------------------------
    // Utility methods
    // ----------------------------------------------------------------------

    /**
     * @return Wildcard key for the given implementation type and qualifier
     */
    public static Key<Object> get( final Class<?> type, final Annotation qualifier )
    {
        return Key.get( OBJECT_TYPE_LITERAL, new QualifiedImpl( type, qualifier ) );
    }

    // ----------------------------------------------------------------------
    // Implementation types
    // ----------------------------------------------------------------------

    /**
     * {@link Qualifier} that captures a qualified implementation type.
     */
    @Qualifier
    @Retention( RetentionPolicy.RUNTIME )
    private static @interface Qualified
    {
        Class<?> value();
    }

    /**
     * Pseudo-{@link Annotation} that can wrap any implementation type as a {@link Qualifier}.
     */
    private static final class QualifiedImpl
        implements Qualified, Provider<Annotation>
    {
        // ----------------------------------------------------------------------
        // Implementation fields
        // ----------------------------------------------------------------------

        private final Class<?> value;

        private final Annotation qualifier;

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

        QualifiedImpl( final Class<?> value, final Annotation qualifier )
        {
            this.value = value;
            this.qualifier = qualifier;
        }

        // ----------------------------------------------------------------------
        // Public methods
        // ----------------------------------------------------------------------

        public Class<?> value()
        {
            return value;
        }

        public Annotation get()
        {
            return qualifier;
        }

        public Class<? extends Annotation> annotationType()
        {
            return Qualified.class;
        }

        @Override
        public int hashCode()
        {
            return value.hashCode(); // no need to follow strict annotation spec
        }

        @Override
        public boolean equals( final Object rhs )
        {
            if ( this == rhs )
            {
                return true;
            }
            if ( rhs instanceof QualifiedImpl )
            {
                return value == ( (QualifiedImpl) rhs ).value;
            }
            return false;
        }

        @Override
        public String toString()
        {
            return "*"; // let people know this is a "wild-card" qualifier
        }
    }
}
