/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.aries.blueprint.parser;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.aries.blueprint.ComponentDefinitionRegistry;
import org.apache.aries.blueprint.ComponentNameAlreadyInUseException;
import org.apache.aries.blueprint.Interceptor;
import org.apache.aries.blueprint.reflect.PassThroughMetadataImpl;
import org.osgi.service.blueprint.reflect.ComponentMetadata;
import org.osgi.service.blueprint.reflect.Target;

/**
 * ComponentDefinitionRegistry implementation.
 *
 * This implementation uses concurrent lists and maps to store components and converters metadata
 * to allow its use by concurrent threads. 
 *
 * @version $Rev$, $Date$
 */
public class ComponentDefinitionRegistryImpl implements ComponentDefinitionRegistry {

    private final Map<String, ComponentMetadata> components;
    private final List<Target> typeConverters;
    private final Map<ComponentMetadata, List<Interceptor>> interceptors;

    public ComponentDefinitionRegistryImpl() {
        // Use a linked hash map to keep the declaration order 
        components = Collections.synchronizedMap(new LinkedHashMap<String, ComponentMetadata>());
        typeConverters = new CopyOnWriteArrayList<Target>();
        interceptors = Collections.synchronizedMap(new HashMap<ComponentMetadata, List<Interceptor>>());
    }

    public void reset() {
        components.clear();
        typeConverters.clear();
        interceptors.clear();
    }

    public boolean containsComponentDefinition(String name) {
        return components.containsKey(name);
    }

    public ComponentMetadata getComponentDefinition(String name) {
        return components.get(name);
    }

    public Set<String> getComponentDefinitionNames() {
        return Collections.unmodifiableSet(components.keySet());
    }

    public void registerComponentDefinition(ComponentMetadata component) {
        String id = component.getId();
        if (id == null) {
            // TODO: should we generate a unique name?
            throw new IllegalArgumentException("Component must have a valid id");
        }
        if (id.startsWith("blueprint") && !(component instanceof PassThroughMetadataImpl)) {
            // TODO: log a warning
        }
        // TODO: perform other validation: scope, class/runtimeClass/factoryMethod, etc...
        if (components.containsKey(id)) {
            throw new ComponentNameAlreadyInUseException(id);
        }
        components.put(id, component);
    }

    public void removeComponentDefinition(String name) {
        ComponentMetadata removed = components.remove(name);
        if(removed!=null){
            interceptors.remove(removed);
        }
    }

    public void registerTypeConverter(Target component) {
        typeConverters.add(component);
        if (component instanceof ComponentMetadata) {
            registerComponentDefinition((ComponentMetadata) component);
        }
    }

    public List<Target> getTypeConverters() {
        return typeConverters;
    }

    public void registerInterceptorWithComponent(ComponentMetadata component, Interceptor interceptor) {
        if(interceptor!=null){
            List<Interceptor> componentInterceptorList = interceptors.get(component);
            if(componentInterceptorList==null){
                componentInterceptorList = new ArrayList<Interceptor>();
                interceptors.put(component, componentInterceptorList);
            }
            if(!componentInterceptorList.contains(interceptor)){
                componentInterceptorList.add(interceptor);
                Collections.sort(componentInterceptorList, new Comparator<Interceptor>(){
                    public int compare(Interceptor object1, Interceptor object2) {
                        //invert the order so higher ranks are sorted 1st
                        return object2.getRank() - object1.getRank();
                    }
                });
            }
        }
    }

    public List<Interceptor> getInterceptors(ComponentMetadata component) {
        List<Interceptor> result = interceptors.get(component);
        return (result == null) ? Collections.<Interceptor>emptyList() : result;
    }
    
}
