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

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;

import junit.framework.TestCase;
import woolpack.fn.Fn;
import woolpack.fn.FnUtils;

public class BoolUtilsTest extends TestCase {

	public void testNotThrowable() {
		final Fn<Object, Boolean, RuntimeException> matcher = BoolUtils.NOT_THROWABLE;
		assertTrue(matcher.exec(null));
		assertTrue(matcher.exec("a"));
		assertTrue(matcher.exec("b"));
		assertFalse(matcher.exec(new Throwable()));
		assertFalse(matcher.exec(new Exception()));
		assertFalse(matcher.exec(new RuntimeException()));
		assertFalse(matcher.exec(new IndexOutOfBoundsException()));
		assertFalse(matcher.exec(new ArrayIndexOutOfBoundsException()));
	}
	
	public static <C> void scenario(
			final C c,
			final Fn<Object, ? extends BooleanState, ? extends RuntimeException> operator,
			final boolean[] input, 
			final boolean expectedResult,
			final List<String> expectedCallList) {
		final List<String> callList = new ArrayList<String>();
		final List<C> paramList = new ArrayList<C>();
		
		final List<Fn<C, Boolean, RuntimeException>> list = new ArrayList<Fn<C, Boolean, RuntimeException>>();
		for (int i = 0; i < input.length; i++) {
			list.add(FnUtils.recode(
					input[i] ? FnUtils.<C, Boolean>fix(true) : FnUtils.<C, Boolean>fix(false),
					String.valueOf(i), callList, paramList, null));
		}
		
		assertEquals(Boolean.valueOf(expectedResult), BoolUtils.boolSeq(operator, list).exec(c));
		assertEquals(expectedCallList, callList);
		for (final C context : paramList) {
			assertSame(c, context);
		}
	}
	
	public void testAndand() throws IOException {
		final Fn<Object, ? extends BooleanState, ? extends RuntimeException> operator = BoolUtils.ANDAND;
		final Object target = new Object();
		scenario(target, operator, new boolean[0], true, new ArrayList<String>());
		scenario(target, operator, new boolean[] { true }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { false }, false, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { true, true }, true, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { true, false }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, true }, false, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { false, false }, false, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { true, true, true }, true, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, true, false }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, false, true }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { true, false, false }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, true, true }, false, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { false, true, false }, false, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { false, false, true }, false, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { false, false, false }, false, Arrays.asList("0"));
	}

	public void testAnd() throws IOException {
		final Fn<Object, ? extends BooleanState, ? extends RuntimeException> operator = BoolUtils.AND;
		final Object target = new Object();
		scenario(target, operator, new boolean[0], true, new ArrayList<String>());
		scenario(target, operator, new boolean[] { true }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { false }, false, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { true, true }, true, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { true, false }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, true }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, false }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { true, true, true }, true, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, true, false }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, false, true }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, false, false }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, true, true }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, true, false }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, false, true }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, false, false }, false, Arrays.asList("0", "1", "2"));
	}
	
	public void testEqeq() throws IOException {
		final Fn<Object, ? extends BooleanState, ? extends RuntimeException> operator = BoolUtils.EQEQ;
		final Object target = new Object();
		scenario(target, operator, new boolean[0], true, new ArrayList<String>());
		scenario(target, operator, new boolean[] { true }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { false }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { true, true }, true, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { true, false }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, true }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, false }, true, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { true, true, true }, true, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, true, false }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, false, true }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { true, false, false }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, true, true }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, true, false }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, false, true }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, false, false }, true, Arrays.asList("0", "1", "2"));
	}
	
	public void testEq() throws IOException {
		final Fn<Object, ? extends BooleanState, ? extends RuntimeException> operator = BoolUtils.EQ;
		final Object target = new Object();
		scenario(target, operator, new boolean[0], true, new ArrayList<String>());
		scenario(target, operator, new boolean[] { true }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { false }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { true, true }, true, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { true, false }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, true }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, false }, true, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { true, true, true }, true, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, true, false }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, false, true }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, false, false }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, true, true }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, true, false }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, false, true }, false, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, false, false }, true, Arrays.asList("0", "1", "2"));
	}
	
	public void testOror() throws IOException {
		final Fn<Object, ? extends BooleanState, ? extends RuntimeException> operator = BoolUtils.OROR;
		final Object target = new Object();
		scenario(target, operator, new boolean[0], false, new ArrayList<String>());
		scenario(target, operator, new boolean[] { true }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { false }, false, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { true, true }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { true, false }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { false, true }, true, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, false }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { true, true, true }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { true, true, false }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { true, false, true }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { true, false, false }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { false, true, true }, true, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, true, false }, true, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, false, true }, true, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, false, false }, false, Arrays.asList("0", "1", "2"));
	}

	public void testOr() throws IOException {
		final Fn<Object, ? extends BooleanState, ? extends RuntimeException> operator = BoolUtils.OR;
		final Object target = new Object();
		scenario(target, operator, new boolean[0], false, new ArrayList<String>());
		scenario(target, operator, new boolean[] { true }, true, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { false }, false, Arrays.asList("0"));
		scenario(target, operator, new boolean[] { true, true }, true, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { true, false }, true, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, true }, true, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { false, false }, false, Arrays.asList("0", "1"));
		scenario(target, operator, new boolean[] { true, true, true }, true, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, true, false }, true, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, false, true }, true, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { true, false, false }, true, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, true, true }, true, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, true, false }, true, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, false, true }, true, Arrays.asList("0", "1", "2"));
		scenario(target, operator, new boolean[] { false, false, false }, false, Arrays.asList("0", "1", "2"));
	}
	
	public void testToBoolean() {
		final Fn<Object, Boolean, RuntimeException> fn = BoolUtils.TO_BOOLEAN;
		assertFalse(fn.exec(null));
		assertFalse(fn.exec(false));
		assertTrue(fn.exec(true));
		assertTrue(fn.exec(new Object()));
	}
	
	public void testCompare() {
		final Fn<Integer, Integer, RuntimeException> fn = BoolUtils.compare(1025);
		assertEquals(Integer.valueOf(0), fn.exec(1025));
		assertEquals(Integer.valueOf(-1), fn.exec(1027));
		assertEquals(Integer.valueOf(1), fn.exec(1023));
	}

	public void testContainsAll() {
		final List<String> list = Arrays.asList("id0", "id1");
		final Fn<Collection<?>, Boolean, RuntimeException> fn = BoolUtils.containsAll(list);
		assertFalse(fn.exec(Arrays.asList("id0", "id2")));
		assertTrue(fn.exec(Arrays.asList("id0", "id1")));
	}

	public void testContains() {
		final List<String> list = Arrays.asList("id0", "id1");
		final Fn<Object, Boolean, RuntimeException> fn = BoolUtils.contains(list);
		assertFalse(fn.exec("id2"));
		assertTrue(fn.exec("id1"));
	}

	public void testCheckEquals() {
		final Fn<Object, Boolean, RuntimeException> fn = BoolUtils.checkEquals("a");
		assertTrue(fn.exec("a"));
		assertFalse(fn.exec("b"));
	}

	public void testCheckMax() {
		final Fn<Integer, Boolean, RuntimeException> fn = BoolUtils.checkMax(1025);
		assertTrue(fn.exec(1024));
		assertTrue(fn.exec(1025));
		assertFalse(fn.exec(1026));
	}

	public void testCheckMaxLength() {
		final MaxLengthChecker fn = BoolUtils.checkMaxLength(5);
		assertTrue(fn.exec("aaaa"));
		assertTrue(fn.exec("aaaaa"));
		assertFalse(fn.exec("aaaaaa"));
	}

	public void testCheckMin() {
		final Fn<Integer, Boolean, RuntimeException> fn = BoolUtils.checkMin(1025);
		assertFalse(fn.exec(1024));
		assertTrue(fn.exec(1025));
		assertTrue(fn.exec(1026));
	}

	public void testCheckMinLength() {
		final MinLengthChecker fn = BoolUtils.checkMinLength(5);
		assertFalse(fn.exec("aaaa"));
		assertTrue(fn.exec("aaaaa"));
		assertTrue(fn.exec("aaaaaa"));
	}

	public void testNot() {
		final Fn<String, Boolean, RuntimeException> fn = 
			BoolUtils.not(BoolUtils.checkRegExp(Pattern.compile("a.+b")));
		assertEquals(Boolean.FALSE, fn.exec("acb"));
		assertEquals(Boolean.TRUE, fn.exec("acB"));
	}

	public void testNotEmpty() {
		final Fn<Object, Boolean, RuntimeException> fn = BoolUtils.NOT_EMPTY;
		assertFalse(fn.exec(null));
		assertFalse(fn.exec(""));
		assertTrue(fn.exec("a"));
		assertTrue(fn.exec(new Object()));
	}

	public void testNotFn() {
		final Fn<Boolean, Boolean, RuntimeException> fn = BoolUtils.NOT;
		assertTrue(fn.exec(false));
		assertFalse(fn.exec(true));
	}

	public void testMatchObject() {
		final Fn<Object, Boolean, RuntimeException> matcher = BoolUtils.matchObject("a");
		assertFalse(matcher.exec(null));
		assertTrue(matcher.exec("a"));
		assertFalse(matcher.exec("b"));
		assertFalse(matcher.exec(new Exception()));
		assertFalse(matcher.exec(new RuntimeException()));
		assertFalse(matcher.exec(new IndexOutOfBoundsException()));
		assertFalse(matcher.exec(new ArrayIndexOutOfBoundsException()));
	}

	public void testMatchObjectNull() {
		final Fn<Object, Boolean, RuntimeException> matcher = BoolUtils.matchObject(null);
		assertTrue(matcher.exec(null));
		assertFalse(matcher.exec("a"));
		assertFalse(matcher.exec("b"));
		assertFalse(matcher.exec(new Exception()));
		assertFalse(matcher.exec(new RuntimeException()));
		assertFalse(matcher.exec(new IndexOutOfBoundsException()));
		assertFalse(matcher.exec(new ArrayIndexOutOfBoundsException()));
	}

	public void testMatchObjectClass() {
		final Fn<Object, Boolean, RuntimeException> matcher = BoolUtils.matchObject(IndexOutOfBoundsException.class);
		assertFalse(matcher.exec(null));
		assertFalse(matcher.exec("a"));
		assertFalse(matcher.exec("b"));
		assertFalse(matcher.exec(new Exception()));
		assertFalse(matcher.exec(new RuntimeException()));
		assertTrue(matcher.exec(new IndexOutOfBoundsException()));
		assertTrue(matcher.exec(new ArrayIndexOutOfBoundsException()));
	}
	
	public void testCheckRegExp() {
		final Fn<String, Boolean, RuntimeException> fn = BoolUtils.checkRegExp(Pattern.compile("a(.*)a"));
		assertTrue(fn.exec("a234a"));
		assertFalse(fn.exec("a234b"));
	}

	public void testIfTrueOnly() {
		assertEquals(Boolean.TRUE, BoolUtils.ifTrue(FnUtils.fix(true), FnUtils.fix(true)).exec(null));
		assertEquals(Boolean.FALSE, BoolUtils.ifTrue(FnUtils.fix(true), FnUtils.fix(false)).exec(null));
		assertEquals(Boolean.FALSE, BoolUtils.ifTrue(FnUtils.fix(false), FnUtils.fix(true)).exec(null));
		assertEquals(Boolean.FALSE, BoolUtils.ifTrue(FnUtils.fix(false), FnUtils.fix(false)).exec(null));
	}

	public void testIfNot() {
		assertEquals(Boolean.TRUE, BoolUtils.ifNot(FnUtils.fix(true), FnUtils.fix(true)).exec(null));
		assertEquals(Boolean.TRUE, BoolUtils.ifNot(FnUtils.fix(true), FnUtils.fix(false)).exec(null));
		assertEquals(Boolean.TRUE, BoolUtils.ifNot(FnUtils.fix(false), FnUtils.fix(true)).exec(null));
		assertEquals(Boolean.FALSE, BoolUtils.ifNot(FnUtils.fix(false), FnUtils.fix(false)).exec(null));
	}
}
