/*
 * JGrapes Event Driven Framework
 * Copyright (C) 2016  Michael N. Lipp
 * 
 * This program is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU 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.jgrapes.core.internal;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jgrapes.core.Component;
import org.jgrapes.core.Channel;
import org.jgrapes.core.AttachedComponent;
import org.jgrapes.core.Event;
import org.jgrapes.core.EventPipeline;
import org.jgrapes.core.HandlingErrorPrinter;
import org.jgrapes.core.annotation.Handler;
import org.jgrapes.core.events.HandlingError;

/**
 * This class represents the component tree. It holds all properties that 
 * are common to all nodes of a component tree (the {@link ComponentVertex}s.
 * 
 * @author Michael N. Lipp
 */
class ComponentTree {

	private static final Logger handlerTracking 
		= Logger.getLogger(AttachedComponent.class.getPackage().getName() 
				+ ".handlerTracking");	
	
	private ComponentVertex root;
	private Map<EventChannelsTuple,HandlerList> handlerCache
		= new HashMap<EventChannelsTuple,HandlerList>();
	private InternalEventPipeline eventPipeline;
	private static HandlingErrorPrinter fallbackErrorHandler 
		= new HandlingErrorPrinter(); 
	public final static ComponentVertex DUMMY_HANDLER 
		= new Component(Channel.SELF) {
			@Handler(channels={Channel.class})
			public void noop(Event<?> event) {
			}
	};

	/**
	 * Creates a new tree for the given node or sub tree.
	 * 
	 * @param root the root node of the new tree
	 */
	ComponentTree(ComponentVertex root) {
		super();
		this.root = root;
	}

	ComponentVertex getRoot() {
		return root;
	}
	
	boolean isStarted() {
		return !(eventPipeline instanceof EventBuffer);
	}

	void setEventPipeline(InternalEventPipeline pipeline) {
		eventPipeline = pipeline;
	}
	
	InternalEventPipeline getEventPipeline() {
		return eventPipeline;
	}
	
	/**
	 * Forward to the thread's event manager.
	 * 
	 * @param event
	 * @param channels
	 */
	public void fire(Event<?> event, Channel[] channels) {
		eventPipeline.add(event, channels);
	}

	/**
	 * Merge all events stored with <code>source</code> with our own.
	 * This is invoked if a component (or component tree) is attached 
	 * to another tree (that uses this component common).
	 * 
	 * @param source
	 */
	void mergeEvents(ComponentTree source) {
		eventPipeline.merge(source.eventPipeline);
	}
	
	/**
	 * Send the event to all matching handlers.
	 * 
	 * @param event the event
	 * @param channels the channels the event is sent to
	 */
	void dispatch(EventPipeline pipeline, 
			EventBase<?> event, Channel[] channels) {
		HandlerList handlers = getEventHandlers(event, channels);
		handlers.process(pipeline, event);
	}
	
	private HandlerList getEventHandlers
		(EventBase<?> event, Channel[] channels) {
		EventChannelsTuple key = new EventChannelsTuple(event, channels);
		HandlerList hdlrs = handlerCache.get(key);
		if (hdlrs != null) {
			return hdlrs;
		}
		hdlrs = new HandlerList();
		root.collectHandlers(hdlrs, event, channels);
		// Make sure that errors are reported.
		if (hdlrs.isEmpty()) {
			if (event instanceof HandlingError) {
				((ComponentVertex)fallbackErrorHandler)
					.collectHandlers(hdlrs, event, channels);
			} else {
				if (handlerTracking.isLoggable(Level.FINER)) {
					DUMMY_HANDLER.collectHandlers(hdlrs, event, channels);
				}
			}
		}
		Collections.sort(hdlrs);
		handlerCache.put(key, hdlrs);
		return hdlrs;
	}
	
	void clearHandlerCache() {
		handlerCache.clear();
	}

}

@@ -1867,79 +1867,299 @@
 r();
- %0A%09public final static ComponentVertex DUMMY_HANDLER %0A%09%09= new Component
+%0A%09/* This could simply be declared as an anonymous class. But then%0A%09 * %22Find bugs%22 complains about the noop() not being callable, because it%0A%09 * doesn't consider that it is called by reflection. */%0A%09private static class DummyComponent extends Component %7B%0A%09%09public DummyComponent() %7B%0A%09%09%09super
 (Cha
@@ -2168,20 +2168,22 @@
 el.SELF)
- %7B
+;
 %0A%09
+%09%7D%0A
 %09%09@Handl
@@ -2213,17 +2213,16 @@
 ass%7D)%0A%09%09
-%09
 public v
@@ -2254,14 +2254,88 @@
 %7B%0A%09%09
+%7D%0A
 %09%7D%0A%09
-%7D;
+public final static ComponentVertex DUMMY_HANDLER = new DummyComponent(); 
 %0A%0A%09/

Make "Find bugs" happy.