package cz.cuni.amis.pogamut.base.communication.translator;

import java.util.LinkedList;
import java.util.Queue;

import com.google.inject.Inject;

import cz.cuni.amis.pogamut.base.communication.exceptions.CommunicationException;
import cz.cuni.amis.pogamut.base.communication.exceptions.TranslatorException;
import cz.cuni.amis.pogamut.base.communication.messages.InfoMessage;
import cz.cuni.amis.pogamut.base.communication.parser.IWorldMessageParser;
import cz.cuni.amis.pogamut.base.factory.guice.AgentScoped;
import cz.cuni.amis.pogamut.base.utils.logging.AgentLogger;
import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;

/**
 * Abstract class for translators between world messages (InfoObjects) and IWorldEvents. 
 * It implements IWorldEventOutput interface meaning
 * it can be given to the mediator as the source for IWorldEvents. It is constructed
 * with parser that should provide parsed messages (translated text messages from the world
 * to the java object wrappers) + IWorldMessageHandler that is given those parsed messages
 * to produce IWorldEvents. It should process those messages into IWorldEvents and return them.
 * 
 * @author Jimmy
 */
@AgentScoped
public class WorldEventOutput implements IWorldEventOutput {
	
	/**
	 * Parser of the world messages, may be used to get additional messages.
	 */
	protected IWorldMessageParser parser = null;
	
	protected AgentLogger agentLogger = null;
	
	protected LogCategory log = null;
	
	protected IWorldMessageTranslator handler = null;
	
	@Inject
	public WorldEventOutput(IWorldMessageParser parser, IWorldMessageTranslator messageHandler, AgentLogger logger) {
		agentLogger = logger;
		log = agentLogger.in();				
		this.parser = parser;
		if (this.parser == null) throw new IllegalArgumentException("parser can't be null");
		this.handler = messageHandler;
		if (this.handler == null) throw new IllegalArgumentException("handler can't be null");
	}
	
	@Override
	public void start() throws CommunicationException {
		parser.start();
	}

	@Override
	public void stop() {
		parser.stop();		
	}
	
	@Override
	public void kill() {
		stop();
	}
	
	/**
	 * Method for translating messages into events.
	 * <p><p>
	 * You may be sure that message won't be null.
	 * <p><p>
	 * You may return null or array of zero-length - in that case the method will be called again with next parsed message.
	 * 
	 * @param message
	 * @return
	 * @throws WorldMessageHandlerException
	 */
	protected IWorldChangeEvent[] processMessage(InfoMessage message) throws TranslatorException {
		return handler.processMessage(message);		
	}
	
	/**
	 * Method processMessage() may produce more events per message - if it does so, we will store those events
	 * in this queue. If queue is not empty - getEvent() will return events from this queue.
	 */
	private Queue<IWorldChangeEvent> worldEventQueue = new LinkedList<IWorldChangeEvent>();

	@Override
	public synchronized IWorldChangeEvent getEvent() throws CommunicationException {
		if (worldEventQueue.size() > 0) return worldEventQueue.poll();
		InfoMessage message;
		IWorldChangeEvent[] worldEvents = null;
		while (worldEvents == null || worldEvents.length == 0) {
			message = null;
			while (message == null) {
				message = parser.parse();
			}
			worldEvents = processMessage(message);			
		}
		for (int i = 1; i < worldEvents.length; ++i) {
			worldEventQueue.add(worldEvents[i]);
		}
		return worldEvents[0];
	}

}
