package gnu.trove.map.hash;

import gnu.trove.strategy.HashingStrategy;
import junit.framework.TestCase;

import java.io.*;
import java.util.*;


/**
 *
 */
public class TCustomHashMapTest extends TestCase {
	// Example from Trove overview doc
	public void testArray() {
		char[] foo = new char[]{ 'a', 'b', 'c' };
		char[] bar = new char[]{ 'a', 'b', 'c' };

		assertFalse( foo.hashCode() == bar.hashCode() );
		//noinspection ArrayEquals
		assertFalse( foo.equals( bar ) );

		HashingStrategy<char[]> strategy = new ArrayHashingStrategy();
		assertTrue( strategy.computeHashCode( foo ) ==
			strategy.computeHashCode( bar ) );
		assertTrue( strategy.equals( foo, bar ) );

		Map<char[], String> map = new TCustomHashMap<char[], String>( strategy );
		map.put( foo, "yay" );
		assertTrue( map.containsKey( foo ) );
		assertTrue( map.containsKey( bar ) );
		assertEquals( "yay", map.get( foo ) );
		assertEquals( "yay", map.get( bar ) );

		Set<char[]> keys = map.keySet();
		assertTrue( keys.contains( foo ) );
		assertTrue( keys.contains( bar ) );
	}


	public void testSerialization() throws Exception {
		char[] foo = new char[]{ 'a', 'b', 'c' };
		char[] bar = new char[]{ 'a', 'b', 'c' };

		HashingStrategy<char[]> strategy = new ArrayHashingStrategy();
		Map<char[], String> map = new TCustomHashMap<char[], String>( strategy );

		map.put( foo, "yay" );

		// Make sure it still works after being serialized
		ObjectOutputStream oout = null;
		ByteArrayOutputStream bout = null;
		ObjectInputStream oin = null;
		ByteArrayInputStream bin = null;
		try {
			bout = new ByteArrayOutputStream();
			oout = new ObjectOutputStream( bout );

			oout.writeObject( map );

			bin = new ByteArrayInputStream( bout.toByteArray() );
			oin = new ObjectInputStream( bin );

			map = ( Map<char[], String> ) oin.readObject();
		}
		finally {
			if ( oin != null ) oin.close();
			if ( bin != null ) bin.close();
			if ( oout != null ) oout.close();
			if ( bout != null ) bout.close();
		}

		assertTrue( map.containsKey( foo ) );
		assertTrue( map.containsKey( bar ) );
		assertEquals( "yay", map.get( foo ) );
		assertEquals( "yay", map.get( bar ) );

		Set<char[]> keys = map.keySet();
		assertTrue( keys.contains( foo ) );
		assertTrue( keys.contains( bar ) );
	}

	static class ByteArrayStrategy implements HashingStrategy<byte[]> {

		// Copied from Arrays.hashCode, but applied only to the first four bytes
		public int computeHashCode( byte[] bytes ) {
			if ( bytes == null ) return 0;
			int h = 1;
			for ( int i = 0; i < 4; i++ ) h = 31 * h + bytes[ i ];
			return h;
		}

		public boolean equals( byte[] o1, byte[] o2 ) {
			return Arrays.equals( o1, o2 );
		}
	}

	private static byte[] random( int n, Random rnd ) {
		byte[] ba = new byte[ n ];
		for ( int i = 0; i < ba.length; i++ ) {
			ba[ i ] = ( byte ) rnd.nextInt();
		}
		return ba;
	}

	public void testBug4706479() throws Exception {
		Random rnd = new Random( 1234 );
		TCustomHashMap<byte[], Integer> map =
			new TCustomHashMap<byte[], Integer>( new ByteArrayStrategy() );
		List<byte[]> list = new ArrayList<byte[]>();
		List<Integer> expected = new ArrayList<Integer>();

		for ( int i = 0; i < 1000; i++ ) {
			byte[] ba = random( 16, rnd );
			list.add( ba );

			Integer obj = Integer.valueOf( i );
			expected.add( obj );
			map.put( ba, obj );
		}

		assertEquals( list.size(), map.size() );

		// Make sure all the arrays are found in the map
		for( byte[] array : map.keySet() ) {
			boolean found_it = false;
			for( byte[] test : list ) {
				if ( Arrays.equals( test, array ) ) {
					found_it = true;
					break;
				}
			}

			assertTrue( "Unable to find: " + Arrays.toString( array ), found_it );
		}
		// Make sure all the Integers are found in the map
		for( Integer obj : map.values() ) {
			assertTrue( "Unable to find: " + obj, expected.contains( obj ) );
		}

		for ( int i = 0; i < expected.size(); i++ ) {
			assertEquals( expected.get( i ), map.get( list.get( i ) ) );
		}

		// Remove items
		for ( int i = 0; i < list.size(); i++ ) {
			assertEquals( expected.get( i ), map.remove( list.get( i ) ) );
		}

		assertEquals( 0, map.size() );
		assertTrue( map.isEmpty() );

		for ( byte[] aList : list ) {
			assertNull( map.get( aList ) );
		}
	}


}
