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

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Node;

import woolpack.action.ActionDefMaker;
import woolpack.action.ActionInvoker;
import woolpack.action.ForwardDef;
import woolpack.adapter.JXP;
import woolpack.adapter.OGE;
import woolpack.bool.BoolUtils;
import woolpack.config.ConfigUtils;
import woolpack.convert.ConvertUtils;
import woolpack.ee.ActionBuilder;
import woolpack.ee.EEContext;
import woolpack.ee.EEUtils;
import woolpack.ee.HttpSessionMap;
import woolpack.ee.ServletContextMap;
import woolpack.ee.ServletInputStreamFactory;
import woolpack.ee.ServletRequestAttributeMap;
import woolpack.ee.ValidatorBuilder;
import woolpack.factory.FactoryUtils;
import woolpack.fn.Delegator;
import woolpack.fn.Fn;
import woolpack.fn.FnUtils;
import woolpack.html.HtmlUtils;
import woolpack.id.IdConverter;
import woolpack.id.IdUtils;
import woolpack.misc.MiscUtils;
import woolpack.utils.BuildableArrayList;
import woolpack.utils.Utils;
import woolpack.validator.ValidatorUtils;
import woolpack.xml.NodeSetter;
import woolpack.xml.XmlTransformerContext;
import woolpack.xml.XmlTransformerUtils;
import woolpack.xml.XmlUtils;


/**
 * ロケールごとに挙動を変えるサンプルウェブアプリケーションです。
 * @author nakamura
 *
 */
public class LocaleServlet extends HttpServlet {

	public final transient ThreadLocal<Locale> threadLocal;

	public final transient Delegator<String, Node> toNode;
	public final transient Fn<EEContext, Void> fn;
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	
	public LocaleServlet() {
		super();
		threadLocal = new ThreadLocal<Locale>();
		
		final ValidatorBuilder validatorBuilder = new ValidatorBuilder(FnUtils.ifTrue(
				FnUtils.join(IdUtils.GET_ID, BoolUtils.checkRegExp(Pattern.compile("bean0_confirm"))),
				ValidatorUtils.branchByName(Utils.map("cache", BoolUtils.boolSeq(Utils
						.list(BoolUtils.ifNot(
								FnUtils.join(ValidatorUtils.VALUE, BoolUtils.NOT_EMPTY),
								ValidatorUtils.message("validator.cache.required")))
						.list(BoolUtils.ifNot(
								// ロケールごとに値検証を切り替えています。
								FnUtils.join(ValidatorUtils.VALUE, FnUtils.castTo(String.class), FnUtils.exec(FnUtils.join(
										MiscUtils.threadLocal(threadLocal),
										FnUtils.switching(Utils
												.map(Locale.JAPANESE, BoolUtils.checkRegExp(Pattern.compile("\\d{3}")))
												.map(Locale.ENGLISH, BoolUtils.checkRegExp(Pattern.compile("\\d{2}"))),
												BoolUtils.checkRegExp(Pattern.compile("\\d{4}")))))
								),
								ValidatorUtils.message("validator.cache.valid")))
						))), FnUtils.fix(true)));
		
		final ActionBuilder actionBuilder = new ActionBuilder(
				new ActionInvoker(
						FnUtils.switching(new ActionDefMaker()
							.putForward("simple_error")
							.putForward("simple_errorValidate")
							.putForward("bean0_input")
							.putEcho("bean0_confirm")
							.get()),
						new ForwardDef("simple_error", EEUtils.LOCAL_EL, FnUtils.fix(true))),
						Arrays.asList("name", "id"));

		toNode = new Delegator<String, Node>();
		
		fn = FnUtils.seq(new BuildableArrayList<Fn<? super EEContext, Void>>()
				.list(new IdConverter<Void>(ConvertUtils.convertRegExp(Pattern.compile("^.*/([^/]+)$"), "$1")))
				
				.list(ConfigUtils.setConfig(FnUtils.fix(new HashMap<String, Object>())))
				.list(ConfigUtils.putResourceBundle(ConfigUtils.resourceBundleFactory("woolpack.samples.locale.selection", threadLocal)))
				.list(ConfigUtils.putResourceBundle(ConfigUtils.resourceBundleFactory("woolpack.samples.locale.messages", threadLocal)))
				.list(ConfigUtils.toLinkedHashMap("address", "part.address.value", "part.address.label", ","))
				
				.list(validatorBuilder.getCheckExpression(
						actionBuilder.getActionExpression(),
						FnUtils.seq(new BuildableArrayList<Fn<? super EEContext, Void>>()
								.list(new IdConverter<Void>(FnUtils.fix("simple_errorValidate")))
								.list(validatorBuilder.getReplaceExpression())
								)))
								
				.list(new NodeSetter<EEContext>(FnUtils.join(
						IdUtils.GET_ID,
						MiscUtils.tryLocales(toNode, threadLocal))))
				.list(HtmlUtils.NORMALIZE_CASE)
				.list(XmlUtils.findNode(new JXP("//SELECT[@name]"),
						HtmlUtils.makeSelect(new OGE("config[node.getAttribute(\"name\")]"))))
				.list(HtmlUtils.removeExtension("FORM", "action"))

				.list(actionBuilder.getAutoUpdateExpression())
				.list(XmlUtils.findNode(new JXP("//P[@id=\"errorValidate\"]"),
						HtmlUtils.updateValue(validatorBuilder.getMessagesEL())))
				);
	}

	@Override public void init(final ServletConfig servletConfig) throws ServletException {
		// ロケールごとにテンプレートを切り替えています。
		super.init(servletConfig);
		toNode.setFn(FnUtils.join(
				ConvertUtils.convertRegExp(Pattern.compile("^(.*)$"), "/html/sample/locale/$1.html"),
				XmlUtils.nodeFactory(
						FactoryUtils.inputStreamReaderFactory(new ServletInputStreamFactory(
						servletConfig.getServletContext()), "UTF-8"),
						XmlTransformerUtils.TRANSFORMER)
			));
	}

	@Override protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
		threadLocal.set(request.getLocale());
		
		final EEContext context = new EEContext();
		context.setId(request.getRequestURI());
		context.setInput(request.getParameterMap());
		context.setRequest(new ServletRequestAttributeMap(request));
		context.setSession(Utils.concurrentMap(new HttpSessionMap(request.getSession()), request.getSession()));
		context.setApplication(Utils.concurrentMap(new ServletContextMap(request.getSession().getServletContext()), request.getSession().getServletContext()));
		fn.exec(context);
		
		final Writer w = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), "UTF-8"));
		try {
			final XmlTransformerContext tc = new XmlTransformerContext();
			tc.setSource(new DOMSource(context.getNode()));
			tc.setResult(new StreamResult(w));
			XmlTransformerUtils.TRANSFORMER.exec(tc);
		} finally {
			w.close();
		}
	}
}
