package org.sophie2.main.func.servers;

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

import javax.swing.JList;

import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.sophie2.base.connectivity.Account;
import org.sophie2.base.connectivity.Connector;
import org.sophie2.base.connectivity.facade.FacadeR4;
import org.sophie2.base.connectivity.facade.Response;
import org.sophie2.base.dialogs.BaseDialogsModule;
import org.sophie2.base.dialogs.TestingDialogManager;
import org.sophie2.base.layout.BaseLayoutModule;
import org.sophie2.base.model.resources.r4.LocationPrefix;
import org.sophie2.base.model.resources.r4.ResourceRef;
import org.sophie2.base.model.resources.r4.keys.Key;
import org.sophie2.base.model.resources.r4.keys.SimpleKey;
import org.sophie2.base.model.resources.r4.resources.ResourceR4;
import org.sophie2.base.skins.BaseSkinsModule;
import org.sophie2.base.visual.BaseVisualModule;
import org.sophie2.core.logging.SophieLog;
import org.sophie2.core.modularity.FakeModuleRegistry;
import org.sophie2.core.modularity.SimpleSophieExtension;
import org.sophie2.core.modularity.SophieExtension;
import org.sophie2.core.modularity.SophieExtensionPoint;
import org.sophie2.core.modularity.SophieModule;
import org.sophie2.core.mvc.CoreMvcModule;
import org.sophie2.core.prolib.interfaces.ListProp;
import org.sophie2.core.testing.IntegrationTestBase;
import org.sophie2.main.app.commons.MainAppModule;
import org.sophie2.main.dialogs.input.MessageDialogInput;
import org.sophie2.main.func.servers.view.ConnectionsPalette;
import org.sophie2.main.func.servers.view.ServersTab;
import org.sophie2.main.func.servers.view.ConnectionsPalette.ConnectionItem;
import org.sophie2.main.layout.mydoggy.LayoutMyDoggyModule;

/**
 * Tests the {@link ConnectionsPalette} and thus the {@link AccountManager}'s correct work.
 * @author tsachev, kyli
 */
public class ConnectionsPaletteTest extends IntegrationTestBase {
	public static interface MockerySupport {
		Mockery getMockery();
	}
	
	public static class MockResource extends ResourceR4 {
		public static final Key<Integer> KEY_MOCK_DUMMY = new SimpleKey<Integer>(
				"mock-dummy", Integer.class, null); 
	}
	

	public static class MockFacadeConnectorModule extends SophieModule {

		@Override
		protected void defineExtensionPoints(List<SophieExtensionPoint<?>> res) {
			// No extension points
		}

		@Override
		protected void defineExtensions(List<SophieExtension<?>> res) {

			class MockeryConnector implements Connector, MockerySupport {
				private final Mockery mockContext = new Mockery();

				@Override
				public Mockery getMockery() {
					return mockContext;
				}

				private FacadeR4 facade = mockContext.mock(FacadeR4.class);

				@Override
				public boolean supports(String serverUrl) {
					assert serverUrl != null;
					return serverUrl.startsWith("mock");
				}

				@Override
				public FacadeR4 getFacade(String serverUrl) {
					return this.facade;
				}
			}
			res.add(new SimpleSophieExtension<Connector>(Connector.class,
					new MockeryConnector()));

		}
	}


	@SuppressWarnings("unchecked")
	@Before
	@Override
	protected void setUp() throws Exception {
		super.setUp();
		TestingDialogManager.beginTesting();
		FakeModuleRegistry.start(CoreMvcModule.class, BaseDialogsModule.class,
				MainAppModule.class, MockFacadeConnectorModule.class,
				MainFuncServersModule.class, BaseLayoutModule.class,
				BaseVisualModule.class, LayoutMyDoggyModule.class,
				BaseSkinsModule.class);
	}

	@After
	protected void tearDown() throws Exception {
		FakeModuleRegistry.stop();
		AccountManager.instance = null;
		ServerResourceLocator.instance = null;
		TestingDialogManager.endTesting();
		super.tearDown();
	}

	@Test
	public void testConnect() {
		SophieLog.debug("testConnect");
		ConnectionsPalette connectionsPalette = BaseVisualModule.get()
				.findProvider(ConnectionsPalette.class, ServersTab.class, null)
				.create(ConnectionsPalette.class);
		assertTrue(connectionsPalette.items().get().isEmpty());
		AccountManager accountManager = AccountManager.get();

		final String serverUrl = "mock://s2s";

		Account testAccount = accountManager.createNewAccount(serverUrl,
				"test", "test");
		ConnectionItem connectionItem = connectionsPalette.items().get(0);

		Connector connector = MainFuncServersModule.get().getConnectorPoint()
				.extensions().get(0).getObject();
		assert connector instanceof MockerySupport;
		final FacadeR4 mockFacade = connector.getFacade(serverUrl);
		Mockery mockery = ((MockerySupport) connector).getMockery();
		mockery.checking(new Expectations() {
			{
				oneOf(mockFacade).openSession();
				will(returnValue(Response.createSuccessfulResponse("123")));

			}
		});

		assertTrue(testAccount.equals(connectionItem.account().get()));
		assertFalse(testAccount.autoConnect().get());
		assertFalse(connectionItem.connected().get());
		accountManager.connect(testAccount);

		assertTrue(accountManager.isConnected(testAccount));

	}

	@Test
	public void testConnectionFailed() {
		SophieLog.debug("testConnectionFailed");
		ConnectionsPalette connectionsPalette = BaseVisualModule.get()
				.findProvider(ConnectionsPalette.class, ServersTab.class, null)
				.create(ConnectionsPalette.class);
		assertTrue(connectionsPalette.items().get().isEmpty());
		AccountManager accountManager = AccountManager.get();

		final String serverUrl = "mock://s2s";

		Account testAccount = accountManager.createNewAccount(serverUrl,
				"test", "test");
		ListProp<ConnectionItem> items = connectionsPalette.items();

		ConnectionItem connectionItem = items.get(items.size() - 1);

		Connector connector = MainFuncServersModule.get().getConnectorPoint()
				.extensions().get(0).getObject();
		assert connector instanceof MockerySupport;
		assertTrue(testAccount.equals(connectionItem.account().get()));
		assertFalse(testAccount.autoConnect().get());
		assertFalse(connectionItem.connected().get());

		final FacadeR4 mockFacade = connector.getFacade(serverUrl);
		Mockery mockery = ((MockerySupport) connector).getMockery();
		mockery.checking(new Expectations() {
			{
				oneOf(mockFacade).openSession();
				will(returnValue(new Response<Void>(null, 1, "Timeout")));

			}
		});
		accountManager.connect(testAccount);
		assertFalse(accountManager.isConnected(testAccount));
	}

	@Test
	public void testConnectionsList() {
		SophieLog.debug("testConnectionsList");
		ConnectionsPalette connectionsPalette = BaseVisualModule.get()
				.findProvider(ConnectionsPalette.class, ServersTab.class, null)
				.create(ConnectionsPalette.class);

		assertTrue(connectionsPalette.items().get().isEmpty());

		AccountManager accountManager = AccountManager.get();

		final String serverUrl = "mock://s2s";

		Account testAccount = accountManager.createNewAccount(serverUrl,
				"test", "test");
		ListProp<ConnectionItem> items = connectionsPalette.items();
		JList jList = connectionsPalette.list().get();
		int index = items.size() - 1;
		jList.setSelectedIndex(index);

		Connector connector = MainFuncServersModule.get().getConnectorPoint()
				.extensions().get(0).getObject();
		assert connector instanceof MockerySupport;
		final FacadeR4 mockFacade = connector.getFacade(serverUrl);
		Mockery mockery = ((MockerySupport) connector).getMockery();
		mockery.checking(new Expectations() {
			{
				oneOf(mockFacade).openSession();
				will(returnValue(Response.createSuccessfulResponse("1234321")));
				oneOf(mockFacade).closeSession();
				will(returnValue(new Response<Void>(null, 0, "")));

			}
		});

		TestingDialogManager.get().expect(
				new TestingDialogManager.BaseExpected(MessageDialogInput.class,
						null));

		ConnectionItem connectionItem = connectionsPalette.items().get(index);

		assertFalse(accountManager.isConnected(testAccount));
		assertFalse(connectionItem.connected().get());

		SophieLog.debug(connectionItem.render());

		connectionsPalette.toggleButton().get().actionPerformed(null);

		assertTrue(accountManager.isConnected(testAccount));
		assertTrue(connectionItem.connected().get());
		SophieLog.debug(connectionItem.render());
	}

	@Test
	public void testOpenResource() {
		ConnectionsPalette connectionsPalette = BaseVisualModule.get()
				.findProvider(ConnectionsPalette.class, ServersTab.class, null)
				.create(ConnectionsPalette.class);

		final String serverUrl = "mock://s2s";
		Connector connector = MainFuncServersModule.get().getConnectorPoint()
				.extensions().get(0).getObject();
		final FacadeR4 mockFacade = connector.getFacade(serverUrl);
		Mockery mockery = ((MockerySupport) connector).getMockery();
		final ResourceRef mockResourceRef = ResourceRef
				.make(LocationPrefix.NULL + "s2s/mockResource");
		
		
		final Map<Key<?>, Object> modelWrites = new HashMap<Key<?>, Object>();
		modelWrites.put(MockResource.KEY_MOCK_DUMMY, 24);
		
		
		
		
		
		
		mockery.checking(new Expectations() {
			private final String mockSessionId = "mockSessionId";
			
			{
				oneOf(mockFacade).openSession();
				will(returnValue(Response.createSuccessfulResponse(
						mockSessionId, "Session opened")));
				oneOf(mockFacade).closeSession();
				will(returnValue(new Response<Void>(null, 0, "Session closed")));
				oneOf(mockFacade).getResourceModel(mockSessionId,
						mockResourceRef, MockResource.KEY_ROOT);
				will(returnValue(Response.createSuccessfulResponse(modelWrites)));
			}
		});

		assertTrue(connectionsPalette.items().get().isEmpty());
		Account account = AccountManager.get().createNewAccount(serverUrl,
				null, null);
		AccountManager.get().connect(account);
		
		
//		// call the facade's getResourceModel
//		// create an empty model, apply changes to it.
//		Map<Key<?>, Object> changes = AccountManager.get().connections().get(0).
//			getResourceModel(mockResourceRef, MockResource.KEY_ROOT);
//		
//		ResourceModel model = new ResourceModel();
//		ResourceModel newModel = model.modify(changes);
//		System.out.println(newModel);
//		newModel.debugPrint();
	}
	
	
	
}

