/*
 * Licensed 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.karaf.cellar.core.event;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * Event handler service registry.
 */
public class EventHandlerServiceRegistry<E extends Event> implements EventHandlerRegistry<E> {

	protected static class EventHandlerHolder {
		
		public static int DEFAULT_PRIORITY = 1;
		
		EventHandler handler;
		int priority = 1;
		
		public EventHandlerHolder(EventHandler handler) {
			this.handler = handler;
		}

		public EventHandlerHolder(EventHandler handler, int priority) {
			this.handler = handler;
			this.priority = priority;
		}
		
	}
	
    private Map<Class,List<EventHandlerHolder>> eventHandlerMap = new ConcurrentHashMap<Class,List<EventHandlerHolder>>();
    
    private Map<EventHandler, EventHandlerHolder> eventHandlerHolderMap = new ConcurrentHashMap<EventHandler, EventHandlerHolder>();

    protected EventHandler<E> getFirst(E event){
    	Class clazz = event.getClass();
    	List<EventHandlerHolder> list = eventHandlerMap.get(clazz);
    	if (list == null || list.isEmpty())
    		return null;
    	return list.get(0).handler;
    }
    
    /**
     * Return the appropriate cluster {@code EventHandler} found inside the cluster {@code HandlerRegistry}.
     *
     * @param event the cluster event to handle.
     * @return the corresponding cluster event handler to use.
     */
    @Override
    public EventHandler<E> getHandler(E event) {
        if (event != null) {
//            Class clazz = event.getClass();
//            return eventHandlerMap.get(clazz)==null?null:;
        	return getFirst(event);
        }
        return null;
    }

    protected List<EventHandlerHolder> getCreateList(Class clazz){
    	if (!eventHandlerMap.containsKey(clazz))
    		eventHandlerMap.put(clazz, new ArrayList<>());
    	return eventHandlerMap.get(clazz);
    }
    
    public void bind(EventHandler handler) {
    	bind(handler, EventHandlerHolder.DEFAULT_PRIORITY);
    }
    
    public void bind(EventHandler handler, int priority) {
        if(handler != null && handler.getType() != null) {
        	List<EventHandlerHolder> list = getCreateList(handler.getType());
        	EventHandlerHolder holder = new EventHandlerHolder(handler, priority); 
        	list.add(holder);
        	Collections.sort(list, new Comparator<EventHandlerHolder>() {

				@Override
				public int compare(EventHandlerHolder o1,
						EventHandlerHolder o2) {
					return Integer.compare(o1.priority, o2.priority);
				}
			});
        	eventHandlerHolderMap.put(handler, holder);
            // eventHandlerMap.put(handler.getType(),handler);
        }
    }

    public void unbind(EventHandler handler) {
    	if(handler != null && handler.getType() != null) {
    		EventHandlerHolder holder = eventHandlerHolderMap.get(handler);
    		if (holder == null)
    			return;
    		if (!eventHandlerMap.containsKey(handler.getType()))
    			return;
    		
            eventHandlerMap.get(handler.getType()).remove(holder);
            eventHandlerHolderMap.remove(handler);
        }
    }

	@Override
	public List<EventHandler<E>> getHandlers(E event) {
		if (!eventHandlerMap.containsKey(event.getClass()))
			return Collections.emptyList();
		return eventHandlerMap.get(event.getClass()).stream().map(holder -> (EventHandler<E>)holder.handler).collect(Collectors.toList());
	}

}
