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

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;

import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Node;

import woolpack.config.ConfigContext;
import woolpack.id.IdContext;
import woolpack.utils.AppendableWriter;
import woolpack.utils.MapIterableMap;
import woolpack.utils.Utils;
import woolpack.web.WebContext;
import woolpack.xml.NodeContext;
import woolpack.xml.XmlTransformerContext;
import woolpack.xml.XmlUtils;

/**
 * 単一リクエストまたは単一スレッドごとの状態を保持するコンテキストです。
 * 
 * <br/>適用しているデザインパターン：Interpreter(Context 役)。
 * @author nakamura
 * 
 */
public class EEContext implements IdContext, ConfigContext, WebContext, NodeContext {
	private String id;
	private Map<String, Object> config;
	private Object container;
	private Map input;
	private Map<String, Object> request;
	private ConcurrentMap<String, Object> session;
	private ConcurrentMap<String, Object> application;
	private Map<String, Object> beforeLocal;
	private Map<String, Object> local;
	private Node node;

	public EEContext() {
	}

	/**
	 * サブクラスだけでなく{@link woolpack.fn.Fn}からも呼び出されることを想定しているため
	 * public にしています。
	 * 
	 * @param base コピー元。
	 */
	public EEContext(final EEContext base) {
		this.id = base.id;
		this.config = base.config;
		this.container = base.container;
		this.input = base.input;
		this.request = base.request;
		this.session = base.session;
		this.application = base.application;
		this.beforeLocal = (base.local != null) ? base.local : base.beforeLocal;
		this.node = base.node;
	}

	/**
	 * 浅いコピーを行います。
	 * 
	 * @return コピーされた{@link EEContext}。
	 */
	public EEContext copy() {
		return new EEContext(this);
	}

	/**
	 * 変更がコピー元{@link EEContext#getLocal()}には反映されない{@link Map}を返します。
	 * 
	 * @return テンポラリの{@link Map}。
	 */
	public Map<String, Object> getLocal() {
		if (local == null) {
			if (beforeLocal == null) {
				local = new HashMap<String, Object>();
			} else {
				local = new MapIterableMap<String, Object>(Utils
						.list((Map<String, Object>) new HashMap<String, Object>())
						.list(beforeLocal));
			}
		}
		return local;
	}

	/**
	 * テスト/デバッグ用にこのインスタンスの内容を出力します。
	 * 
	 * @param sb　出力先。
	 */
	public void appendTo(final Appendable sb) throws IOException {
		sb.append("DomContext dump information:");
		appendTo(sb, "id", id);
		appendTo(sb, "config", config);
		appendTo(sb, "container", container);
		appendTo(sb, "input", input);
		appendTo(sb, "request", request);
		appendTo(sb, "session", session);
		appendTo(sb, "application", application);
		appendTo(sb, "local", local);
		sb.append("\n,node:");
		if (node != null) {
			final XmlTransformerContext context = new XmlTransformerContext();
			context.setSource(new DOMSource(node));
			context.setResult(new StreamResult(new AppendableWriter(sb)));
			XmlUtils.TRANSFORMER.exec(context);
		}
	}

	private static void appendTo(final Appendable sb, final String label, final Object o) throws IOException {
		sb.append('\n');
		sb.append(',');
		sb.append(label);
		sb.append(':');
		if (o != null) {
			sb.append(o.toString());
		}
	}

	public Node getNode() {
		return node;
	}
	public void setNode(final Node node) {
		this.node = node;
	}
	public Map<String, Object> getConfig() {
		return config;
	}
	public void setConfig(final Map<String, Object> config) {
		this.config = config;
	}
	public Object getContainer() {
		return container;
	}
	public void setContainer(final Object container) {
		this.container = container;
	}
	public Map<String, Object> getRequest() {
		return request;
	}
	public void setRequest(final Map<String, Object> request) {
		this.request = request;
	}
	public ConcurrentMap<String, Object> getSession() {
		return session;
	}
	public void setSession(final ConcurrentMap<String, Object> session) {
		this.session = session;
	}
	public String getId() {
		return id;
	}
	public void setId(final String id) {
		this.id = id;
	}
	public Map getInput() {
		return input;
	}
	public void setInput(final Map input) {
		this.input = input;
	}
	public ConcurrentMap<String, Object> getApplication() {
		return application;
	}
	public void setApplication(
			final ConcurrentMap<String, Object> application) {
		this.application = application;
	}
}
