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

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import woolpack.fn.Fn;
import woolpack.utils.Utils;
import junit.framework.TestCase;

public class ConvertUtilsTest extends TestCase {
	
	public void testKeySet() {
		final Map<String, String> map = Utils.map("ka", "va");
		final Fn<Map<String, ?>, Set<String>, RuntimeException> fn = ConvertUtils.keySet();
		assertSame(map.keySet(), fn.exec(map));
	}
	
	public void testDate() {
		assertEquals(new java.sql.Date(2000), ConvertUtils.TO_SQL_DATE.exec(new java.util.Date(2000)));
		assertEquals(new java.sql.Date(2000), ConvertUtils.TO_SQL_DATE.exec(new java.sql.Date(2000)));
		
		assertEquals(new java.sql.Timestamp(2000), ConvertUtils.TO_TIMESTAMP.exec(new java.util.Date(2000)));
		assertEquals(new java.sql.Timestamp(2000), ConvertUtils.TO_TIMESTAMP.exec(new java.sql.Timestamp(2000)));

		assertEquals(new java.sql.Time(2000), ConvertUtils.TO_TIMESTAMP.exec(new java.util.Date(2000)));
		assertEquals(new java.sql.Time(2000), ConvertUtils.TO_TIMESTAMP.exec(new java.sql.Time(2000)));

		{
			final Calendar calendar = Calendar.getInstance();
			calendar.setTimeInMillis(2000);
			assertEquals(calendar, ConvertUtils.TO_CALENDAR.exec(new java.util.Date(2000)));
			assertEquals(calendar, ConvertUtils.TO_CALENDAR.exec(calendar));
		}
	}
	
	public void testNumber() {
		assertEquals(Byte.valueOf(Byte.MAX_VALUE), ConvertUtils.TO_BYTE.exec(Byte.MAX_VALUE));
		assertEquals(Byte.valueOf(Byte.MAX_VALUE), ConvertUtils.TO_BYTE.exec((long)Byte.MAX_VALUE));
		try {
			ConvertUtils.TO_BYTE.exec(1+Byte.MAX_VALUE);
			fail();
		} catch (final IllegalArgumentException e) {
		}
		try {
			ConvertUtils.TO_BYTE.exec(1.5);
			fail();
		} catch (final IllegalArgumentException e) {
		}

		assertEquals(Short.valueOf(Short.MAX_VALUE), ConvertUtils.TO_SHORT.exec(Short.MAX_VALUE));
		assertEquals(Short.valueOf(Short.MAX_VALUE), ConvertUtils.TO_SHORT.exec((long)Short.MAX_VALUE));
		try {
			ConvertUtils.TO_SHORT.exec(1+Short.MAX_VALUE);
			fail();
		} catch (final IllegalArgumentException e) {
		}
		try {
			ConvertUtils.TO_SHORT.exec(1.5);
			fail();
		} catch (final IllegalArgumentException e) {
		}

		assertEquals(Integer.valueOf(Integer.MAX_VALUE), ConvertUtils.TO_INTEGER.exec(Integer.MAX_VALUE));
		assertEquals(Integer.valueOf(Integer.MAX_VALUE), ConvertUtils.TO_INTEGER.exec((long)Integer.MAX_VALUE));
		try {
			ConvertUtils.TO_INTEGER.exec(1L+Integer.MAX_VALUE);
			fail();
		} catch (final IllegalArgumentException e) {
		}
		try {
			ConvertUtils.TO_INTEGER.exec(1.5);
			fail();
		} catch (final IllegalArgumentException e) {
		}

		assertEquals(Long.valueOf(Long.MAX_VALUE), ConvertUtils.TO_LONG.exec(Long.MAX_VALUE));
		assertEquals(Long.valueOf(Long.MAX_VALUE), ConvertUtils.TO_LONG.exec((double)Long.MAX_VALUE));
		try {
			ConvertUtils.TO_LONG.exec(1.5);
			fail();
		} catch (final IllegalArgumentException e) {
		}
		
		assertEquals(Float.valueOf(Float.MAX_VALUE), ConvertUtils.TO_FLOAT.exec(Float.MAX_VALUE));
		assertEquals(Float.valueOf(Float.MAX_VALUE), ConvertUtils.TO_FLOAT.exec((double)Float.MAX_VALUE));
		
		assertEquals(Double.valueOf(Double.MAX_VALUE), ConvertUtils.TO_DOUBLE.exec(Double.MAX_VALUE));
		assertEquals(Double.valueOf(Double.MAX_VALUE), ConvertUtils.TO_DOUBLE.exec(new BigDecimal(Double.MAX_VALUE)));
		
		assertEquals(new BigInteger("1"), ConvertUtils.TO_BIG_INTEGER.exec(new BigDecimal(1)));
		assertEquals(new BigInteger("1"), ConvertUtils.TO_BIG_INTEGER.exec(new BigInteger("1")));
		assertEquals(new BigInteger("1"), ConvertUtils.TO_BIG_INTEGER.exec(1));
		try {
			ConvertUtils.TO_BIG_INTEGER.exec(new BigDecimal(1.5));
			fail();
		} catch (final ArithmeticException e) {
		}
		try {
			ConvertUtils.TO_BIG_INTEGER.exec(1.5);
			fail();
		} catch (final ArithmeticException e) {
		}

		assertEquals(new BigDecimal(1), ConvertUtils.TO_BIG_DECIMAL.exec(new BigDecimal(1)));
		assertEquals(new BigDecimal(1), ConvertUtils.TO_BIG_DECIMAL.exec(new BigInteger("1")));
		assertEquals(new BigDecimal(1), ConvertUtils.TO_BIG_DECIMAL.exec(1));
	}
	
	public void testFormatFactory() {
		final Format format0 = new SimpleDateFormat();
		final Format format1 = ConvertUtils.formatFactory(format0).exec(null);
		assertTrue(format1 instanceof SimpleDateFormat);
		assertNotSame(format0, format1);
	}

	public void testFormat() {
		assertEquals("3",
				ConvertUtils.format(ConvertUtils.formatFactory(new DecimalFormat()))
				.exec(Integer.valueOf(3)));
	}

	public void testNetMask() {
		{
			final Fn<String, String, RuntimeException> fn = ConvertUtils.netMask("255.255.255.0");
			assertEquals(null, fn.exec(null));
			assertEquals("192.168.0.0", fn.exec("192.168.0.1"));
			assertEquals("192.168.1.0", fn.exec("192.168.1.2"));
			assertEquals("192.168.255.0", fn.exec("192.168.255.3"));
		}
		{
			final Fn<String, String, RuntimeException> fn = ConvertUtils.netMask("255.255.240.0");
			assertEquals(null, fn.exec(null));
			assertEquals("192.168.0.0", fn.exec("192.168.0.1"));
			assertEquals("192.168.0.0", fn.exec("192.168.15.2"));
			assertEquals("192.168.16.0", fn.exec("192.168.16.3"));
			assertEquals("192.168.240.0", fn.exec("192.168.255.4"));
		}
		{
			final Fn<String, String, RuntimeException> fn = ConvertUtils.netMask("255.255.240.");
			try {
				fn.exec("192.168.0.1");
				fail();
			} catch (final NullPointerException e) {
			}
		}
	}

	public void testParse() throws Exception {
		final Fn<String, Object, Exception> fn =
			ConvertUtils.parse(ConvertUtils.formatFactory(new DecimalFormat()));
		assertEquals(Long.valueOf(3), fn.exec("3"));
		
		try {
			fn.exec("a3a");
			fail();
		} catch (final ParseException e) {
		}
		try {
			fn.exec("123,4r56.7");
			fail();
		} catch (final ParseException e) {
		}
	}
	
	public void testConvertRegExp() {
		assertEquals("234",
				ConvertUtils.convertRegExp(Pattern.compile("a(.*)a"), "$1")
				.exec("a234a"));
	}

	public void testToString() {
		assertNull(ConvertUtils.TO_STRING.exec(null));
		assertEquals("4", ConvertUtils.TO_STRING.exec(4));
	}

	public void testTrys() throws Exception {
		final Fn<String, Object, ? extends RuntimeException> fn = ConvertUtils.trys(
				Utils
				.list(ConvertUtils.parse(ConvertUtils.formatFactory(new SimpleDateFormat("yyyyMMddHHmmss"))))
				.list(ConvertUtils.parse(ConvertUtils.formatFactory(new SimpleDateFormat("yyyyMMdd"))))
		);

		final Date date0 = new SimpleDateFormat("yyyyMMddHHmmss").parse("20060601123157");
		final Date date1 = new SimpleDateFormat("yyyyMMdd").parse("20060601");
		
		assertEquals(date0, fn.exec("20060601123157"));
		assertEquals(date1, fn.exec("20060601"));
		try {
			fn.exec("a2006060");
			fail();
		} catch (final IllegalStateException e) {
			assertTrue(e.getCause() instanceof ParseException);
		}
	}

	public void testRetainKeys() {
		final Map<String, String> map = Utils.map("a", "1").map("c", "2").map("b", "3");
		
		ConvertUtils.retainKeys(Arrays.asList("a", "b")).exec(map);
		
		assertEquals(Utils.map("a", "1").map("b", "3"), map);
	}
}
