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

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Arrays;
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.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Node;

import woolpack.action.ActionDef;
import woolpack.action.ActionDefMaker;
import woolpack.action.ActionInvoker;
import woolpack.action.ForwardDef;
import woolpack.adapter.OGE;
import woolpack.adapter.OGNLUtils;
import woolpack.bool.BoolUtils;
import woolpack.container.ContainerContext;
import woolpack.container.ContainerUtils;
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.factory.FactoryUtils;
import woolpack.fn.Delegator;
import woolpack.fn.Fn;
import woolpack.fn.FnUtils;
import woolpack.html.HtmlUtils;
import woolpack.id.IdUtils;
import woolpack.utils.Utils;
import woolpack.web.WebContext;
import woolpack.web.WebUtils;
import woolpack.xml.XmlTransformerContext;
import woolpack.xml.XmlTransformerUtils;
import woolpack.xml.XmlUtils;

/**
 * 認証認可(Authentication and Authorization)を制御するサンプルウェブアプリケーションです。
 * @author nakamura
 *
 */
public class AAServlet extends HttpServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public final transient Delegator<String, Node, Exception> toNode;
	public final transient Fn<EEContext, Void, Exception> fn;
	
	public AAServlet() {
		super();
		OGNLUtils.setting();
		
		final ActionBuilder actionBuilder = new ActionBuilder(
				new ActionInvoker(
						FnUtils.switching(new ActionDefMaker()
						.putForward("simple_loginInput")
						.put("common_top", new ActionDef(
								new OGE("container.aaComponent"),
								new OGE("container.aaComponent.login(),container.aaComponent.valid"),
								new ForwardDef("common_top", BoolUtils.checkEquals(true)),
								new ForwardDef("simple_errorLogin")))
						.putForward("evenRole_result")
						.putForward("oddRole_result")
						.putForward("simple_logout").get()),
						new ForwardDef("simple_error", EEUtils.LOCAL_EL, FnUtils.fix(true))),
				Arrays.asList("name", "id"));

		toNode = new Delegator<String, Node, Exception>(null);

		final Fn<EEContext, Void, RuntimeException> actionExpression = FnUtils.ifTrue(
				FactoryUtils.doEL(new OGE("container.resource[id] == null || container.aaComponent.roleCollection.contains(container.resource[id])")),
				// 認可結果が true の場合はアクションに委譲します。
				actionBuilder.getActionExpression(),
				// 認可結果が false の場合は遷移する画面を切り替えます。
				IdUtils.<Void, RuntimeException>convertId(FnUtils.fix("simple_errorForbidden")));

		fn = FnUtils.seq(Utils.<Fn<? super EEContext, Void, ? extends Exception>>
				list(IdUtils.<Void, RuntimeException>convertId(ConvertUtils.convertRegExp(Pattern.compile("^.*/([^/]+)$"), "$1")))
				.list(WebUtils.setContainerContext(
						FnUtils.exec(FnUtils.join(
								ContainerUtils.getKey(),
								FnUtils.switching(Utils.<Object, Fn<? super ContainerContext<WebContext>, ?, ? extends Exception>>
									map(
										"aaComponent",
										WebUtils.session(FactoryUtils.newInstance(AAComponent.class)))
									.map("resource", FnUtils.fix(Utils
											.map("evenRole_result", "even")
											.map("oddRole_result", "odd")))
								)))))
				.list(FnUtils.exec(FnUtils.join(IdUtils.GET_ID, FnUtils.switching(Utils
								.map("simple_loginInput", actionExpression)
								.map("common_top")
								.map("simple_logout"),
								FnUtils.ifTrue(
										FactoryUtils.doEL(new OGE("container.aaComponent.valid")),
										actionExpression,
										IdUtils.<Void, RuntimeException>convertId(FnUtils.fix("simple_errorForbidden")))
						))))
				.list(XmlUtils.<EEContext, Exception>setNode(FnUtils.join(IdUtils.GET_ID, toNode)))
				.list(HtmlUtils.NORMALIZE_CASE)
				.list(HtmlUtils.removeExtension("A", "href"))
				.list(HtmlUtils.removeExtension("FORM", "action"))
				.list(actionBuilder.getAutoUpdateExpression())
				.list(FnUtils.exec(FnUtils.join(FnUtils.<EEContext, String, RuntimeException>delegate(IdUtils.GET_ID), FnUtils.switching(Utils
								.map("simple_logout", FnUtils.<EEContext, Void, RuntimeException>delegate(WebUtils.CLEAR_SESSION))
								.map("simple_errorLogin")
								.map("simple_errorForbidden"),
								FnUtils.fix(FnUtils.<EEContext, Void>fix(null))
						))))
		);
	}


	@Override public void init(final ServletConfig servletConfig) throws ServletException {
		super.init(servletConfig);
		toNode.setFn(FnUtils.join(
				ConvertUtils.convertRegExp(Pattern.compile("^(.*)$"), "/html/sample/aa/$1.html"),
				XmlUtils.nodeFactory(
						FactoryUtils.inputStreamReaderFactory(new ServletInputStreamFactory<IOException>(
						servletConfig.getServletContext()), "UTF-8"),
						XmlTransformerUtils.TRANSFORMER)));
	}

	@Override protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
		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()));

		try {
			fn.exec(context);
		} catch (final Exception e) {
			throw new ServletException(e);
		}
		
		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);
		} catch (final TransformerException e) {
			throw new ServletException(e);
		} finally {
			w.close();
		}
	}
}
