/*******************************************************************************
 * Copyright (c) 2007 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.core.parser.tests.ast2;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import junit.framework.TestCase;

import org.eclipse.cdt.core.parser.util.CharArrayMap;
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;

/**
 * 
 * @author Mike Kucera
 */
public class CharArrayMapTest extends TestCase {

	private static class Slice { // convenience class
		final char[] chars;
		final int start;
		final int length;
		public Slice(char[] chars, int start, int length) {
			this.chars = chars;
			this.length = length;
			this.start = start;
		}
		public String toString() {
			return new String(chars, start, length);
		}
	}
	
	
	
	public void disabled_testPerformance() {
		final int iterations = 10000;
		// insert tons of keys
		char[][] keys = new char[iterations][];
		for(int i = 0; i < keys.length; i++) {
			keys[i] = String.valueOf(i).toCharArray();
		}
		
		System.gc();
		long mapTime = timeMap(keys);
		
		System.gc();
		long oldMapTime = timeOldMap(keys);

		System.out.println("mapTime: " + mapTime);
		System.out.println("oldMapTime: " + oldMapTime);
		assertTrue(oldMapTime > mapTime);
	}
	
	
	private static long timeMap(char[][] keys) {
		long start = System.currentTimeMillis();
		CharArrayMap<Integer> map = new CharArrayMap<Integer>(keys.length);
		for(int i = 0; i < keys.length; i++) {
			map.put(keys[i], i);
		}
		assertEquals(keys.length, map.size());
		for(int i = 0; i < keys.length; i++) {
			assertEquals(new Integer(i), map.get(keys[i]));
		}
		return System.currentTimeMillis() - start;
	}
	
	
	private static long timeOldMap(char[][] keys) {
		long start = System.currentTimeMillis();
		CharArrayObjectMap oldMap = new CharArrayObjectMap(keys.length);
		for(int i = 0; i < keys.length; i++) {
			oldMap.put(keys[i], new Integer(i));
		}
		assertEquals(keys.length, oldMap.size());
		for(int i = 0; i < keys.length; i++) {
			assertEquals(new Integer(i), oldMap.get(keys[i]));
		}
		return System.currentTimeMillis() - start;
	}
	
	
	public void testBasicUsage1() {
		char[] key1 = "first key".toCharArray();
		char[] key2 = "second key".toCharArray();
		char[] key3 = "third key".toCharArray();
		char[] key4 = "forth key".toCharArray();
		
		CharArrayMap<Integer> map = new CharArrayMap<Integer>();
		assertTrue(map.isEmpty());
		assertEquals(0, map.size());
		
		map.put(key1, 1);
		map.put(key2, 2);
		map.put(key3, 3);
		map.put(key4, 4);
		
		assertFalse(map.isEmpty());
		assertEquals(4, map.size());
		
		assertEquals(new Integer(1), map.get(key1));
		assertEquals(new Integer(2), map.get(key2));
		assertEquals(new Integer(3), map.get(key3));
		assertEquals(new Integer(4), map.get(key4));
		
		assertTrue(map.containsKey(key1));
		assertTrue(map.containsKey(key2));
		assertTrue(map.containsKey(key3));
		assertTrue(map.containsKey(key4));
		
		assertTrue(map.containsValue(1));
		assertTrue(map.containsValue(2));
		assertTrue(map.containsValue(3));
		assertTrue(map.containsValue(4));
		
		Set<Integer> values = new HashSet<Integer>();
		values.add(1);
		values.add(2);
		values.add(3);
		values.add(4);
		
		for(int i : map.values()) {
			assertTrue(values.remove(i));
		}
		
		// remove a mapping
		assertEquals(new Integer(1), map.remove(key1));
		assertEquals(3, map.size());
	    assertNull(map.get(key1));
	    assertFalse(map.containsKey(key1));
	    assertFalse(map.containsValue(1));
	    assertNull(map.remove(key1)); // its already removed
		
		map.clear();
		assertTrue(map.isEmpty());
		assertEquals(0, map.size());
		
		// test null values	
		map.put(key1, null);
		assertEquals(1, map.size());
		assertNull(map.get(key1));
		assertTrue(map.containsKey(key1));
		assertTrue(map.containsValue(null));
		
		// overrideing values should
		map.put(key1, 100);
		assertEquals(1, map.size());
		assertEquals(new Integer(100), map.get(key1));
		assertTrue(map.containsValue(100));
		assertFalse(map.containsValue(null));
		// override the value
		map.put(key1, 200);
		assertEquals(1, map.size());
		assertEquals(new Integer(200), map.get(key1));
		assertTrue(map.containsValue(200));
		assertFalse(map.containsValue(100));
	}
	
	
	public void testBasicUsage2() {
		char[] chars = "pantera, megadeth, soulfly, metallica, in flames, lamb of god, carcass".toCharArray();
		
		Slice[] slices = new Slice[7];
		slices[0] = new Slice(chars, 0, 7);
		slices[1] = new Slice(chars, 9, 8);
		slices[2] = new Slice(chars, 19, 7);
		slices[3] = new Slice(chars, 28, 9);
		slices[4] = new Slice(chars, 39, 9);
		slices[5] = new Slice(chars, 50, 11);
		slices[6] = new Slice(chars, 63, 7);
		
		char[][] keys = new char[7][];
		keys[0] = "pantera".toCharArray();
		keys[1] = "megadeth".toCharArray();
		keys[2] = "soulfly".toCharArray();
		keys[3] = "metallica".toCharArray();
		keys[4] = "in flames".toCharArray();
		keys[5] = "lamb of god".toCharArray();
		keys[6] = "carcass".toCharArray();
		
		
		CharArrayMap<Integer> map = new CharArrayMap<Integer>();
		assertTrue(map.isEmpty());
		assertEquals(0, map.size());
		
		for(int i = 0; i < slices.length; i++) {
			Slice slice = slices[i];
			map.put(slice.chars, slice.start, slice.length, i);
		}
		
		assertFalse(map.isEmpty());
		assertEquals(7, map.size());
		
		// should still work with equivalent keys
		for(int i = 0; i < keys.length; i++) {
			Slice slice = slices[i];
			assertEquals(new Integer(i), map.get(slice.chars, slice.start, slice.length));
			assertEquals(new Integer(i), map.get(keys[i]));
			assertTrue(map.containsKey(slice.chars, slice.start, slice.length));
			assertTrue(map.containsKey(keys[i]));
			assertTrue(map.containsValue(i));
		}

		Set<Integer> values = new HashSet<Integer>();
		for(int i = 0; i < keys.length; i++) {
			values.add(i);
		}
		
		for(int i : map.values()) {
			assertTrue(values.remove(i));
		}
		
		// remove the last two keys
		map.remove(keys[5]);
		map.remove(slices[6].chars, slices[6].start, slices[6].length);
		
		assertEquals(5, map.size());
		
		// remaining keys should still be there
		for(int i = 0; i < 5; i++) {
			Slice slice = slices[i];
			assertEquals(new Integer(i), map.get(slice.chars, slice.start, slice.length));
			assertEquals(new Integer(i), map.get(keys[i]));
			assertTrue(map.containsKey(slice.chars, slice.start, slice.length));
			assertTrue(map.containsKey(keys[i]));
			assertTrue(map.containsValue(i));
		}
		
		map.clear();
		assertTrue(map.isEmpty());
		assertEquals(0, map.size());
	}
	
	
	public void testProperFail() {
		char[] hello = "hello".toCharArray();
		CharArrayMap<Integer> map = new CharArrayMap<Integer>();
		Integer value = new Integer(9);
		
		
		try {
			map.put(null, value);
			fail();
		} catch(NullPointerException _) {}
		
		try {
			map.put(hello, -1, 5, value);
			fail();
		} catch(IndexOutOfBoundsException _) {}
		
		try {
			map.put(hello, 0, -1, value);
			fail();
		} catch(IndexOutOfBoundsException _) {}
		
		try {
			map.put(hello, 0, 100, value);
			fail();
		} catch(IndexOutOfBoundsException _) {}
		
		
		try {
			map.get(null);
			fail();
		} catch(NullPointerException _) {}
		
		try {
			map.get(hello, -1, 5);
			fail();
		} catch(IndexOutOfBoundsException _) {}
		
		try {
			map.get(hello, 0, -1);
			fail();
		} catch(IndexOutOfBoundsException _) {}
		
		try {
			map.get(hello, 0, 100);
			fail();
		} catch(IndexOutOfBoundsException _) {}
		
		
		try {
			map.remove(null);
			fail();
		} catch(NullPointerException _) {}
		
		try {
			map.remove(hello, -1, 5);
			fail();
		} catch(IndexOutOfBoundsException _) {}
		
		try {
			map.remove(hello, 0, -1);
			fail();
		} catch(IndexOutOfBoundsException _) {}
		
		try {
			map.remove(hello, 0, 100);
			fail();
		} catch(IndexOutOfBoundsException _) {}
		
		
		try {
			map.containsKey(null);
			fail();
		} catch(NullPointerException _) {}
		
		try {
			map.containsKey(hello, -1, 5);
			fail();
		} catch(IndexOutOfBoundsException _) {}
		
		try {
			map.containsKey(hello, 0, -1);
			fail();
		} catch(IndexOutOfBoundsException _) {}
		
		try {
			map.containsKey(hello, 0, 100);
			fail();
		} catch(IndexOutOfBoundsException _) {}
		
		
		try {
			new CharArrayMap<Integer>(-1);
		} catch(IllegalArgumentException _) {}
	}
}
