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 core.config.Factory;
import online.listener.SessionMutexListener;

/**
 * セション属性制御
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public 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 saveParameters(final HttpServletRequest request) {
		setAttributeMap(this.mod, request);
	}

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

	/**
	 * セション保持オブジェクト設定
	 *
	 * @param key キー
	 * @param obj 記憶オブジェクト
	 */
	public void setParameter(final String key, final Serializable obj) {
		this.mod.put(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) {
			final HttpSession session = request.getSession(false);
			if (session != null) {
				synchronized (SessionMutexListener.getMutex(session)) {
					map.entrySet().stream().forEach(e ->
						session.setAttribute(toSessionAttributeKey(e.getKey()), e.getValue()));
				}
			}
		}
	}

	/**
	 * セション情報保存マップ取得
	 *
	 * @param request リクエストオブジェクト
	 * @return セション記憶オブジェクト
	 */
	public static Map<String, Serializable> getAttributeMap(final HttpServletRequest request) {
		// セションオブジェクト取得
		if (request != null) {
			final 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.substring(PRE_SESSION_KEY.length()),
								v -> Serializable.class.cast(session.getAttribute(v))));
				}
			}
		}
		return null;
	}

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

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