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

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;

import junit.framework.TestCase;
import woolpack.convert.ConvertUtils;
import woolpack.el.PropertyEL;
import woolpack.fn.Fn;
import woolpack.fn.FnUtils;
import woolpack.fn.NullFn;
import woolpack.id.IdUtils;
import woolpack.id.IdContext;
import woolpack.id.SimpleIdContext;
import woolpack.test.TestUtils;
import woolpack.utils.Utils;

public class FactoryUtilsTest extends TestCase {

	public void testInputStreamReaderFactory() throws IOException {
		{
			final Fn<String, Reader> factory = FactoryUtils.inputStreamReaderFactory(
					FactoryUtils.stringInputStreamFactory("US-ASCII", "hoge"),
					"US-ASCII");
			assertEquals("hoge", TestUtils.toString(factory.exec(null)));
		}
		{
			final Fn<String, Reader> factory = FactoryUtils.inputStreamReaderFactory(
					FactoryUtils.stringInputStreamFactory(null, "hoge"), null);
			assertEquals("hoge", TestUtils.toString(factory.exec(null)));
		}
		{
			final Fn<String, Reader> factory = FactoryUtils.inputStreamReaderFactory(
					FactoryUtils.stringInputStreamFactory("UTF-8", "hoあge"), "UTF-8");
			assertEquals("hoあge", TestUtils.toString(factory.exec(null)));
		}
		{
			final Fn<String, Reader> factory = FactoryUtils.inputStreamReaderFactory(
					new NullFn<String, InputStream>(), null);
			assertNull(factory.exec(null));
		}
	}

	public void testInputStreamReaderFactoryIOException() {
		final Fn<String, Reader> factory = FactoryUtils.inputStreamReaderFactory(
				FactoryUtils.stringInputStreamFactory("US-ASCII", "hoge"), "US-ASCIIa");
		try {
			factory.exec(null);
			fail();
		} catch (final IllegalStateException e) {
			assertTrue(e.getCause() instanceof UnsupportedEncodingException);
		}
	}

	public void testStringReaderFactory() throws IOException {
		final Fn<Object, StringReader> factory = FactoryUtils.stringReaderFactory("hoge");
		assertEquals("hoge", TestUtils.toString(factory.exec(null)));
	}

	public void testStringInputStreamFactoryException() throws IOException {
		final Fn<Object, InputStream> factory = FactoryUtils.stringInputStreamFactory("US-ASCIIa", "hoge");
		try {
			factory.exec(null);
			fail();
		} catch (final IllegalStateException e) {
			assertTrue(e.getCause() instanceof UnsupportedEncodingException);
		}
	}


	public void testMapCache() {
		final List<String> list = new ArrayList<String>();
		final Map<String, Object> map = new HashMap<String, Object>();

		final MapCache<String, String> fn = FactoryUtils.cache(
				FnUtils.fix(map),
				ConvertUtils.convertRegExp(Pattern.compile("(.+)"), "&$1&"),
				FnUtils.recode(ConvertUtils.convertRegExp(Pattern.compile("(.+)"), "%$1%"), "a", list));
		
		assertEquals("%id0%", fn.exec("id0"));
		assertEquals(1, list.size());
		assertEquals("%id0%", fn.exec("id0"));
		assertEquals(1, list.size());
		assertEquals("%id1%", fn.exec("id1"));
		assertEquals(2, list.size());
		assertEquals("%id0%", fn.exec("id0"));
		assertEquals(2, list.size());
		assertEquals(Utils.map("&id0&", "%id0%").map("&id1&", "%id1%"), map);
	}
	
	public void testConcurrentMapCache() {
		final List<String> list = new ArrayList<String>();
		final ConcurrentMap<String, Object> map = new ConcurrentHashMap<String, Object>();
		final Fn<String, Object> fn = FactoryUtils.concurrentCache(
				FnUtils.fix(map),
				ConvertUtils.convertRegExp(Pattern.compile("(.+)"), "&$1&"),
				FnUtils.recode(ConvertUtils.convertRegExp(Pattern.compile("(.+)"), "%$1%"), "a", list));
		
		assertEquals("%id0%", fn.exec("id0"));
		assertEquals(1, list.size());
		assertEquals("%id0%", fn.exec("id0"));
		assertEquals(1, list.size());
		assertEquals("%id1%", fn.exec("id1"));
		assertEquals(2, list.size());
		assertEquals("%id0%", fn.exec("id0"));
		assertEquals(2, list.size());
		assertEquals(Utils.map("&id0&", "%id0%").map("&id1&", "%id1%"), new HashMap<String, Object>(map));
	}
	
	public void testSideEffectMapCache() {
		final List<String> list = new ArrayList<String>();
		final Map<String, Object> map = new HashMap<String, Object>();

		final Fn<IdContext, Void> fn = FactoryUtils.cache(
				FnUtils.fix(map),
				IdUtils.GET_ID,
				FnUtils.recode(IdUtils.convertId(ConvertUtils.convertRegExp(Pattern.compile("(.+)"), "%$1%")), "a", list),
				new PropertyEL("id"));
		final SimpleIdContext context = new SimpleIdContext();

		context.setId("id0");
		assertNull(fn.exec(context));
		assertEquals("%id0%", context.getId());
		assertEquals(1, list.size());

		context.setId("id0");
		assertNull(fn.exec(context));
		assertEquals("%id0%", context.getId());
		assertEquals(1, list.size());

		context.setId("id1");
		assertNull(fn.exec(context));
		assertEquals("%id1%", context.getId());
		assertEquals(2, list.size());

		context.setId("id0");
		assertNull(fn.exec(context));
		assertEquals("%id0%", context.getId());
		assertEquals(2, list.size());
		
		assertEquals(2, list.size());
		assertEquals(Utils.map("id0", "%id0%").map("id1", "%id1%"), map);
	}
	
	public void testSideEffectConcurrentMapCache() {
		final List<String> list = new ArrayList<String>();
		final ConcurrentMap<String, Object> map = new ConcurrentHashMap<String, Object>();

		final Fn<IdContext, Void> fn = FactoryUtils.concurrentCache(
				FnUtils.fix(map),
				IdUtils.GET_ID,
				FnUtils.recode(IdUtils.convertId(ConvertUtils.convertRegExp(Pattern.compile("(.+)"), "%$1%")), "a", list),
				new PropertyEL("id"));
		final SimpleIdContext context = new SimpleIdContext();

		context.setId("id0");
		assertNull(fn.exec(context));
		assertEquals("%id0%", context.getId());
		assertEquals(1, list.size());

		context.setId("id0");
		assertNull(fn.exec(context));
		assertEquals("%id0%", context.getId());
		assertEquals(1, list.size());

		context.setId("id1");
		assertNull(fn.exec(context));
		assertEquals("%id1%", context.getId());
		assertEquals(2, list.size());

		context.setId("id0");
		assertNull(fn.exec(context));
		assertEquals("%id0%", context.getId());
		assertEquals(2, list.size());
		
		assertEquals(2, list.size());
		assertEquals(Utils.map("id0", "%id0%").map("id1", "%id1%"), map);
	}
}
