package com.oy.shared.scample.actor;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

public class MessageBus {
	private static MessageBus current;
	
	private MessageBusWorker worker;
	private Set actors = new HashSet();
	
	private Object lock = new Object();
	private List targets = new LinkedList();
 
	class MessageBusWorker extends Thread {
		private boolean terminated = false;
		
		public void run(){
			while (true){
				synchronized(lock){
					if (terminated) {
						break;
					}
					 
					if (targets.size() != 0){
						MessageTarget mt = (MessageTarget) targets.remove(0);
						mt.target.react(mt.msg);
						
						continue;
					} 
 
					try {
						lock.wait();
					} catch (InterruptedException ie){
						// respond only to 'terminated' and nothing else
					}
				}
			}
		}
	}

	class MessageTarget {
		Message msg;
		Actor target;
	}	
	 
	public void start(){
		worker = new MessageBusWorker();
		worker.start();

		current = this;
		System.out.println("Message bus start()");
	} 
	 
	public void stopWhenActorsFinish(){
		while(true){ 
			synchronized(lock){
				if (actors.size() == 0) {
					worker.terminated = true;
					lock.notifyAll();
					break;
				}
			}
		} 
		 
		try { 
			worker.join();
		} catch (InterruptedException ie){
			throw new RuntimeException(ie);
		}
		   
		current = null;
		System.out.println("Message bus stop()");
	}	
	
	public void addActor(Actor actor){
		synchronized(lock){
			if (actors.contains(actor)){
				throw new RuntimeException("Actor already added");
			}
			actors.add(actor);
		
			lock.notifyAll();
		}
	}
	 
	public void removeActor(Actor actor){
		synchronized(lock){
			if (!actors.contains(actor)){
				throw new RuntimeException("Actor not yet added");
			}
			actors.remove(actor);
			
			lock.notifyAll();
		}
	}
	
	public void postMessage(Message msg, Actor target){
		synchronized(lock){
			MessageTarget mt = new MessageTarget();
			 
			mt.msg = msg;
			mt.target = target;
	 
			targets.add(mt);
			 
			lock.notifyAll();
		}
	}
	
	public Message sendMessage(Message msg, Actor target){
		throw new RuntimeException("Not implemented");
	}
	
	public static MessageBus getCurrent(){
		return current; 
	}
}
