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

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import javax.xml.transform.TransformerException;

import junit.framework.TestCase;

import org.w3c.dom.Node;

import woolpack.fn.FixFn;
import woolpack.fn.FnUtils;
import woolpack.utils.Utils;
import woolpack.validator.ValidatorUtils;
import woolpack.xml.XmlTransformerUtils;
import woolpack.xml.XmlUtils;

public class VisitorAppUtilsTest extends TestCase {
	public void testNotEmpty() {
		final Visitor<List<Object>, RuntimeException> visitor = new Visitor<List<Object>, RuntimeException>();
		visitor.setMap(VisitorAppUtils.CHECK_NOT_EMPTY);
		
		try {
			visitor.visit(null);
			fail();
		} catch (final IllegalStateException e) {
		}
		try {
			visitor.visit("");
			fail();
		} catch (final IllegalStateException e) {
		}
		try {
			visitor.visit(new String[]{"a", null, "b"});
			fail();
		} catch (final IllegalStateException e) {
		}
		try {
			visitor.visit(new String[]{"a", "", "b"});
			fail();
		} catch (final IllegalStateException e) {
		}
		try {
			visitor.visit(Arrays.asList("a", null, "a"));
			fail();
		} catch (final IllegalStateException e) {
		}
		try {
			visitor.visit(Arrays.asList("a", "", "a"));
			fail();
		} catch (final IllegalStateException e) {
		}
		try {
			visitor.visit(Utils.map(null, "b"));
			fail();
		} catch (final IllegalStateException e) {
		}
		try {
			visitor.visit(Utils.map("", "b"));
			fail();
		} catch (final IllegalStateException e) {
		}
		try {
			visitor.visit(Utils.map("a", null));
			fail();
		} catch (final IllegalStateException e) {
		}
		try {
			visitor.visit(Utils.map("a", ""));
			fail();
		} catch (final IllegalStateException e) {
		}
		try {
			visitor.visit(FnUtils.fix(null));
			fail();
		} catch (final IllegalStateException e) {
		}
		try {
			visitor.visit(FnUtils.fix(""));
			fail();
		} catch (final IllegalStateException e) {
		}
		
		visitor.visit("a");
		visitor.visit(Integer.valueOf(1));
		visitor.visit(new int[]{1, 2});
		visitor.visit(new String[]{"a", "b"});
		visitor.visit(Arrays.asList("a", "b"));
		visitor.visit(Utils.map("a", "b"));
		visitor.visit(FnUtils.fix("a"));
	}
	
	public void testNotEmpty2() {
		final Visitor<List<Object>, RuntimeException> visitor = new Visitor<List<Object>, RuntimeException>();
		visitor.setMap(VisitorAppUtils.CHECK_NOT_EMPTY);
		
		try {
			visitor.setSubContext(new ArrayList<Object>());
			visitor.visit(null);
			fail();
		} catch (final IllegalStateException e) {
			assertTrue(visitor.getSubContext().isEmpty());
		}
		try {
			visitor.setSubContext(new ArrayList<Object>());
			visitor.visit("");
			fail();
		} catch (final IllegalStateException e) {
			assertTrue(visitor.getSubContext().isEmpty());
		}
		try {
			visitor.setSubContext(new ArrayList<Object>());
			visitor.visit(new String[]{"a", null, "b"});
			fail();
		} catch (final IllegalStateException e) {
			assertEquals(1, visitor.getSubContext().size());
			assertTrue(visitor.getSubContext().get(0) instanceof String[]);
		}
		try {
			visitor.setSubContext(new ArrayList<Object>());
			visitor.visit(new String[]{"a", "", "b"});
			fail();
		} catch (final IllegalStateException e) {
			assertEquals(1, visitor.getSubContext().size());
			assertTrue(visitor.getSubContext().get(0) instanceof String[]);
		}
		try {
			visitor.setSubContext(new ArrayList<Object>());
			visitor.visit(Arrays.asList("a", null, "a"));
			fail();
		} catch (final IllegalStateException e) {
			assertEquals(1, visitor.getSubContext().size());
			assertTrue(visitor.getSubContext().get(0) instanceof List);
		}
		try {
			visitor.setSubContext(new ArrayList<Object>());
			visitor.visit(Arrays.asList("a", "", "a"));
			fail();
		} catch (final IllegalStateException e) {
			assertEquals(1, visitor.getSubContext().size());
			assertTrue(visitor.getSubContext().get(0) instanceof List);
		}
		try {
			visitor.setSubContext(new ArrayList<Object>());
			visitor.visit(Utils.map(null, "b"));
			fail();
		} catch (final IllegalStateException e) {
			assertEquals(1, visitor.getSubContext().size());
			assertTrue(visitor.getSubContext().get(0) instanceof Map);
		}
		try {
			visitor.setSubContext(new ArrayList<Object>());
			visitor.visit(Utils.map("", "b"));
			fail();
		} catch (final IllegalStateException e) {
			assertEquals(1, visitor.getSubContext().size());
			assertTrue(visitor.getSubContext().get(0) instanceof Map);
		}
		try {
			visitor.setSubContext(new ArrayList<Object>());
			visitor.visit(Utils.map("a", null));
			fail();
		} catch (final IllegalStateException e) {
			assertEquals(1, visitor.getSubContext().size());
			assertTrue(visitor.getSubContext().get(0) instanceof Map);
		}
		try {
			visitor.setSubContext(new ArrayList<Object>());
			visitor.visit(Utils.map("a", ""));
			fail();
		} catch (final IllegalStateException e) {
			assertEquals(1, visitor.getSubContext().size());
			assertTrue(visitor.getSubContext().get(0) instanceof Map);
		}
		try {
			visitor.setSubContext(new ArrayList<Object>());
			visitor.visit(FnUtils.fix(null));
			fail();
		} catch (final IllegalStateException e) {
			assertEquals(1, visitor.getSubContext().size());
			assertTrue(visitor.getSubContext().get(0) instanceof FixFn);
		}
		try {
			visitor.setSubContext(new ArrayList<Object>());
			visitor.visit(FnUtils.fix(""));
			fail();
		} catch (final IllegalStateException e) {
			assertEquals(1, visitor.getSubContext().size());
			assertTrue(visitor.getSubContext().get(0) instanceof FixFn);
		}
		
		visitor.visit("a");
		visitor.visit(Integer.valueOf(1));
		visitor.visit(new int[]{1, 2});
		visitor.visit(new String[]{"a", "b"});
		visitor.visit(Arrays.asList("a", "b"));
		visitor.visit(Utils.map("a", "b"));
		visitor.visit(FnUtils.fix("a"));
	}
	
	private void scenarioJs(final String expect, final Object object) {
		final Visitor<StringBuilder, RuntimeException> visitor = new Visitor<StringBuilder, RuntimeException>();
		visitor.setMap(VisitorAppUtils.JS);
		visitor.setSubContext(new StringBuilder());
		visitor.visit(object);
		assertEquals(expect, visitor.getSubContext().toString());
	}
	
	public void testJs() {
		scenarioJs("null", null);
		scenarioJs("null", Integer.class);
		scenarioJs("\"\"", "");
		scenarioJs("\"\\\n\\\r\\\t\\\"\\\'a\"", "\n\r\t\"\'a");
		scenarioJs("\"a\"", 'a');
		scenarioJs("4", 4);
		scenarioJs("true", true);
		scenarioJs("false", false);
		scenarioJs("{\"a\":\"b\"}", Utils.map("a", "b"));
		scenarioJs("{\"a\":\"b\",\"c\":\"d\"}", Utils.map("a", "b").map("c", "d"));
		scenarioJs("[\"a\",\"b\"]", Arrays.asList("a", "b"));
		scenarioJs("[\"a\",\"b\"]", new String[]{"a", "b"});
		scenarioJs("[1,2]", new int[]{1, 2});
		scenarioJs("new FixFn(\"a\")", FnUtils.<Object, Object>fix("a"));
		scenarioJs("new JoinFn(new FixFn(\"a\"),new FixFn(\"b\"))", FnUtils.join(
				FnUtils.<Object, Object>fix("a"),
				FnUtils.<Object, Object>fix("b")));
		scenarioJs("new SimpleDateFormat(\"yyyyMMddHH\")",
				new SimpleDateFormat("yyyyMMddHH"));
		scenarioJs("new DecimalFormat(\"#,##0.###\")",
				new DecimalFormat());
		scenarioJs("new RegExp(\"^(.*)$\",\"g\")",
				Pattern.compile("^(.*)$"));
	}
	
	private void scenarioDoc(final String expect, final Object object) throws TransformerException {
		final Visitor<Node, RuntimeException> visitor = new Visitor<Node, RuntimeException>();
		visitor.setMap(VisitorAppUtils.DOC);
		visitor.setSubContext(XmlTransformerUtils.convert("<HTML><BODY></BODY></HTML>").getFirstChild().getFirstChild());
		visitor.visit(object);
		visitor.getSubContext().normalize();
		final Node expected = XmlTransformerUtils.convert("<HTML><BODY>" + expect + "</BODY></HTML>").getFirstChild().getFirstChild();
		if (!XmlUtils.equalsNode(expected, visitor.getSubContext())) {
			System.out.println(XmlTransformerUtils.convert(expected));
			System.out.println(XmlTransformerUtils.convert(visitor.getSubContext()));
			fail();
		}
	}
	
	public void testDoc() throws TransformerException {
		scenarioDoc("null", null);
		scenarioDoc("", "");
		scenarioDoc("a", "a");
		scenarioDoc("a", 'a');
		scenarioDoc("4", 4);
		scenarioDoc("true", true);
		scenarioDoc("false", false);
		scenarioDoc("<dl><dt>a</dt><dd>b</dd></dl>", Utils.map("a", "b"));
		scenarioDoc("<dl><dt>a</dt><dd>b</dd><dt>c</dt><dd>d</dd></dl>", Utils.map("a", "b").map("c", "d"));
		scenarioDoc("<ol><li>a</li><li>b</li></ol>", Arrays.asList("a", "b"));
		scenarioDoc("<ol><li>a</li><li>b</li></ol>", new String[]{"a", "b"});
		scenarioDoc("<ol><li>1</li><li>2</li></ol>", new int[]{1, 2});
		scenarioDoc("FixFn<dl><dt>value</dt><dd>a</dd></dl>", FnUtils.<Object, Object>fix("a"));
		scenarioDoc("SimpleDateFormat<dl><dt>pattern</dt><dd>yyyyMMddHH</dd></dl>",
				new SimpleDateFormat("yyyyMMddHH"));
		scenarioDoc("DecimalFormat<dl><dt>pattern</dt><dd>#,##0.###</dd></dl>",
				new DecimalFormat());
		scenarioDoc("RegExp<dl><dt>pattern</dt><dd>^(.*)$</dd></dl>",
				Pattern.compile("^(.*)$"));
	}

	private void scenarioMessage(
			final Collection<String> expected,
			final Object object) {
		final Visitor<Collection<String>, RuntimeException> visitor = new Visitor<Collection<String>, RuntimeException>();
		visitor.setMap(VisitorAppUtils.MESSAGE);
		visitor.setSubContext(new ArrayList<String>());
		visitor.visit(object);
		assertEquals(expected, visitor.getSubContext());
	}
	
	public void testMessage() {
		scenarioMessage(new ArrayList<String>(), null);
		scenarioMessage(new ArrayList<String>(), "a");
		scenarioMessage(new ArrayList<String>(), Integer.valueOf(1));
		scenarioMessage(new ArrayList<String>(), new int[]{1, 2});
		scenarioMessage(new ArrayList<String>(), new String[]{"a", "b"});
		scenarioMessage(new ArrayList<String>(), Arrays.asList("a", "b"));
		scenarioMessage(new ArrayList<String>(), Utils.map("a", "b"));
		scenarioMessage(new ArrayList<String>(), FnUtils.fix("a"));
		scenarioMessage(Arrays.asList("message0"), ValidatorUtils.message("message0"));
		scenarioMessage(Arrays.asList("message0"), FnUtils.fix(ValidatorUtils.message("message0")));

		scenarioMessage(Arrays.asList("message0"), new Object[]{
			"a",
			ValidatorUtils.message("message0")});
		scenarioMessage(Arrays.asList("message0", "message1"), new Object[]{
			"a",
			ValidatorUtils.message("message0"),
			ValidatorUtils.message("message1")});

		scenarioMessage(Arrays.asList("message0"), Arrays.asList(
				(Object) "a",
				ValidatorUtils.message("message0")));
		scenarioMessage(Arrays.asList("message0", "message1"), Arrays.asList(
				(Object) "a",
				ValidatorUtils.message("message0"),
				ValidatorUtils.message("message1")));
		
		scenarioMessage(Arrays.asList("message0"), Utils
				.map("a", ValidatorUtils.message("message0")));
		scenarioMessage(Arrays.asList("message0", "message1"), Utils
				.map("a", ValidatorUtils.message("message0"))
				.map("b", ValidatorUtils.message("message1")));
	}
}
