package migl.lisp;

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

import java.math.BigInteger;
import java.util.Map;
import java.util.HashMap;

import org.junit.Test;

public class LispElementOwnTest {

	@Test(expected = IllegalArgumentException.class)
	public void testGenerateBadElement() {
		Object obj = new Object();
		LispElement.generate(obj);
	}
	
	@Test(expected = IllegalStateException.class)
	public void testToIntFail() {
		LispElement elt = LispElement.valueOf("5.8");
		elt.toInt();
	}
	
	@Test(expected = IllegalStateException.class)
	public void testToNumberFail() {
		LispElement elt = LispElement.valueOf("test");
		elt.toNumber();
	}
	
	@Test(expected = IllegalStateException.class)
	public void testToBooleanFail() {
		LispElement elt = LispElement.valueOf("5.8");
		elt.toBoolean();
	}
	
	@Test(expected = IllegalStateException.class)
	public void testToStrFail() {
		LispElement elt = LispElement.valueOf("5.8");
		elt.toStr();
	}
	
	@Test
	public void testToInt() {
		LispElement elt = LispElement.valueOf("8888888888888888");
		BigInteger expected = new BigInteger("8888888888888888");
		assertEquals(expected, elt.toInt());
	}
	
	@Test
	public void testToNumberFromDouble() {
		LispElement elt = LispElement.valueOf("5.8");
		Double expected = Double.valueOf(5.8);
		Double value = elt.toNumber();
		assertEquals(expected, value);
	}
	
	@Test
	public void testToNumberFromInt() {
		LispElement elt = LispElement.valueOf("8");
		Double expected = Double.valueOf(8.0);
		Integer notExpected = 8;
		assertEquals(expected, (Double) elt.toNumber());
		assertNotEquals(notExpected, elt.toNumber());
	}
	
	@Test
	public void testToBoolean() {
		LispElement elt1 = LispElement.valueOf("#t");
		LispElement elt2 = LispElement.generate(false);
		assertTrue(elt1.toBoolean());
		assertFalse(elt2.toBoolean());
	}
	
	@Test
	public void testToStr() {
		LispElement elt1 = LispElement.valueOf("test");
		LispElement elt2 = LispElement.valueOf("test ");
		assertEquals("test", elt1.toStr());
		assertNotEquals("test", elt2.toStr());
	}
	
	@Test
	public void testHashCode() {
		LispElement elt1 = LispElement.valueOf("test");
		LispElement elt2 = LispElement.valueOf("#t");
		LispElement elt3 = LispElement.valueOf("8");
		LispElement elt4 = LispElement.valueOf("8.0");
		Map<LispElement, String> map = new HashMap<>();
		map.put(elt1, "elt1");
		map.put(elt2, "elt2");
		map.put(elt3, "elt3");
		map.put(elt4, "elt4");
		assertEquals("elt1", map.get(elt1));
		assertEquals("elt2", map.get(elt2));
		assertEquals("elt3", map.get(elt3));
		assertEquals("elt4", map.get(elt4));
	}
	
	@Test
	public void testEquals() {
		LispElement elt1 = LispElement.valueOf("test");
		LispElement elt2 = LispElement.valueOf("test");
		LispElement elt3 = LispElement.valueOf("Test");
		LispElement elt4 = LispElement.valueOf("#t");
		LispElement elt5 = LispElement.valueOf("#t");
		LispElement elt6 = LispElement.valueOf("#f");
		LispElement elt7 = LispElement.valueOf("8");
		LispElement elt8 = LispElement.valueOf("8.0");
		Object other = new Object();
		assertTrue(elt1.equals(elt2));
		assertFalse(elt1.equals(elt3));
		assertTrue(elt1.equals(elt1));
		assertFalse(elt1.equals(null));
		assertFalse(elt1.equals(elt4));
		assertTrue(elt4.equals(elt5));
		assertFalse(elt4.equals(elt6));
		assertFalse(elt7.equals(elt8));
		assertFalse(elt1.equals(other));
		assertEquals(elt1.hashCode(), elt2.hashCode());
		assertNotEquals(elt1.hashCode(), elt3.hashCode());
		assertEquals(elt4.hashCode(), elt5.hashCode());
		assertNotEquals(elt4.hashCode(), elt6.hashCode());
		assertNotEquals(elt7.hashCode(), elt8.hashCode());
	}

	@Test
	public void testGenerateBigIntFromInt() {
		LispElement elt = LispElement.generate(Integer.valueOf(8));
		BigInteger expected = new BigInteger("8");
		assertEquals(expected, elt.value);
	}
}
