package project.view.tag;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import javax.servlet.http.Cookie;
import javax.servlet.jsp.JspException;

import online.filter.ActionCountFilter;
import online.model.ModelUtil;
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 = -1411640555265468110L;

	/** アイテム(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() throws JspException {
		ViewMap vm = getTargetMap();
		if (vm.containsKey(ModelUtil.TAG_MESSAGE)) {
			Number count = ActionCountFilter.getActionCount(super.getRequest());
			String seed = Objects.toString(count, null);
			if (seed != null && !containsSeed(seed)) {
				super.getResponse().addCookie(new Cookie(TAG_SEED + seed, seed));
			}
		}
		return EVAL_BODY_INCLUDE;
	}

	/**
	 * Viewマップ取得
	 * @return Viewマップ
	 */
	private ViewMap getTargetMap() {
		ViewMap vm = super.getViewMap();
		if (this.name != null) {
			Object obj = super.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() throws JspException {
		try {
			ViewMap vm = getTargetMap();
			if (!vm.containsKey(ModelUtil.TAG_MESSAGE)) {
				return EVAL_PAGE;
			}

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

			Set<String> 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) {
		Cookie[] cookie = super.getRequest().getCookies();
		if (cookie != null) {
			for (final Cookie ck : cookie) {
				if (ck.getName().startsWith(TAG_SEED) && ck.getValue().equals(seed)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * リスト出力
	 * @param msg メッセージキー
	 */
	private void outputList(final String msg) {
		ViewMap vm = getTargetMap();
		Object obj = vm.get(msg);
		if (obj != null) {
			String key = msg.substring(0, msg.length()
							- (ModelUtil.TAG_SEPARATOR.length() + ModelUtil.TAG_MESSAGE.length()));
			if (obj.getClass().isArray()) {
				writeList(key, Arrays.asList(obj), vm);
			} else if (List.class.isInstance(obj)) {
				writeList(key, List.class.cast(obj), vm);
			} else {
				String sts = key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_STATUS;
				String inv = key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_INVALID;
				super.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) {
		List<?> stss = getList(key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_STATUS, m);
		List<?> invs = getList(key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_INVALID, m);
		for (int i = 0; i < msgs.size(); i++) {
			if (msgs.get(i) != null) {
				super.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;
		Object obj = m.get(key);
		if (obj != null) {
			if (obj.getClass().isArray()) {
				ret = Arrays.asList(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) {
			super.output(null, "<script>", true);
			if (super.getResponse().getContentType().contains("xml")) {
				super.output(null, "//<![CDATA[", true);
			} else {
				super.output(null, "<!--", true);
			}
			super.output(null, "if (VIEW.msgsts.removeCookie('SEED");
			super.output(null, seed + "', '" + seed + "')) {", true);
		}
	}

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