package online.context.session;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import online.listener.SessionMutexListener;

/**
 * セション属性制御
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class SessionScope implements Serializable {
	/** serialVersionUID */
	private static final long serialVersionUID = 3685688787504825480L;

	/** セション保存キー */
	private static final String PRE_SESSION_KEY = SessionScope.class.getName();

	/** セション保存オブジェクト取得用マップ */
	private final Map<String, Serializable> smap;
	/** セション保存オブジェクト再設定用マップ */
	private final Map<String, Serializable> mod = new HashMap<>();

	/**
	 * コンストラクタ
	 *
	 * @param request リクエスト
	 */
	public SessionScope(final HttpServletRequest request) {
		this.smap = getAttributeMap(request);
	}

	/**
	 * セション保存用マップ取得
	 *
	 * @param request リクエスト
	 */
	public void saveSessionParameters(final HttpServletRequest request) {
		setAttributeMap(this.mod, request);
	}

	/**
	 * セション保持オブジェクト取得
	 *
	 * @param key キー
	 * @return セション保持オブジェクト
	 */
	public Object getSessionParameter(final String key) {
		String name = toSessionAttributeKey(key);
		if (!this.mod.containsKey(name)) {
			if (this.smap.get(name) != null) {
				this.mod.put(name, this.smap.get(name));
			}
		}
		return this.mod.get(name);
	}

	/**
	 * セション保持オブジェクト設定
	 *
	 * @param key キー
	 * @param obj 記憶オブジェクト
	 */
	public void setSessionParameter(final String key, final Serializable obj) {
		this.mod.put(toSessionAttributeKey(key), obj);
	}

	/**
	 * セション保持キー取得
	 *
	 * @param key 元キー文字列
	 * @return セション保持キー
	 */
	public static String toSessionAttributeKey(final String key) {
		return PRE_SESSION_KEY + key;
	}

	/**
	 * セション情報設定
	 *
	 * @param map マップ
	 * @param request リクエストオブジェクト
	 */
	public static void setAttributeMap(final Map<String, ? extends Serializable> map,
					final HttpServletRequest request) {
		// セションオブジェクト取得
		if (map != null && request != null) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				synchronized (SessionMutexListener.getMutex(session)) {
					map.entrySet().stream().
						filter(e -> e.getKey().startsWith(PRE_SESSION_KEY)).
						forEach(e -> session.setAttribute(e.getKey(), e.getValue()));
				}
			}
		}
	}

	/**
	 * セション情報保存マップ取得
	 *
	 * @param request リクエストオブジェクト
	 * @return セション記憶オブジェクト
	 */
	public static Map<String, Serializable> getAttributeMap(final HttpServletRequest request) {
		// セションオブジェクト取得
		if (request != null) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				synchronized (SessionMutexListener.getMutex(session)) {
					return Collections.list(session.getAttributeNames()).stream().
							filter(v -> v.startsWith(PRE_SESSION_KEY)).
							collect(Collectors.toMap(
								v -> v, v -> Serializable.class.cast(session.getAttribute(v))));
				}
			}
		}
		return null;
	}

	/**
	 * セション共通モデル情報設定
	 *
	 * @param request リクエストオブジェクト
	 * @param um 設定元モデル
	 */
	public static void setSessionUser(final HttpServletRequest request, final SessionUser um) {
		// セションオブジェクト取得
		HttpSession session = request.getSession(false);
		if (session != null) {
			session.setAttribute(session.getId(), um);
		}
	}

	/**
	 * セション共通モデル情報取得
	 *
	 * @param request リクエストオブジェクト
	 * @return セション共通情報
	 */
	public static SessionUser getSessionUser(final HttpServletRequest request) {
		// セションオブジェクト取得
		HttpSession session = request.getSession(false);
		return SessionUser.class.cast(
				session != null ? session.getAttribute(session.getId()) : null);
	}
}
