/*
 * 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.misc;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

import junit.framework.TestCase;
import woolpack.convert.ConvertUtils;
import woolpack.fn.Fn;
import woolpack.fn.FnUtils;
import woolpack.fn.NullFn;
import woolpack.fn.SwitchFn;
import woolpack.fn.ThrowFn;
import woolpack.utils.BuildableArrayList;
import woolpack.utils.Utils;

public class MiscUtilsTest extends TestCase {

	public void testLapTime() {
		final List<Long> list = new ArrayList<Long>();
		final Fn<Integer, String> fn = MiscUtils.lapTime(
				FnUtils.seq(new BuildableArrayList<Fn<? super Integer, ? extends String>>()
				.list(new SleepFn<Integer, String>(100))
				.list(ConvertUtils.TO_STRING)),
				FnUtils.recode(null, list, null));
		assertEquals("4", fn.exec(4));
		assertTrue(Math.abs(list.get(0) - 100) < 10);
	}

	public void testLapTimeException() {
		final List<Long> list = new ArrayList<Long>();
		final Fn<Integer, String> fn = MiscUtils.lapTime(
				FnUtils.seq(new BuildableArrayList<Fn<? super Integer, ? extends String>>()
				.list(new SleepFn<Integer, String>(100))
				.list(ConvertUtils.TO_STRING)
				.list(new ThrowFn<Integer, String>(new IllegalStateException()))),
				FnUtils.recode(null, list, null));
		try {
			fn.exec(4);
			fail();
		} catch(final IllegalStateException e) {
		}
		assertTrue(Math.abs(list.get(0) - 100) < 10);
	}
	
	public void testSleep() {
		final SleepFn<String, String> fn = MiscUtils.sleep(100);
		assertEquals(100, fn.getSleepMillis());
		final long before = System.currentTimeMillis();
		assertNull(fn.exec("id0"));
		final long after = System.currentTimeMillis();
		assertTrue(Math.abs(Math.abs(after - before) - 100) <= 10);
	}
	
	public void testSleepException() {
		final SleepFn<String, String> fn = MiscUtils.sleep(100);
		Thread.currentThread().interrupt();
		try {
			fn.exec("id0");
			fail();
		} catch (final IllegalStateException e) {
			assertTrue(e.getCause() instanceof InterruptedException);
			Thread.interrupted();
		}
	}

	public void testSwitchNearLocale() {
		{
			final Fn<Locale, String> switchable = MiscUtils.switchNearLocale(
					FnUtils.switching(Utils
					.map(Locale.ENGLISH, "e")
					.map(Locale.JAPANESE, "j"), "d"));
			assertEquals("e", switchable.exec(Locale.ENGLISH));
			assertEquals("e", switchable.exec(Locale.US));
			assertEquals("j", switchable.exec(Locale.JAPANESE));
			assertEquals("j", switchable.exec(Locale.JAPAN));
			assertEquals("d", switchable.exec(Locale.CHINESE));
			assertEquals("d", switchable.exec(null));
		}
		{
			final Fn<Locale, String> switchable = MiscUtils.switchNearLocale(
					new SwitchFn<Locale, String>(Utils
					.map(new Object(), "o")
					.map(Locale.ENGLISH, "e")
					.map(Locale.JAPANESE, "j"), "d"));
			assertEquals("e", switchable.exec(Locale.ENGLISH));
			assertEquals("e", switchable.exec(Locale.US));
			assertEquals("j", switchable.exec(Locale.JAPANESE));
			assertEquals("j", switchable.exec(Locale.JAPAN));
			assertEquals("d", switchable.exec(Locale.CHINESE));
			assertEquals("d", switchable.exec(null));
		}
		{
			final Fn<Locale, String> switchable = MiscUtils.switchNearLocale(
					FnUtils.switching(Utils
					.map(Locale.ENGLISH, "e")
					.map(Locale.JAPANESE, "j")
					.map(new Locale("ja", "JP", "POSIX"), "p"), "d"));
			assertEquals("e", switchable.exec(Locale.ENGLISH));
			assertEquals("e", switchable.exec(Locale.US));
			assertEquals("j", switchable.exec(Locale.JAPANESE));
			assertEquals("j", switchable.exec(Locale.JAPAN));
			assertEquals("d", switchable.exec(Locale.CHINESE));
			assertEquals("d", switchable.exec(null));
			assertEquals("p", switchable.exec(new Locale("ja", "JP", "POSIX")));
		}
		{
			final Fn<Locale, String> switchable = MiscUtils.switchNearLocale(
					FnUtils.switching(Utils
					.map(Locale.JAPAN, "japan")
					.map(Locale.JAPANESE, "japanese")
					.map(new Locale("ja", "JP", "POSIX"), "p"), "d"));
			assertEquals("p", switchable.exec(new Locale("ja", "JP", "POSIX")));
			assertEquals("japan", switchable.exec(new Locale("ja", "JP", "WIN")));
			assertEquals("japan", switchable.exec(new Locale("ja", "JP")));
			assertEquals("japanese", switchable.exec(new Locale("ja")));
			// assertEquals("japanese", switchable.get(new Locale("ja", "US")));
			assertEquals("d", switchable.exec(new Locale("en")));
		}
		{
			final Fn<Locale, String> switchable = MiscUtils.switchNearLocale(
					FnUtils.switching(Utils
					.map(Locale.US, "e")
					.map(Locale.JAPAN, "j"), "d"));
			assertEquals("d", switchable.exec(Locale.ENGLISH));
			assertEquals("e", switchable.exec(Locale.US));
			assertEquals("d", switchable.exec(Locale.JAPANESE));
			assertEquals("j", switchable.exec(Locale.JAPAN));
			assertEquals("d", switchable.exec(Locale.CHINESE));
			assertEquals("d", switchable.exec(null));
		}
	}
	
	public void testThreadLocal() {
		final ThreadLocal<String> threadLocal = new ThreadLocal<String>();
		final Fn<Object, String> fn = MiscUtils.threadLocal(threadLocal);
		assertNull(fn.exec(0));
		threadLocal.set("value0");
		assertEquals("value0", fn.exec(0));
	}

	public void testTryLocales() {
		final ThreadLocal<Locale> threadLocal = new ThreadLocal<Locale>();
		final List<String> result = new ArrayList<String>();
		final Object o = new Object();
		final Fn<String, Object> expression = MiscUtils.tryLocales(
				FnUtils.recode(FnUtils.fix(o), "", result),
				threadLocal);
		threadLocal.set(Locale.JAPANESE);
		assertSame(o, expression.exec("id0"));
	}
	
	public void testTryLocalesSearch() {
		final ThreadLocal<Locale> threadLocal = new ThreadLocal<Locale>();
		final List<String> result = new ArrayList<String>();
		final Fn<String, Object> expression = MiscUtils.tryLocales(
				FnUtils.recode(FnUtils.fix(null), result, null), threadLocal);

		final Locale defaultLocale = Locale.getDefault();
		try {
			Locale.setDefault(new Locale("ja", "JP", "POSIX"));

			result.clear();
			threadLocal.set(new Locale("en", "US", "WIN"));
			try {
				expression.exec("id0");
				fail();
			} catch (final IllegalStateException expected) {
			}
			assertEquals(Arrays.asList(
					"id0_en_US_WIN",
					"id0_en_US",
					"id0_en",
					"id0_ja_JP_POSIX",
					"id0_ja_JP",
					"id0_ja",
					"id0"), result);

			result.clear();
			threadLocal.set(null);
			try {
				expression.exec("id0");
				fail();
			} catch (final IllegalStateException expected) {
			}
			assertEquals(Arrays.asList("id0_ja_JP_POSIX", "id0_ja_JP", "id0_ja", "id0"), result);
		} finally {
			Locale.setDefault(defaultLocale);
		}
	}

	public void testTryLocalesIOException() {
		final ThreadLocal<Locale> threadLocal = new ThreadLocal<Locale>();
		final RuntimeException exception = new IllegalStateException();
		final Fn<String, Object> expression = MiscUtils.tryLocales(
				FnUtils.throwing(exception), threadLocal);
		threadLocal.set(Locale.JAPANESE);
		try {
			expression.exec("id0");
			fail();
		} catch (final IllegalStateException expected) {
			assertSame(exception, expected);
		}
	}

	public void testLoadBalancerException() {
		MiscUtils.loadBalancer(new NullFn<Object, Fn<Object,?>>(), 1);
		try {
			MiscUtils.loadBalancer(new NullFn<Object, Fn<Object,?>>(), 0);
			fail();
		} catch (final IllegalArgumentException expected) {
		}
	}
}
