/*******************************************************************************
*   Copyright 2014 Analog Devices, Inc.
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
********************************************************************************/

package com.analog.lyric.collect.tests;

import static org.junit.Assert.*;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;

import org.junit.Test;

import com.analog.lyric.collect.CopyOnWriteConcurrentMap;
import com.analog.lyric.collect.CopyOnWriteMap;

public class CopyOnWriteMapTest
{
	private final CopyOnWriteMapTester<String,Object> tester = new CopyOnWriteMapTester<String,Object>();

	@Test
	public void test()
	{
		testCopyOf(new HashMap<String,Object>());
		testCopyOf(new TreeMap<String,Object>());
		testCopyOfConcurrent(new ConcurrentHashMap<String,Object>());
		testCopyOfConcurrent(new ConcurrentSkipListMap<String,Object>());
	}
	
	private void testCopyOf(Map<String,Object> map)
	{
		testCopyOf(map, new CopyOnWriteMap<String,Object>(map));
	}
	
	private void testCopyOf(Map<String,Object> map, CopyOnWriteMap<String,Object> copyMap)
	{
		tester.validateCopyOnWriteMap(copyMap);
		
		assertSame(map, copyMap.originalMap());
		assertTrue(copyMap.isEmpty());
		assertFalse(copyMap.wasCopied());
		
		map.put("foo", 42);
		assertEquals(42, copyMap.get("foo"));
		
		assertNull(copyMap.put("bar", 23));
		assertEquals(23, copyMap.get("bar"));
		assertNull(map.get("bar"));
		assertEquals(42, copyMap.get("foo"));
		assertTrue(copyMap.wasCopied());
		
		assertEquals(42, copyMap.put("foo",  24));
		assertEquals(24, copyMap.get("foo"));
		assertEquals(42, map.get("foo"));
		
		tester.validateCopyOnWriteMap(copyMap);

		assertEquals(24, copyMap.remove("foo"));
		assertNull(copyMap.get("foo"));
		assertEquals(42, map.get("foo"));
		
		copyMap.clear();
		assertEquals(0, copyMap.size());
		assertEquals(1, map.size());
		assertNull(copyMap.get("bar"));
		
		copyMap.revertToOriginalMap();
		assertFalse(copyMap.wasCopied());
		tester.assertMapEquals(map, copyMap);
		
		map.put("baz", 12);
		assertEquals(12, copyMap.get("baz"));
		
		assertEquals(42, copyMap.remove("foo"));
		assertTrue(copyMap.wasCopied());
		assertNull(copyMap.get("foo"));
		assertEquals(42, map.get("foo"));
		
		copyMap.revertToOriginalMap();
		
		assertFalse(map.isEmpty());
		copyMap.clear();
		assertTrue(copyMap.isEmpty());
		assertFalse(map.isEmpty());
		
		copyMap.revertToOriginalMap();
		assertFalse(copyMap.isEmpty());
		
		Map<String,Object> map2 = new HashMap<String,Object>();
		map2.put("a", 1);
		map2.put("b", 2);
		map2.put("c", 3);
		
		copyMap.putAll(map2);
		for (Map.Entry<String,Object> entry : map2.entrySet())
		{
			assertEquals(entry.getValue(), copyMap.get(entry.getKey()));
			assertFalse(map.containsKey(entry.getKey()));
		}
	}
	
	void testCopyOfConcurrent(ConcurrentMap<String, Object> map)
	{
		testCopyOfConcurrent(map, new CopyOnWriteConcurrentMap<String,Object>(map));
	}
	
	void testCopyOfConcurrent(ConcurrentMap<String,Object> map, CopyOnWriteConcurrentMap<String, Object> copyMap)
	{
		testCopyOf(map, copyMap);
		
		map.clear();
		
		copyMap.revertToOriginalMap();
		assertFalse(copyMap.wasCopied());
		
		map.put("foo", 23);
		assertEquals(23, copyMap.putIfAbsent("foo", 42));
		assertTrue(copyMap.wasCopied());
		assertEquals(23, copyMap.get("foo"));
		copyMap.revertToOriginalMap();
		
		assertEquals(null, copyMap.putIfAbsent("bar", 42));
		assertTrue(copyMap.wasCopied());
		assertEquals(42, copyMap.get("bar"));
		assertFalse(map.containsKey("bar"));
		assertEquals(42, copyMap.putIfAbsent("bar", 1000));
		assertEquals(42, copyMap.get("bar"));
		
		assertFalse(copyMap.remove("bar", 12));
		assertEquals(42, copyMap.get("bar"));
		assertTrue(copyMap.remove("bar", 42));
		assertNull(copyMap.get("bar"));
		assertTrue(copyMap.remove("foo", 23));
		assertNull(copyMap.get("foo"));
		copyMap.revertToOriginalMap();
		
		assertNull(copyMap.replace("baz", 10));
		assertTrue(copyMap.wasCopied());
		assertEquals(23, copyMap.replace("foo", 42));
		assertEquals(42, copyMap.get("foo"));
		assertEquals(23, map.get("foo"));
		
		assertFalse(copyMap.replace("foo", 10, 11));
		assertEquals(42, copyMap.get("foo"));
		assertTrue(copyMap.replace("foo", 42, 11));
		assertEquals(11, copyMap.get("foo"));
		copyMap.revertToOriginalMap();
		assertFalse(copyMap.replace("foo", 10, 11));
		assertTrue(copyMap.wasCopied());
		assertEquals(23, map.get("foo"));
	}
}
