/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.collection.map;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import net.ranides.assira.collection.SetUtils;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 *
 * @author msieron
 */


public class RandomAccessMapTest {
	
	private RandomAccessMap<String, Integer> make() {
		RandomAccessMap<String, Integer> map = new RandomAccessMap<>();
		map.put("a", 10);
		map.put("q", 20);
		map.put("s", 15);
		map.put("d", 12);
		map.put("x-end", 1);
		return map;
	}
	
	@Test
	public void testBasic() {
		RandomAccessMap<String, Integer> map = make();
		TreeMap<String, Integer> imap = new TreeMap<>();
		imap.put("u-a", 101);
		imap.put("u-b", 102);
		imap.put("u-c", 103);
		map.putAll(imap);
		
		assertEquals("{a=10, q=20, s=15, d=12, x-end=1, u-a=101, u-b=102, u-c=103}", map.toString());
		assertEquals(8, map.size());
		assertFalse( map.isEmpty());
		
		assertEquals(Arrays.asList("a","q", "s", "d", "x-end", "u-a", "u-b", "u-c"), map.keysList() );
		assertEquals(SetUtils.asHashSet("a","q", "s", "d", "x-end", "u-a", "u-b", "u-c"), map.keySet());
		
		assertEquals(10, map.remove("a").intValue());
		assertEquals(15, map.remove("s").intValue());
		assertNull( map.remove("?"));
		
		assertEquals("{q=20, d=12, x-end=1, u-a=101, u-b=102, u-c=103}", map.toString());
		
		assertEquals(12, map.put("d", 10).intValue());
		
		assertEquals("{q=20, d=10, x-end=1, u-a=101, u-b=102, u-c=103}", map.toString());
		
		assertTrue( map.containsValue(1));
		assertTrue( map.containsValue(103));
		assertTrue( map.containsValue(20));
		assertFalse( map.containsValue(99));
		
		map.clear();
		assertEquals(0, map.size());
		assertTrue( map.isEmpty());
	}
	
	@Test
	public void testListAdapter() {
		RandomAccessMap<String, Integer> map = make();
		List<Integer> adapter = map.asListAdapter();
		Collection<Integer> values = map.values();
		
		assertEquals(adapter, values);
		assertEquals( adapter.getClass(), values.getClass()); // that's implementation detail but...
		assertEquals( adapter.iterator().getClass(), map.iterator().getClass()); // that's implementation detail but...
		
		
		assertEquals(Arrays.asList(10, 20, 15, 12, 1), adapter);
		
		
		assertTrue(adapter.contains(10));
		assertTrue(adapter.contains(20));
		assertFalse(adapter.contains(80));
		
		assertFalse(adapter.remove((Integer)80));
		assertTrue(adapter.remove((Integer)15));
		assertFalse(map.containsKey("s"));
		
		assertEquals(10, adapter.remove(0).intValue());
		assertFalse(map.containsKey("a"));
		
		assertEquals(12, adapter.set(1, 99).intValue());
		assertEquals(99, map.get("d").intValue());
	}
	
	@Test
	public void testEntryAdapter() {
		RandomAccessMap<String, Integer> map = make();
		List<Entry<String,Integer>> adapter = map.entryList();
		Set<Entry<String,Integer>> set = map.entrySet();
		
		assertEquals(set, adapter);
		assertEquals(set.getClass(), adapter.getClass()); // that's implementation detail but...
				
		List<String> entries = new ArrayList<>();
		List<String> expected = Arrays.asList("a=10","q=20","s=15","d=12","x-end=1");
		
		for(Entry<String,Integer> entry : adapter) {
			entries.add(entry.getKey()+"="+entry.getValue());
		}
		assertEquals(expected, entries);
		
		assertTrue( adapter.add(new AbstractMap.SimpleEntry<>("H",5)) );
		assertTrue( adapter.add(new AbstractMap.SimpleEntry<>("null",(Integer)null)) );
		assertEquals(5, map.get("H").intValue() );
		
		assertTrue(adapter.contains(new AbstractMap.SimpleEntry<>("q",20)));
		assertFalse(adapter.contains(new AbstractMap.SimpleEntry<>("?",20)));
		assertFalse(adapter.contains(new AbstractMap.SimpleEntry<>("q",17)));
		assertFalse(adapter.contains(new AbstractMap.SimpleEntry<>("q",null)));
		assertFalse(adapter.contains(new AbstractMap.SimpleEntry<>("?",null)));
		assertTrue(adapter.contains(new AbstractMap.SimpleEntry<>("null",null)));
		
		assertFalse( adapter.remove(new AbstractMap.SimpleEntry<>("q",2)) );
		assertTrue( map.containsKey("q"));
		
		assertTrue( adapter.remove(new AbstractMap.SimpleEntry<>("q",20)) );
		assertFalse( map.containsKey("q"));
		
		assertEquals("d", adapter.remove(2).getKey());
		assertFalse( map.containsKey("d"));
		
		// {a=10, s=15, x-end=1, H=5, null=}
		
		adapter.set(1, new AbstractMap.SimpleEntry<>("S",7));
		assertEquals(7, map.get("S").intValue());
		assertNull( map.get("s"));
		
		adapter.set(1, new AbstractMap.SimpleEntry<>("S",9));
		assertEquals(9, map.get("S").intValue());
		
		try {
			adapter.set(2, new AbstractMap.SimpleEntry<>("S",9));
			fail("IllegalArgumentException expected");
		} catch(IllegalArgumentException ex) {
			assertTrue(true);
		}
		
		assertEquals("{a=10, S=9, x-end=1, H=5, null=}", map.toString());
		adapter.get(2).setValue(111);
		assertEquals("{a=10, S=9, x-end=111, H=5, null=}", map.toString());
		
		assertTrue(adapter.get(1).equals(new AbstractMap.SimpleEntry<>("S",9)));
		assertFalse(adapter.get(1).equals(new AbstractMap.SimpleEntry<>("S",19)));
		assertFalse(adapter.get(1).equals(new Object()));
		
	}
	
	@Test
	public void testKeysAdapter() {
		RandomAccessMap<String, Integer> map = make();
		List<String> adapter = map.keysList();
		Set<String> set = map.keySet();
		
		assertEquals(set, adapter);
		assertEquals(set.getClass(), adapter.getClass()); // that's implementation detail but...
		
		assertEquals("{a=10, q=20, s=15, d=12, x-end=1}", map.toString() );
		
		assertEquals("s", adapter.remove(2));
		assertEquals("{a=10, q=20, d=12, x-end=1}", map.toString() );
		
		assertTrue( adapter.remove("d"));
		assertFalse( adapter.remove("?"));
		
		assertEquals("{a=10, q=20, x-end=1}", map.toString() );
		assertEquals(3, adapter.size());
		
		try {
			adapter.set(2, "a");
			fail("IllegalArgumentException expected");
		} catch(IllegalArgumentException ex) {
			assertTrue(true);
		}
		
		assertEquals("q", adapter.set(1, "Q"));
		assertEquals("{a=10, Q=20, x-end=1}", map.toString() );
		
		adapter.clear();
		assertTrue( map.isEmpty() );
	}
	
}
