package geniusweb.issuevalue;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import org.junit.Test;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class DiscreteValueSetTest {

	private static final DiscreteValue A = new DiscreteValue("a");
	private static final DiscreteValue A1 = new DiscreteValue("a");
	private static final DiscreteValue B = new DiscreteValue("b");
	private final static List<DiscreteValue> values = Arrays.asList(A, B);
	private final static Collection<DiscreteValue> valuesReverse = Arrays
			.asList(B, A);

	private static final DiscreteValueSet set = new DiscreteValueSet(values);
	private static final DiscreteValueSet setReverse = new DiscreteValueSet(
			valuesReverse);
	private static final String asString = "{\"values\":[\"a\",\"b\"]}";
	// private static final String asString = "{\"DiscreteValueSet
	// \":[\"a\",\"b\"]}";
	private static final String asString1 = "[\"a\",\"b\"]";

	// SPECIAL. note that the "numbers" have no leading "=" and therefore are
	// parsed as discrete values.
	private static final String discretenumbers = "{\"values\":[\"0\",\"1\"]}";
	private static final ObjectMapper jackson = new ObjectMapper();

	@Test
	public void testSerialize() throws JsonProcessingException {

		String jsonstr = jackson.writeValueAsString(set);
		//System.out.println(json);
		assertEquals(asString, jsonstr);
	}

	@Test
	public void testDeserialize()
			throws JsonParseException, JsonMappingException, IOException {
		DiscreteValueSet p = jackson.readValue(asString,
				DiscreteValueSet.class);
		//System.out.println(p);
		assertEquals(set, p);
	}

	@Test
	public void testDeserializeNumberValues()
			throws JsonParseException, JsonMappingException, IOException {
		DiscreteValueSet p = jackson.readValue(discretenumbers,
				DiscreteValueSet.class);
		//System.out.println(p);
		assertEquals("0", p.get(BigInteger.ZERO).getValue()); // not the number
																// but string
		assertEquals("1", p.get(BigInteger.ONE).getValue());
	}

	@Test
	public void testEquality() {
		assertEquals(set, setReverse);
	}

	@Test
	public void testHashCodeInsensitiveToOrder() {
		assertEquals(set.hashCode(), setReverse.hashCode());
	}

	@Test
	public void testSize() {
		assertEquals(new BigInteger("2"), set.size());
	}

	@Test
	public void testGet() {
		assertEquals(A, set.get(0));
		assertEquals(B, set.get(1));
		assertEquals(A, set.get(BigInteger.ZERO));
		assertEquals(B, set.get(BigInteger.ONE));
	}

	@Test
	public void getValuesTest() {
		assertEquals(values, set.getValues());
	}

	@Test
	public void containsTest() {
		assertTrue(set.contains(A));
		assertTrue(set.contains(A1));
		assertTrue(set.contains(B));
		assertFalse(set.contains(new DiscreteValue("c")));
		assertFalse(set.contains(null));
	}

	@Test
	public void iteratorTest1() {
		Iterator<DiscreteValue> it = set.iterator();
		assertEquals(A, it.next());
		assertEquals(B, it.next());
		assertThrows(() -> it.next(), NoSuchElementException.class); // check that there is a next.

	}

	@Test
	public void iteratorTest2() {
		Iterator<DiscreteValue> it = setReverse.iterator();
		assertEquals(B, it.next());
		assertEquals(A, it.next());
		assertThrows(() -> it.next(), NoSuchElementException.class); // check that there is a next.

	}

	/**
	 * assert that f throws exception of type e
	 * 
	 * @param f the function to be called, wrapped as {@link Runnable}
	 * @param e the {@link Class} of expected exception
	 */
	private static void assertThrows(Runnable f, Class<? extends Throwable> e) {
		try {
			f.run();
			throw new AssertionError("Expected " + e.getSimpleName()
					+ " but nothing was thrown");
		} catch (Throwable actuale) {
			if (actuale.getClass() != e) {
				throw new AssertionError("Expected " + e.getSimpleName()
						+ " but was " + actuale.getClass());
			}
		}
	}

}
