package org.testng.gigaspaces;

import java.io.IOException;
import java.util.Properties;
import java.util.UUID;

import net.jini.core.lease.Lease;

import org.testng.remote.adapter.IMasterAdapter;
import org.testng.remote.adapter.RemoteResultListener;
import org.testng.xml.XmlSuite;

import com.j_spaces.core.IJSpace;
import com.j_spaces.core.client.SpaceFinder;

import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger;

/**
 * Provides a Master adapter based on Gigaspaces space implementation.
 * 
 * @author	Guy Korland
 * 
 * @date 	April 20, 2007
 */
public class SpaceMasterAdapter implements IMasterAdapter
{
	public static final String SPACE_URL = "gigaspaces.url";
	
	private IJSpace					m_space;
	
	final private ConcurrentHashMap m_listeners = new ConcurrentHashMap(); 
	
	final private SuitesCounter m_counter = new SuitesCounter();
	private ResultCollector m_collector;
	
	/*
	 * @see org.testng.remote.adapter.IWorkerApadter#init(java.util.Properties)
	 */
	public void init(Properties prop) throws Exception
	{
		String url = prop.getProperty(SPACE_URL, "/./TestNGSpace?groups=TestNG");
		m_space = (IJSpace) SpaceFinder.find(url);
		m_collector = new ResultCollector();
		m_collector.start();
	}

	/*
	 * @see org.testng.remote.adapter.IMasterAdapter#awaitTermination(long)
	 */
	public void awaitTermination(long timeout) throws InterruptedException
	{
		try
		{
			m_counter.waitFor( timeout);
		}
		finally
		{
			m_collector.shutDown();
		}
	}

	/*
	 * @see org.testng.remote.adapter.IMasterAdapter#runSuitesRemotely(org.testng.xml.XmlSuite, org.testng.remote.adapter.RemoteResultListener)
	 */
	public void runSuitesRemotely(XmlSuite suite, RemoteResultListener listener)
	throws IOException
	{
		UUID testID = UUID.randomUUID();
		SuiteEntry suiteEntry	= new SuiteEntry( suite, testID);
		try
		{
			m_listeners.putIfAbsent(testID, listener);
			m_space.write(suiteEntry, null, Lease.FOREVER);
			m_counter.inc();
		}
		catch (Exception e)
		{
			m_listeners.remove( testID);
			e.printStackTrace();
		}
	}

	private class ResultCollector extends Thread
	{
		final private ResultEntry resultTemplate = new ResultEntry();
		volatile private boolean m_shutdown = false;  

		public ResultCollector()
		{
			this.setDaemon(true);
			this.setName("ResultCollector");
		}

		public void run()
		{
			while( !m_shutdown)
			{
				try
				{
					ResultEntry rs = (ResultEntry) m_space.take(resultTemplate, null,
					                                           Long.MAX_VALUE);
					RemoteResultListener listener = (RemoteResultListener)m_listeners.remove(rs.getTestID());
					listener.onResult(rs.getSuite());
					m_counter.dec();
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
			}
		}
		
		public void shutDown()
		{
			m_shutdown = true;
			this.interrupt();
		}
	}


	private static class SuitesCounter
	{
		private AtomicInteger m_count = new AtomicInteger(1); 
		
		public void dec()
		{
			int c = m_count.decrementAndGet();
			if( c == 0)
			{
				synchronized (this)
				{
					notifyAll();
				}
			}
		}
		
		public void inc()
		{
			m_count.incrementAndGet();
		}
		
		public void waitFor( long timeout) throws InterruptedException
		{
			m_count.decrementAndGet();
			synchronized (this)
			{
				wait( timeout);	
			}
			
		}
	}
}
