/**
 * 
 */
package anoncard;

import static org.junit.Assert.*;

import java.util.Arrays;

import mixconfig.tools.dataretention.smartcard.ApduConstants;
import mixconfig.tools.dataretention.smartcard.commands.DataTransmissionCommand;
import mixconfig.tools.dataretention.smartcard.commands.StartDataTransmissionCommand;
import javacard.framework.ISOException;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import anoncard.DataReceiveChannel;
import anoncard.util.Helpers;
import anoncard.util.ISOExceptionException;

/**
 * @author christoph
 * 
 */
public class DataReceiveChannelTest {

	private DataReceiveChannel dataReceiveChannel;
	private byte instruction = 0x23;
	private byte numberOfExpectedPackages = 0x02;

	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception {
		dataReceiveChannel = new DataReceiveChannel();
	}

	/**
	 * @throws java.lang.Exception
	 */
	@After
	public void tearDown() throws Exception {
	}

	@Test(expected = ISOExceptionException.class)
	public void testAspect() {
		ISOException.throwIt((short) 1);
	}

	/**
	 * Test method for
	 * {@link anoncard.DataReceiveChannel#receive(byte[], byte, byte)}
	 * .
	 */
	@Test
	public void testReceiveBenignTransmission() {
		byte[] data = new byte[] { 0, 1, 2, 3 };
		dataReceiveChannel.open(instruction, (byte) 2);
		// send data
		for (int i = 0; i < 2; i++) {
			dataReceiveChannel.receive(data, (byte) (2 * i), (byte) 2);
		}
		// check data
		if (dataReceiveChannel.isTransmissionComplete()) {
			assertArrayEquals(data, mixconfig.tools.dataretention.smartcard.Helpers.copyOfRange(dataReceiveChannel.getData(), 0, dataReceiveChannel.length()));
		} else {
			fail("DataTransmission must be complete.");
		}

	}

	/**
	 * Test method for
	 * {@link anoncard.DataReceiveChannel#receive(byte[], byte, byte)}
	 * .
	 */
	@Test
	public void testReceive_OnClosedChannel() {
		try {
			dataReceiveChannel.receive(new byte[] { 0, 1, 2, 3 }, (byte) 0, (byte) 1);
			fail("Expect ISOExceptionException: EXCEPTION_DATA_TRANSMISSION_CHANNEL_IS_CLOSED ("
					+ Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_CHANNEL_IS_CLOSED) + ")");
		} catch (ISOExceptionException e) {
			if (!e.getErrorCode().equals(Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_CHANNEL_IS_CLOSED))) {
				fail("Wrong Exception, expected: " + Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_CHANNEL_IS_CLOSED) + "\nwas: " + e.getErrorCode());
			}
		}
	}

	/**
	 * Test method for
	 * {@link anoncard.DataReceiveChannel#receive(byte[], byte, byte)}
	 * .
	 */
	@Test
	public void testReceive_IllegalLength_StartDataTransmissionCommand() {
		dataReceiveChannel.open((byte) 0x00, (byte) 0x01);
		try {
			dataReceiveChannel.receive(new byte[] { 0, 1, 2, 3 }, (byte) 0, (byte) (StartDataTransmissionCommand.bytesPerStartDataTransmisionCommand + 1));
			fail("Expect ISOExceptionException: EXCEPTION_DATA_TRANSMISSION_ILLEGAL_LENGTH_ARGUMENT ("
					+ Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_ILLEGAL_LENGTH_ARGUMENT) + ")");
		} catch (ISOExceptionException e) {
			if (!e.getErrorCode().equals(Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_ILLEGAL_LENGTH_ARGUMENT))) {
				fail("Wrong Exception, expected: " + Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_ILLEGAL_LENGTH_ARGUMENT) + "\nwas: " + e.getErrorCode());
			}
		}
	}

	/**
	 * Test method for
	 * {@link anoncard.DataReceiveChannel#receive(byte[], byte, byte)}
	 * .
	 */
	@Test
	public void testReceive_IllegalLength_DataTranmissionCommand() {
		dataReceiveChannel.open((byte) 0x00, (byte) 0x02);
		dataReceiveChannel.receive(new byte[] {}, (byte) 0, (byte) 0);
		try {
			dataReceiveChannel.receive(new byte[] { 0, 1, 2, 3 }, (byte) 0, (byte) (DataTransmissionCommand.bytesPerDataTransmisionCommand + 1));
			fail("Expect ISOExceptionException: EXCEPTION_DATA_TRANSMISSION_ILLEGAL_LENGTH_ARGUMENT ("
					+ Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_ILLEGAL_LENGTH_ARGUMENT) + ")");
		} catch (ISOExceptionException e) {
			if (!e.getErrorCode().equals(Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_ILLEGAL_LENGTH_ARGUMENT))) {
				fail("Wrong Exception, expected: " + Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_ILLEGAL_LENGTH_ARGUMENT) + "\nwas: " + e.getErrorCode());
			}
		}
	}

	/**
	 * Test method for
	 * {@link anoncard.DataReceiveChannel#receive(byte[], byte, byte)}
	 * .
	 */
	@Test
	public void testReceiveOverflow1() {
		dataReceiveChannel.open((byte) 0x00, (byte) 0x00);
		try {
			dataReceiveChannel.receive(new byte[] { 0, 1, 2, 3 }, (byte) 0, (byte) 1);
			fail("Expect ISOExceptionException: EXCEPTION_DATA_TRANSMISSION_CHANNEL_DATA_OVERFLOW ("
					+ Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_CHANNEL_DATA_OVERFLOW) + ")");
		} catch (ISOExceptionException e) {
			if (!e.getErrorCode().equals(Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_CHANNEL_DATA_OVERFLOW))) {
				fail("Wrong Exception, expected: " + Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_CHANNEL_DATA_OVERFLOW) + "\nwas: " + e.getErrorCode());
			}
		}
	}

	/**
	 * Test method for
	 * {@link anoncard.DataReceiveChannel#receive(byte[], byte, byte)}
	 * .
	 */
	@Test
	public void testReceiveOverflow2() {
		dataReceiveChannel.open(instruction, (byte) 0x01);
		dataReceiveChannel.receive(new byte[] { 0, 1, 2, 3 }, (byte) 0, (byte) 1);

		try {
			dataReceiveChannel.receive(new byte[] { 0, 1, 2, 3 }, (byte) 0, (byte) 1);
			fail("Expect ISOExceptionException: EXCEPTION_DATA_TRANSMISSION_CHANNEL_DATA_OVERFLOW ("
					+ Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_CHANNEL_DATA_OVERFLOW) + ")");
		} catch (ISOExceptionException e) {
			if (!e.getErrorCode().equals(Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_CHANNEL_DATA_OVERFLOW))) {
				fail("Wrong Exception, expected: " + Helpers.shortToHexString(ApduConstants.EXCEPTION_DATA_TRANSMISSION_CHANNEL_DATA_OVERFLOW) + "\nwas: " + e.getErrorCode());
			}
		}
	}

	/**
	 * Test method for
	 * {@link anoncard.DataReceiveChannel#isOpen()},
	 * {@link anoncard.DataReceiveChannel#isOpen()} and
	 * {@link anoncard.DataReceiveChannel#close()} (since they
	 * are very closely related).
	 */
	@Test
	public void testOpen_IsOpen_Close() {
		assertEquals(false, dataReceiveChannel.isOpen());
		assertEquals(ApduConstants.INSTRUCTION_NOP, dataReceiveChannel.getInstruction());
		dataReceiveChannel.open(instruction, numberOfExpectedPackages);
		assertEquals(true, dataReceiveChannel.isOpen());
		assertEquals(instruction, dataReceiveChannel.getInstruction());
		dataReceiveChannel.close();
		assertEquals(false, dataReceiveChannel.isOpen());
		assertEquals(ApduConstants.INSTRUCTION_NOP, dataReceiveChannel.getInstruction());
	}

	/**
	 * Test method for
	 * {@link anoncard.DataReceiveChannel#getData()},
	 * {@link anoncard.DataReceiveChannel#length()} and
	 * {@link DataReceiveChannel#isTransmissionComplete()} (since they are
	 * very closely related).
	 */
	@Test
	public void testGetData_Length_TransmissionComplete1() {
		byte[] data = new byte[] { 0, 1, 2, 3 };

		assertEquals(null, dataReceiveChannel.getData());
		assertEquals(0, dataReceiveChannel.length());
		assertEquals(false, dataReceiveChannel.isTransmissionComplete());

		dataReceiveChannel.open(instruction, numberOfExpectedPackages);
		assertEquals(null, dataReceiveChannel.getData());
		assertEquals(0, dataReceiveChannel.length());
		assertEquals(false, dataReceiveChannel.isTransmissionComplete());

		dataReceiveChannel.receive(data, (byte) 0, (byte) 2);
		assertEquals(null, dataReceiveChannel.getData());
		assertEquals(2, dataReceiveChannel.length());
		assertEquals(false, dataReceiveChannel.isTransmissionComplete());

		dataReceiveChannel.receive(data, (byte) 2, (byte) 2);
		assertArrayEquals(data, Arrays.copyOfRange(dataReceiveChannel.getData(), 0, data.length));
		assertEquals(4, dataReceiveChannel.length());
		assertEquals(true, dataReceiveChannel.isTransmissionComplete());

	}

	/**
	 * Test method for
	 * {@link anoncard.DataReceiveChannel#getData()},
	 * {@link anoncard.DataReceiveChannel#length()} and
	 * {@link DataReceiveChannel#isTransmissionComplete()} (since they are
	 * very closely related).
	 */
	@Test
	public void testGetData_Length_TransmissionComplete2() {
		dataReceiveChannel.open(instruction, (byte) 0x01);
		dataReceiveChannel.receive(new byte[] {}, (byte) 0, (byte) 0);
		assertEquals(0, dataReceiveChannel.length());
		assertEquals(true, dataReceiveChannel.isTransmissionComplete());
		assertArrayEquals(new byte[] {}, Arrays.copyOfRange(dataReceiveChannel.getData(), 0, 0));

	}
}
