package online.context.session;

import java.io.Serializable;
import java.util.Objects;
import java.util.Optional;

import javax.servlet.http.HttpServletRequest;

import core.config.Factory;
import online.filter.FilterUtil;

/**
 * SessionAttributeUtil
 *
 * @author Tadashi Nakayama
 */
public final class SessionAttributeUtil {

	/** キー */
	private static final String CLAZZ = SessionAttributeUtil.class.getName();

	/** セションタイプ */
	private static final String TAG_TYPE = CLAZZ + ".TYPE";
	/** タグ ADDED */
	private static final String TAG_ADDED = CLAZZ + ".ADDED";
	/** タグ REMOVED */
	private static final String TAG_REMOVED = CLAZZ + ".REMOVED";

	/** タグ SID */
	private static final String TAG_SID = "SID";

	/**
	 * コンストラクタ
	 */
	private SessionAttributeUtil() {
		throw new AssertionError();
	}

	/**
	 * 多重セション判断
	 *
	 * @param request リクエストオブジェクト
	 * @return 多重セション時 true を返す。
	 */
	public static boolean isMultipleSession(final HttpServletRequest request) {
		return request.getAttribute(TAG_TYPE) != null;
	}

	/**
	 * セションタイプ設定
	 *
	 * @param request リクエスト
	 */
	public static void setMultipleSession(final HttpServletRequest request) {
		request.setAttribute(TAG_TYPE, TAG_TYPE);
	}

	/**
	 * SIDをAttributeに設定
	 *
	 * @param request リクエストオブジェクト
	 * @param obj 設定オブジェクト
	 * @return 設定した場合 true を返す。
	 */
	public static boolean setSessionAttribute(
			final HttpServletRequest request, final Serializable obj) {
		final var sid = getAttributeSid(request);
		final var ret = sid != null;
		if (ret) {
			if (request.getAttribute(sid) == null) {
				setSessionAdded(request);
			}
			request.setAttribute(sid, obj);
		}
		return ret;
	}

	/**
	 * Attribute設定オブジェクト取得
	 *
	 * @param <T> Type
	 * @param request リクエスト
	 * @return Attribute設定オブジェクト
	 */
	public static <T> T getSessionAttribute(final HttpServletRequest request) {
		final var sid = getAttributeSid(request);
		return (sid != null) ? Factory.cast(request.getAttribute(sid)) : null;
	}

	/**
	 * アクションセション存在確認
	 *
	 * @param request リクエストオブジェクト
	 * @return 存在した場合 true を返す。
	 */
	public static boolean hasSessionAttribute(final HttpServletRequest request) {
		final var sid = getAttributeSid(request);
		return sid != null && request.getAttribute(sid) != null;
	}

	/**
	 * 保存消去
	 *
	 * @param request リクエストオブジェクト
	 */
	public static void removeSessionAttribute(final HttpServletRequest request) {
		Optional.ofNullable(getAttributeSid(request)).ifPresent(request::removeAttribute);
		setSessionRemoved(request);
	}

	/**
	 * SIDをパラメタから取得
	 *
	 * @param request リクエストオブジェクト
	 * @return SID
	 */
	public static String getParameterSid(final HttpServletRequest request) {
		return request.getParameter(TAG_SID);
	}

	/**
	 * URLのクエリ文字列内からSIDを削除する。
	 *
	 * @param query GETのパラメタクエリ
	 * @return 削除後文字列
	 */
	public static String stripSid(final String query) {
		return FilterUtil.stripParameter(query, TAG_SID);
	}

	/**
	 * SIDをAttributeから取得
	 *
	 * @param request リクエストオブジェクト
	 * @return SID
	 */
	public static String getAttributeSid(final HttpServletRequest request) {
		return String.class.cast(request.getAttribute(TAG_SID));
	}

	/**
	 * SIDをAttributeに設定
	 *
	 * @param request リクエストオブジェクト
	 * @param sid SID
	 */
	public static void setAttributeSid(final HttpServletRequest request, final String sid) {
		request.setAttribute(TAG_SID, sid);
	}

	/**
	 * SIDをAttributeから削除
	 *
	 * @param request リクエストオブジェクト
	 */
	public static void removeAttributeSid(final HttpServletRequest request) {
		request.removeAttribute(TAG_SID);
	}

	/**
	 * 開始確認
	 *
	 * @param request リクエストオブジェクト
	 * @return 開始された場合 true を返す。
	 */
	public static boolean isSessionAdded(final HttpServletRequest request) {
		return request.getAttribute(TAG_ADDED) != null;
	}

	/**
	 * 開始設定
	 *
	 * @param request リクエストオブジェクト
	 */
	private static void setSessionAdded(final HttpServletRequest request) {
		request.setAttribute(TAG_ADDED, TAG_ADDED);
	}

	/**
	 * 削除確認
	 *
	 * @param request リクエストオブジェクト
	 * @return 削除された場合 true を返す。
	 */
	public static boolean isSessionRemoved(final HttpServletRequest request) {
		return request.getAttribute(TAG_REMOVED) != null;
	}

	/**
	 * 削除設定
	 *
	 * @param request リクエストオブジェクト
	 */
	private static void setSessionRemoved(final HttpServletRequest request) {
		request.setAttribute(TAG_REMOVED, TAG_REMOVED);
	}

	/**
	 * Location取得
	 *
	 * @param request リクエスト
	 * @return Location
	 */
	public static String getSidLocation(final HttpServletRequest request) {
		return FilterUtil.getRequestUri(request)
				+ toSidQuery(FilterUtil.getRequestQuery(request), request);
	}

	/**
	 * sid付きクエリ文字列取得
	 *
	 * @param val クエリ文字列
	 * @param request リクエストオブジェクト
	 * @return アクションフォワード
	 */
	private static String toSidQuery(final String val, final HttpServletRequest request) {
		final var query = new StringBuilder();
		final var sid = getAttributeSid(request);
		if (!Objects.toString(sid, "").isEmpty()) {
			query.append("?").append(TAG_SID).append("=").append(sid);
		}
		final var qry = stripSid(val);
		if (!Objects.toString(qry, "").isEmpty()) {
			if (0 < query.length()) {
				query.append("&");
			} else if (!qry.startsWith("?")) {
				query.append("?");
			}
			query.append(qry);
		}
		return query.toString();
	}
}
