package project.view.tag;

import java.util.Collections;
import java.util.List;
import java.util.Objects;

import javax.servlet.http.Cookie;

import online.filter.ActionCountFilter;
import online.model.ModelUtil;
import online.view.ViewUtil;
import online.view.model.ViewMap;
import online.view.tag.BaseTag;

/**
 * メッセージステータスタグライブラリ
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class MsgStsSeedTag extends BaseTag {
	/** serialVersionUID */
	private static final long serialVersionUID = 1L;

	/** アイテム(SEED) */
	private static final String TAG_SEED = "SEED";

	/** 項目名 */
	private String name = null;

	/**
	 * リリース処理
	 */
	@Override
	public void release() {
		this.name = null;
	}

	/**
	 * 項目名設定
	 * @param val 項目名
	 */
	public void setName(final String val) {
		this.name = val;
	}

	/**
	 * @see javax.servlet.jsp.tagext.TagSupport#doStartTag()
	 */
	@Override
	public int doStartTag() {
		final var vm = getTargetMap();
		if (vm.containsKey(ModelUtil.TAG_MESSAGE)) {
			final var count = ActionCountFilter.getActionCount(getRequest());
			final var seed = Objects.toString(count, null);
			if (seed != null && !containsSeed(seed)) {
				final var cookie = new Cookie(TAG_SEED + seed, seed);
				cookie.setPath(Objects.toString(getRequest().getContextPath(), "") + "/");
				getResponse().addCookie(cookie);
			}
		}
		return EVAL_BODY_INCLUDE;
	}

	/**
	 * Viewマップ取得
	 * @return Viewマップ
	 */
	private ViewMap getTargetMap() {
		var vm = getViewMap();
		if (this.name != null) {
			final var obj = ViewUtil.getObject(vm, this.name);
			if (ViewMap.class.isInstance(obj)) {
				vm = ViewMap.class.cast(obj);
			}
		}
		return vm;
	}

	/**
	 * @see javax.servlet.jsp.tagext.TagSupport#doEndTag()
	 */
	@Override
	public int doEndTag() {
		try {
			final var vm = getTargetMap();
			if (!vm.containsKey(ModelUtil.TAG_MESSAGE)) {
				return EVAL_PAGE;
			}

			final var count = ActionCountFilter.getActionCount(getRequest());
			writeHeader(Objects.toString(count, null));
			if (vm.get(ModelUtil.TAG_MESSAGE) != null) {
				output(null, String.format("VIEW.msgsts.setTopMessage('%s', '%s');",
						vm.get(ModelUtil.TAG_MESSAGE), vm.get(ModelUtil.TAG_STATUS)), true);
			}

			final var set = vm.keySet();
			for (final String msg : set) {
				if (msg.endsWith(ModelUtil.TAG_MESSAGE) && !msg.equals(ModelUtil.TAG_MESSAGE)) {
					outputList(msg);
				}
			}

			writeFooter();

			return EVAL_PAGE;
		} finally {
			release();
		}
	}

	/**
	 * クッキー内SEED存在確認
	 *
	 * @param seed SEED値
	 * @return 存在した場合 true を返す。
	 */
	private boolean containsSeed(final String seed) {
		final var cookie = getRequest().getCookies();
		if (cookie != null) {
			for (final var ck : cookie) {
				if (ck.getName().startsWith(TAG_SEED) && ck.getValue().equals(seed)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * リスト出力
	 * @param msg メッセージキー
	 */
	private void outputList(final String msg) {
		final var vm = getTargetMap();
		final var obj = vm.get(msg);
		if (obj != null) {
			final var key = msg.substring(0, msg.length()
					- (ModelUtil.TAG_SEPARATOR.length() + ModelUtil.TAG_MESSAGE.length()));
			if (obj.getClass().isArray()) {
				writeList(key, Collections.singletonList(obj), vm);
			} else if (List.class.isInstance(obj)) {
				writeList(key, List.class.cast(obj), vm);
			} else {
				final var sts = key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_STATUS;
				final var inv = key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_INVALID;
				output(null, String.format(
						"VIEW.msgsts.setItemMessage('%s', '%s', '%s', 0, '%s');",
						key, obj, vm.get(sts), vm.getOrDefault(inv, "")), true);
			}
		}
	}

	/**
	 * リスト出力
	 *
	 * @param key キー
	 * @param msgs メッセージリスト
	 * @param m ビューマップ
	 */
	private void writeList(final String key, final List<?> msgs, final ViewMap m) {
		final var stss = getList(key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_STATUS, m);
		final var invs = getList(key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_INVALID, m);
		for (var i = 0; i < msgs.size(); i++) {
			if (msgs.get(i) != null) {
				output(null, String.format(
						"VIEW.msgsts.setItemMessage('%s', '%s', '%s', %s, '%s');",
						key, msgs.get(i), getListValue(stss, i),
						String.valueOf(i), getListValue(invs, i)), true);
			}
		}
	}

	/**
	 * リスト取得
	 *
	 * @param key キー
	 * @param m ビューマップ
	 * @return リスト
	 */
	private List<?> getList(final String key, final ViewMap m) {
		List<?> ret = null;
		final var obj = m.get(key);
		if (obj != null) {
			if (obj.getClass().isArray()) {
				ret = Collections.singletonList(obj);
			} else if (List.class.isInstance(obj)) {
				ret = List.class.cast(obj);
			}
		}
		return ret;
	}

	/**
	 * 値取得
	 *
	 * @param list リスト
	 * @param loc 位置
	 * @return 値
	 */
	private String getListValue(final List<?> list, final int loc) {
		if (list != null && loc < list.size()) {
			return Objects.toString(list.get(loc), "");
		}
		return "";
	}

	/**
	 * ヘッダ出力
	 *
	 * @param seed SEED値
	 */
	private void writeHeader(final String seed) {
		if (seed != null) {
			output(null, "<script>", true);
			if (getResponse().getContentType().contains("xml")) {
				output(null, "//<![CDATA[", true);
			} else {
				output(null, "<!--", true);
			}
			output(null, "if (VIEW.msgsts.removeCookie('SEED");
			output(null, seed + "', '" + seed + "')) {", true);
		}
	}

	/**
	 * フッタ出力
	 */
	private void writeFooter() {
		output(null, "}", true);
		if (getResponse().getContentType().contains("xml")) {
			output(null, "//]]>", true);
		} else {
			output(null, "// -->", true);
		}
		output(null, "</script>", true);
	}
}
