package com.hammurapi.common.concurrent.tests;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;

import javax.transaction.Status;
import javax.transaction.Transaction;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import org.junit.Test;

import com.hammurapi.common.SimpleMutableContext;
import com.hammurapi.common.concurrent.Logger;
import com.hammurapi.common.concurrent.LoggerWrapper;
import com.hammurapi.common.concurrent.PropertySet;
import com.hammurapi.common.concurrent.transactions.Command;
import com.hammurapi.common.concurrent.transactions.ExecutionContext;
import com.hammurapi.common.concurrent.transactions.StandaloneTransactionalProcessor;

public class TransactionsTest {

	static class TestXaResource implements XAResource {

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((name == null) ? 0 : name.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			TestXaResource other = (TestXaResource) obj;
			if (name == null) {
				if (other.name != null)
					return false;
			} else if (!name.equals(other.name))
				return false;
			return true;
		}

		private String name;

		public TestXaResource(String name) {
			this.name = name;
		}

		@Override
		public void commit(Xid arg0, boolean arg1) throws XAException {
			System.out.println(Thread.currentThread()+" [" + name + "] Commit " + arg0 + ", " + arg1);

		}

		@Override
		public void end(Xid arg0, int arg1) throws XAException {
			System.out.println(Thread.currentThread()+" [" + name + "] End " + arg0);

		}

		@Override
		public void forget(Xid arg0) throws XAException {
			System.out.println(Thread.currentThread()+" [" + name + "] Forget " + arg0);

		}

		@Override
		public int getTransactionTimeout() throws XAException {
			// TODO Auto-generated method stub
			return 0;
		}

		@Override
		public boolean isSameRM(XAResource arg0) throws XAException {
			return equals(arg0);
		}

		@Override
		public int prepare(Xid arg0) throws XAException {
			System.out.println(Thread.currentThread()+" [" + name + "] Prepare " + arg0);
			return XA_OK;
		}

		@Override
		public Xid[] recover(int arg0) throws XAException {
			System.out.println(Thread.currentThread()+" [" + name + "] Recover " + arg0);
			return null;
		}

		@Override
		public void rollback(Xid arg0) throws XAException {
			System.out.println(Thread.currentThread()+" [" + name + "] Rollback " + arg0);

		}

		@Override
		public boolean setTransactionTimeout(int arg0) throws XAException {
			// TODO Auto-generated method stub
			return false;
		}

		@Override
		public void start(Xid arg0, int arg1) throws XAException {
			System.out.println(Thread.currentThread()+" [" + name + "] Start " + arg0);
		}

	}

	@Test
	public void testTransactionalProcessor() throws Exception {
		StandaloneTransactionalProcessor processor = new StandaloneTransactionalProcessor(Executors.defaultThreadFactory(), 5);

		Command<String, Object> command = new Command<String, Object>() {

			@Override
			public Object execute(ExecutionContext<String> context,	PropertySet<String> propertySet, Object... args) throws Exception {
				System.out.println(Thread.currentThread().getName());

				TestXaResource testXaResource = new TestXaResource("Test resource");
				context.registerXAResource(testXaResource);
				
				Transaction transaction = context.getTransaction();
				transaction.enlistResource(testXaResource);

				Runnable undoTask = new Runnable() {

					@Override
					public void run() {
						System.out.println("Undone!");
					}

				};
				context.addUndo(undoTask);
				
				Command<String, Object> c2 = new Command<String, Object>() {
					
					@Override
					public String toString() {
						return "Nice command!";
					}
					
					@Override
					public Object execute(ExecutionContext<String> context,	PropertySet<String> propertySet, Object... args) throws Exception {
						System.out.println(Thread.currentThread().getName());
						TestXaResource testXaResource = new TestXaResource("Test resource 2");
						context.registerXAResource(testXaResource);
						
						Transaction transaction = context.getTransaction();
						transaction.enlistResource(testXaResource);
						
						context.getLogger().info("Life is good!");
						
						Runnable undoTask = new Runnable() {

							@Override
							public void run() {
								System.out.println("Undone 2!");
							}

						};
						
						System.out.println(transaction.getStatus());
						
//						context.getExecutorService().shutdown();
						
						context.lookup(ExecutorService.class).execute(new Runnable() {

							@Override
							public void run() {
//								throw new RuntimeException("Poluchi!");								
							}
							
						});
						
						context.addUndo(undoTask);
						
//						context.getTransactionManager().setRollbackOnly();
						Thread.sleep(100);
						
						System.out.println(transaction.getStatus()==Status.STATUS_MARKED_ROLLBACK);
						return "OK";
					}
				};
				
				System.out.println(context.execute(c2, context, propertySet, args).get());

				Thread.sleep(200);
				return "Super!";
			}

		};

		PropertySet<String> ps = processor.createPropertySet(null);
		ps.set("z", "ZZZZ");
		
		SimpleMutableContext ctx = new SimpleMutableContext();
		java.util.logging.Logger juLogger = java.util.logging.Logger.getLogger("TEST");
		Handler handler = new ConsoleHandler();
		handler.setLevel(Level.INFO);
//		juLogger.addHandler(handler);
		juLogger.setLevel(Level.FINEST);
		ctx.register(Logger.class, new LoggerWrapper(juLogger));

	    Object result = processor.process(command, ctx, ps);
	    
	    System.out.println(result);

		processor.shutdown();
	}

}
