package com.refactorerl.ui.communication;

import static org.junit.Assert.fail;

import java.util.Random;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangString;
import com.ericsson.otp.erlang.OtpErlangTuple;
import com.refactorerl.ui.common.Common;
import com.refactorerl.ui.common.MockEventDispatcher;
import com.refactorerl.ui.common.OtpErlangHelper;
import com.refactorerl.ui.communication.exceptions.CommunicationException;
import com.refactorerl.ui.communication.exceptions.ConnectionException;
import com.refactorerl.ui.communication.process.ReferlProcess;

public class ReferlProxyTest {

	private static ReferlProcess process;
	private static String aliveName;
	private static MockEventDispatcher eventDispatcher;
	private ReferlProxy proxy;	

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		System.out.println("# Setup before class #");
		
		// JVM argument is something like:
		// -Drefactorerl_base="/home/daniel/opt/refactorerl-0.9.14.09"
		String baseDirPath = System.getProperty("refactorerl_base");
		System.out.println("Starting process on " + baseDirPath);
		aliveName = Common.createRandomName();
		process = new ReferlProcess(baseDirPath, aliveName,
				ReferlProcess.DatabaseType.NIF,
				null, Common.createLogHandlers());

		if (process.start() == false) {
			throw new Exception("RefactorErl process was not started.");
		}

		System.out.println(("RefactorErl process has started."));

		eventDispatcher = new MockEventDispatcher();
		eventDispatcher.start();

		System.out.println("# Setup before class complete #");
		
		Runtime.getRuntime().addShutdownHook(new Thread(){
			@Override
			public void run() {
				process.stop();
			}
		});
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
		System.out.println("# Teardown after class #");
		
		if (process.stop()) {
			System.out.println(("RefactorErl process has been closed."));
		} else {
			System.err
					.println(("RefactorErl process was not closed. Please close it manually. Alivename is " + aliveName));
		}
		
		eventDispatcher.stop();
		System.out.println("# Teardown after class complete #");
	}

	@Before
	public void setUp() throws Exception {
		System.out.println("## Setup ##");
		proxy = new ReferlProxy(eventDispatcher, Common.createRandomName(),
				process.getFullAddress(), Common.createLogHandlers());
		if (!proxy.start()) {
			throw new Exception("RefactorErl proxy was not started.");
		}
		
		Runtime.getRuntime().addShutdownHook(new Thread(){
			@Override
			public void run() {
				proxy.stop();
			}
		});

		System.out.println("## Setup complete ##");
	}

	@After
	public void tearDown() throws Exception {
		System.out.println("## Teardown ##");
		proxy.stop();
		System.out.println("## Teardown complete ##");
	}

	@Test
	public void testStart() {
		proxy.stop();
		if (!proxy.start()) {
			fail("RefactorErl proxy was not started.");
		}
	}

	@Test
	public void testSendOtpErlangObjectIReferlProgressMonitor() {

		
		try {
			System.out.println("### Sending correct request");
			OtpErlangTuple request = OtpErlangHelper.createTuple(new OtpErlangAtom("system_info"));
			OtpErlangObject o = proxy.send(request, null);
			Assert.assertEquals(OtpErlangHelper.nth(MessageParseHelper.getBody(o), 0), new OtpErlangAtom("ok"));
			
			System.out.println("### Sending non-existent request");
			request = OtpErlangHelper.createTuple(new OtpErlangAtom("loremipsumdolorsitamet"));
			o = proxy.send(request, null);
			Assert.assertEquals(OtpErlangHelper.nth(MessageParseHelper.getBody(o), 0), new OtpErlangAtom("error"));
				
			System.out.println("### Sending ill formed request");
			request = new OtpErlangTuple(new OtpErlangObject[0]);  
			boolean exceptionThrown = false;
			try{
			o = proxy.send(request, null);
			} catch (CommunicationException e){ 
				 exceptionThrown = true;
			}
			Assert.assertTrue(exceptionThrown);

			System.out.println("### Sending null");
			exceptionThrown = false;
			try{
			o = proxy.send(null, null);
			} catch (CommunicationException e){
				 exceptionThrown = true;
			}
			Assert.assertTrue(exceptionThrown);
			
		} catch (ConnectionException | CommunicationException  e) {
			e.printStackTrace();
			fail(e.toString());
		}
	}

	@Test
	public void testSendRPC() {
		try {
			System.out.println("### Sending correct request");
			OtpErlangObject o = proxy.sendRPC("referl_misc", "data_dir", new OtpErlangObject[0]);
			Assert.assertTrue(o instanceof OtpErlangString);
			
			System.out.println("### Sending non-existent request");
			boolean exceptionThrown = false;
			try{
			o = proxy.sendRPC("loremipsumdolorsitamet", "loremipsumdolorsitamet", new OtpErlangObject[0]); 
			} catch (CommunicationException e){ 
				 exceptionThrown = true;
			}
			Assert.assertTrue(exceptionThrown);
			
		} catch (CommunicationException | ConnectionException e) {
			e.printStackTrace();
			fail(e.toString());
		}

	}

}
