package online.struts.chain.command;

import javax.servlet.http.HttpServletRequest;

import online.model.UniModel;
import online.struts.action.UniForm;
import online.struts.chain.command.ActionRequest.CommandRequest;
import online.struts.mapping.RequestMapping;
import online.view.ViewUtil;

import org.apache.commons.chain2.Processing;
import org.apache.struts.action.ActionForward;
import org.apache.struts.chain.commands.ActionCommandBase;
import org.apache.struts.chain.contexts.ActionContext;
import org.apache.struts.chain.contexts.ServletActionContext;
import org.apache.struts.config.ForwardConfig;

/**
 * 可変パス埋め込み
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class FillInProcessor extends ActionCommandBase implements CommandRequest {

	/**
	 * @see org.apache.struts.chain.commands.ActionCommandBase
	 * #execute(org.apache.struts.chain.contexts.ActionContext)
	 */
	@Override
	public Processing execute(final ActionContext<String, Object> context) {
		return ActionRequest.execute(context, this);
	}

	/**
	 * @see online.struts.chain.command.ActionRequest.CommandRequest
	 * #command(org.apache.struts.chain.contexts.ServletActionContext,
	 * online.struts.mapping.RequestMapping, online.struts.action.UniForm)
	 */
	@Override
	public Processing command(final ServletActionContext sac,
					final RequestMapping mapping, final UniForm uf) {
		if (!sac.getResponse().isCommitted()) {
			ForwardConfig af = sac.getForwardConfig();
			if (af != null && !sac.getResponse().isCommitted()) {
				af = fillIn(af, uf, sac.getRequest());
				if (!RequestMapping.isJsp(af)) {
					af.setPath(mapping.addPathInfo(sac.getRequest(), af.getPath()));
				}
				sac.setForwardConfig(af);
			}
		}
		return Processing.CONTINUE;
	}

	/**
	 * パス内の{}で括られた文字列をモデル内の値で置換する。
	 * @param af アクションフォワード
	 * @param uf 汎用フォーム
	 * @param request リクエスト
	 * @return リクエストフォワード
	 */
	private static ActionForward fillIn(final ForwardConfig af,
					final UniForm uf, final HttpServletRequest request) {
		ActionForward rf = new ActionForward();
		rf.setName(af.getName());
		rf.setPath(fillIn(af.getPath(), uf, request));
		rf.setRedirect(af.getRedirect());
		rf.setModule(af.getModule());
		rf.setCatalog(af.getCatalog());
		rf.setCommand(af.getCommand());
		return rf;
	}

	/**
	 * パス内の{}で括られた文字列をモデル内の値で置換する。
	 * @param pass パス文字列
	 * @param uf 汎用フォーム
	 * @param request リクエスト
	 * @return 置換後文字列
	 */
	private static String fillIn(final String pass, final UniForm uf,
					final HttpServletRequest request) {
		if (pass != null) {
			int open = pass.indexOf('{');
			int close = pass.indexOf('}', open);
			if (0 <= open && open < close) {
				return pass.substring(0, open)
					+ getValueToFill(uf, pass.substring(open + "{".length(), close), request)
					+ fillIn(pass.substring(close + "}".length()), uf, request);
			}
		}
		return pass;
	}

	/**
	 * 埋込値取得
	 *
	 * @param uf 汎用フォーム
	 * @param param 項目名
	 * @param request リクエスト
	 * @return 埋込値
	 */
	private static String getValueToFill(final UniForm uf, final String param,
					final HttpServletRequest request) {
		UniModel um = uf;
		String[] keys = param.split("\\.");
		for (int i = 0; i < keys.length - 1; i++) {
			if (!UniModel.class.equals(um.getValueClass(keys[i]))) {
				return null;
			}
			um = um.getModel(keys[i]);
		}

		String key = keys[keys.length - 1];
		String ret = um.getString(key);
		if (request.getAttribute(key) != null) {
			ret = String.class.cast(request.getAttribute(key));
		}
		return ViewUtil.encode(
				RequestMapping.dropLineTerminators(ret), request.getCharacterEncoding());
	}
}
