/*
 * Copyright 2006 Takahiro Nakamura.
 *
 * 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 woolpack.utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import woolpack.utils.fn.UtilsFnUtils;

import junit.framework.TestCase;

public class UtilsTest extends TestCase {
	
	public void testBuildableArrayList() {
		final BuildableArrayList<String> list = Utils.list("key0");

		assertEquals(list, list.list("key1"));
		assertEquals(list, list.list("key2"));
		assertEquals(list, list.list("key2"));
		assertEquals(list, list.merge(Arrays.asList("key3", "key4")));
		assertEquals(list, list.list("key2"));

		assertEquals(Arrays.asList("key0", "key1", "key2", "key2", "key3", "key4", "key2"), list);
	}
	
	public void testBuildableHashMap() {
		final BuildableHashMap<String, Integer> map = Utils.map("key0", 5);

		assertEquals(map, map.map("key1"));
		assertEquals(map, map.map("key2", 7));
		assertEquals(map, map.map("key2", 9));
		assertEquals(map, map.merge(Utils.map("key3", 11).map("key4", 13)));

		final Map<String, Integer> expected = new HashMap<String, Integer>();
		expected.put("key0", Integer.valueOf(5));
		expected.put("key1", Integer.valueOf(5));
		expected.put("key2", Integer.valueOf(9));
		expected.put("key3", Integer.valueOf(11));
		expected.put("key4", Integer.valueOf(13));
		assertEquals(expected, map);
	}
	
	public void testBuildableLinkedHashMap() {
		final BuildableLinkedHashMap<String, Integer> map = Utils.linkedMap("key0", 5);

		assertEquals(map, map.map("key1"));
		assertEquals(map, map.map("key2", 7));
		assertEquals(map, map.map("key2", 9));
		assertEquals(map, map.merge(Utils.map("key3", 11).map("key4", 13)));

		final Map<String, Integer> expected = new HashMap<String, Integer>();
		expected.put("key0", Integer.valueOf(5));
		expected.put("key1", Integer.valueOf(5));
		expected.put("key2", Integer.valueOf(9));
		expected.put("key3", Integer.valueOf(11));
		expected.put("key4", Integer.valueOf(13));
		assertEquals(expected, map);
	}

	public void testInverseMap() {
		final Map<String, Integer> before = new HashMap<String, Integer>();
		final Map<Integer, String> after = UtilsFnUtils.<String, Integer>inverseMap().exec(before);
		assertEquals(0, after.size());
		
		after.put(1, "1");
		assertEquals(Utils.map("1", 1), before);
		assertEquals(Utils.map(1, "1"), after);

		before.put("2", 2);
		assertEquals(Utils.map("1", 1).map("2", 2), before);
		assertEquals(Utils.map(1, "1").map(2, "2"), after);
		
		try {
			after.put(1, "2");
			fail();
		} catch(final IllegalStateException e) {
		}

		try {
			after.entrySet().iterator().next().setValue("3");
			fail();
		} catch(final UnsupportedOperationException e) {
		}

		after.keySet().remove(1);
		assertEquals(Utils.map("2", 2), before);
		assertEquals(Utils.map(2, "2"), after);
	}

	public void testInjectiveMap() {
		final Map<String, String> map = UtilsFnUtils
				.<String, String>injectiveMap().exec(new HashMap<String, String>());
		map.put("a", "A");
		map.put("b", "B");
		try {
			map.put("b", "A");
			fail();
		} catch (final IllegalStateException expected) {
			assertEquals("value duplicate:A", expected.getMessage());
		}
		map.put("a", "A");
		map.put("x", "X");
		{
			final Map<String, String> tmp = new HashMap<String, String>();
			tmp.put("a", "A");
			tmp.put("b", "B");
			map.putAll(tmp);
		}
		{
			final Map<String, String> tmp = new HashMap<String, String>();
			tmp.put("a", "C");
			tmp.put("b", "D");
			map.putAll(tmp);
		}
		try {
			final Map<String, String> tmp = new HashMap<String, String>();
			tmp.put("a", "C");
			tmp.put("b", "C");
			map.putAll(tmp);
			fail();
		} catch (final IllegalStateException expected) {
			assertEquals("value duplicate", expected.getMessage());
		}
		{
			final Map<String, String> tmp = new HashMap<String, String>();
			tmp.put("a", "D");
			tmp.put("b", "C");
			map.putAll(tmp);
		}
		assertEquals(Utils.map("a", "D").map("b", "C").map("x", "X"), map);
	}

	public void testUnoverwritableMap() {
		final Map<String, String> map = UtilsFnUtils
				.<String, String>unoverwritableMap().exec(new HashMap<String, String>());
		map.put("a", "z");
		map.put("b", "z");
		try {
			map.put("a", "y");
			fail();
		} catch (final IllegalStateException expected) {
			assertEquals("already registered:a", expected.getMessage());
		}
		{
			final Map<String, String> tmp = new HashMap<String, String>();
			tmp.put("c", "z");
			tmp.put("d", "z");
			map.putAll(tmp);
		}
		try {
			final Map<String, String> tmp = new HashMap<String, String>();
			tmp.put("e", "z");
			tmp.put("b", "z");
			map.putAll(tmp);
			fail();
		} catch (final IllegalStateException expected) {
			assertEquals("already registered:b", expected.getMessage());
		}
	}

	public void testNotNullMap() {
		final Map<String, String> map = UtilsFnUtils
				.<String, String>notNullMap().exec(new HashMap<String, String>());
		map.put("a", "z");
		map.put("b", "z");
		try {
			map.put(null, "y");
			fail();
		} catch (final NullPointerException expected) {
		}
		map.put("c", "z");
		try {
			map.put("d", null);
			fail();
		} catch (final NullPointerException expected) {
		}
		{
			final Map<String, String> tmp = new HashMap<String, String>();
			tmp.put("e", "z");
			tmp.put("f", "z");
			map.putAll(tmp);
		}
		try {
			final Map<String, String> tmp = new HashMap<String, String>();
			tmp.put("g", "z");
			tmp.put(null, "z");
			map.putAll(tmp);
			fail();
		} catch (final NullPointerException expected) {
		}
		try {
			final Map<String, String> tmp = new HashMap<String, String>();
			tmp.put("h", "z");
			tmp.put("i", null);
			map.putAll(tmp);
			fail();
		} catch (final NullPointerException expected) {
		}
	}

	public void testKeyNotEmptyMap() {
		final Map<String, String> map = UtilsFnUtils
				.<String>keyNotEmptyMap().exec(new HashMap<String, String>());
		map.put("a", "z");
		map.put("b", "z");
		{
			final Map<String, String> tmp = new HashMap<String, String>();
			map.put("c", "z");
			map.put("d", "z");
			map.putAll(tmp);
		}
		try {
			map.put("", "y");
			fail();
		} catch (final StringIndexOutOfBoundsException expected) {
		}
		{
			final Map<String, String> tmp = new HashMap<String, String>();
			tmp.put("c", "z");
			tmp.put("d", "z");
			map.putAll(tmp);
		}
		try {
			final Map<String, String> tmp = new HashMap<String, String>();
			tmp.put("e", "z");
			tmp.put("", "z");
			map.putAll(tmp);
			fail();
		} catch (final StringIndexOutOfBoundsException expected) {
		}
	}

	public void testUnmodifiableIterable() {
		final Iterable<String> iterable;
		{
			final List<String> list = new ArrayList<String>();
			list.add("a");
			iterable = UtilsFnUtils.<String>unmodifiableIterable().exec(list);
		}
		final Iterator<String> it = iterable.iterator();
		assertTrue(it.hasNext());
		assertEquals("a", it.next());
		assertFalse(it.hasNext());
		try {
			it.remove();
			fail();
		} catch (final UnsupportedOperationException expected) {
		}
	}

	public void testToListFromNull() {
		final List list = UtilsFnUtils.TO_LIST.exec(null);
		assertEquals(1, list.size());
		assertNull(list.get(0));
	}

	public void testToListFromPrimitiveArray() {
		final int[] array = new int[2];
		final List list = UtilsFnUtils.TO_LIST.exec(array);
		assertEquals(2, list.size());
		list.set(1, Integer.valueOf(3));
		assertEquals(3, array[1]);
		assertEquals(Integer.valueOf(3), list.get(1));
	}

	public void testToListFromObjectArray() {
		final Integer[] array = new Integer[2];
		final List list = UtilsFnUtils.TO_LIST.exec(array);
		assertEquals(2, list.size());
		list.set(1, Integer.valueOf(3));
		assertEquals(Integer.valueOf(3), array[1]);
		assertEquals(Integer.valueOf(3), list.get(1));
	}

	public void testToListFromList() {
		final List target = new ArrayList();
		assertEquals(target, UtilsFnUtils.TO_LIST.exec(target));
	}

	public void testToListFromObject() {
		final Integer target = Integer.valueOf(3);
		final List list = UtilsFnUtils.TO_LIST.exec(target);
		assertEquals(1, list.size());
		assertEquals(target, list.get(0));
	}

	public void testToCollectionFromCollection() {
		final Collection target = new ArrayList();
		assertEquals(target, UtilsFnUtils.TO_COLLECTION.exec(target));
	}

	public void testToIterableFromIterable() {
		final Iterable target = new ArrayList();
		assertEquals(target, UtilsFnUtils.TO_ITERABLE.exec(target));
	}

	public void testSimilarKeyMap() {
		final Map<String, String> mapA = new HashMap<String, String>();
		mapA.put("hoge_piyo", "hoge_piyo");
		mapA.put("NARU_HODO", "NARU_HODO");
		mapA.put("fooBar", "fooBar");
		mapA.put("GinowanOkinawa", "GinowanOkinawa");
		final Map<String, String> mapB = UtilsFnUtils.<String>similarKeyMap().exec(mapA);

		assertEquals(mapA.get("hoge_piyo"), mapB.get("hoge_piyo"));
		assertEquals(mapA.get("hoge_piyo"), mapB.get("HOGE_PIYO"));
		assertEquals(mapA.get("hoge_piyo"), mapB.get("hogePiyo"));
		assertEquals(mapA.get("hoge_piyo"), mapB.get("HogePiyo"));

		assertEquals(mapA.get("NARU_HODO"), mapB.get("NARU_HODO"));
		assertEquals(mapA.get("NARU_HODO"), mapB.get("naru_hodo"));
		assertEquals(mapA.get("NARU_HODO"), mapB.get("naruHodo"));
		assertEquals(mapA.get("NARU_HODO"), mapB.get("NaruHodo"));

		assertEquals(mapA.get("fooBar"), mapB.get("fooBar"));
		assertEquals(mapA.get("fooBar"), mapB.get("FooBar"));
		assertEquals(mapA.get("fooBar"), mapB.get("foo_bar"));
		assertEquals(mapA.get("fooBar"), mapB.get("FOO_BAR"));

		assertEquals(mapA.get("GinowanOkinawa"), mapB.get("GinowanOkinawa"));
		assertEquals(mapA.get("GinowanOkinawa"), mapB.get("ginowanOkinawa"));
		assertEquals(mapA.get("GinowanOkinawa"), mapB.get("GINOWAN_OKINAWA"));
		assertEquals(mapA.get("GinowanOkinawa"), mapB.get("ginowan_okinawa"));

		assertTrue(mapB.containsKey("hoge_piyo"));
		assertTrue(mapB.containsKey("HOGE_PIYO"));
		assertTrue(mapB.containsKey("hogePiyo"));
		assertTrue(mapB.containsKey("HogePiyo"));
		assertFalse(mapB.containsKey("HogePiyo "));

		assertNull(mapB.get("hoge_piy0"));
	}

	public void testToMessageList() {
		assertEquals(Arrays.asList(new String[] {
				"message0",
				"message1",
				"message2", }),
				Arrays.asList(UtilsFnUtils.TO_MESSAGE_LIST.exec(
				new Exception("message0",
						new Exception("message1",
								new Exception("message2"))))
				.toArray(new String[0])));
		assertEquals(Arrays.asList(new String[] { "message0", "message2" }),
				Arrays.asList(UtilsFnUtils.TO_MESSAGE_LIST.exec(
						new Exception("message0", new Exception(null,
								new Exception("message2"))))
				.toArray(new String[0])));
		assertEquals(Arrays.asList(new String[] { "message1", "message2" }),
				Arrays.asList(UtilsFnUtils.TO_MESSAGE_LIST.exec(
						new Exception(new Exception("message1", new Exception("message2"))))
				.toArray(new String[0])));
		assertEquals(Arrays.asList(new String[] { "message0", "message2" }),
				Arrays.asList(UtilsFnUtils.TO_MESSAGE_LIST.exec(
						new Exception("message0", new Exception(new Exception("message2"))))
				.toArray(new String[0])));
		assertEquals(Arrays.asList(new String[] { "message0", "message1",
				"java.lang.IllegalArgumentException", }), Arrays
				.asList(UtilsFnUtils.TO_MESSAGE_LIST.exec(
						new Exception("message0", new Exception("message1",
								new IllegalArgumentException())))
				.toArray(new String[0])));
	}
}
