package edu.teco.dnd.network.tcp.tests;

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

import java.util.HashMap;
import java.util.Map;

import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import edu.teco.dnd.network.ChannelFutureNotifier;

public class MockChannel {
	@Mock
	private Channel channel;

	@Mock
	private ChannelFutureNotifier channelFutureNotifier;

	@Mock
	private ChannelFuture closeFuture;

	private ChannelFutureListenerHandler closeListenerHandler;
	
	private Map<AttributeKey<?>, Attribute<?>> attributes = new HashMap<AttributeKey<?>, Attribute<?>>();

	@SuppressWarnings("unchecked")
	public MockChannel() {
		MockitoAnnotations.initMocks(this);
		when(channelFutureNotifier.channel()).thenReturn(channel);
		when(channel.close()).thenReturn(closeFuture);
		when(channel.closeFuture()).thenReturn(closeFuture);
		when(closeFuture.channel()).thenReturn(channel);
		closeListenerHandler = new ChannelFutureListenerHandler(closeFuture);
		when(channel.attr(any(AttributeKey.class))).then(new AttributeAnswer());
	}

	public Channel getChannel() {
		return channel;
	}

	public ChannelFutureNotifier getChannelFutureNotifier() {
		return channelFutureNotifier;
	}

	public void close() throws Exception {
		closeFuture();
		notifyCloseFutureListeners();
	}

	private void closeFuture() {
		when(closeFuture.isSuccess()).thenReturn(true);
		when(closeFuture.isDone()).thenReturn(true);
	}

	private void notifyCloseFutureListeners() throws Exception {
		for (final GenericFutureListener<Future<Void>> listener : closeListenerHandler.getListeners()) {
			listener.operationComplete(closeFuture);
		}
	}

	public <T> void setAttribute(final AttributeKey<T> key, T value) {
		when(attr(key).get()).thenReturn(value);
	}

	@SuppressWarnings("unchecked")
	public <T> Attribute<T> attr(final AttributeKey<T> key) {
		if (!attributes.containsKey(key)) {
			attributes.put(key, mock(Attribute.class));
		}
		return (Attribute<T>) attributes.get(key);
	}

	private class AttributeAnswer implements Answer<Attribute<?>> {
		@Override
		public Attribute<?> answer(final InvocationOnMock invocation) throws Throwable {
			return attr((AttributeKey<?>) invocation.getArguments()[0]);
		}
	}
}

@@ -74,16 +74,60 @@
 rs.any;%0A
+import static org.mockito.Mockito.doAnswer;%0A
 import s
@@ -2309,25 +2309,31 @@
 )) %7B%0A%09%09%09
-a
+final A
 ttribute
 s.put(ke
@@ -2328,18 +2328,27 @@
 bute
-s.put(key,
+%3CT%3E mockAttribute =
  moc
@@ -2365,16 +2365,134 @@
 e.class)
+;%0A%09%09%09doAnswer(new SetAttributeAnswer%3CT%3E(key)).when(mockAttribute).set((T) any());%0A%09%09%09attributes.put(key, mockAttribute
 );%0A%09%09%7D%0A%09
@@ -2699,16 +2699,16 @@
 wable %7B%0A
-
 %09%09%09retur
@@ -2769,11 +2769,394 @@
 ;%0A%09%09%7D%0A%09%7D
+%0A%0A%09private class SetAttributeAnswer%3CT%3E implements Answer%3CVoid%3E %7B%0A%09%09private final AttributeKey%3CT%3E key;%0A%0A%09%09private SetAttributeAnswer(final AttributeKey%3CT%3E key) %7B%0A%09%09%09this.key = key;%0A%09%09%7D%0A%0A%09%09@SuppressWarnings(%22unchecked%22)%0A%09%09@Override%0A%09%09public Void answer(final InvocationOnMock invocation) throws Throwable %7B%0A%09%09%09setAttribute(key, (T) invocation.getArguments()%5B0%5D);%0A%09%09%09return null;%0A%09%09%7D%0A%09%7D
 %0A%7D%0A

Fix MockChannel attributes