package online.struts.chain.processor;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;
import org.apache.struts.chain.commands.servlet.PerformForward;
import org.apache.struts.chain.contexts.ActionContext;
import org.apache.struts.chain.contexts.ServletActionContext;
import org.apache.struts.config.ForwardConfig;

import online.filter.FilterUtil;
import online.struts.mapping.RequestMapping;

/**
 * フォワード
 *
 * @author Tadashi Nakayama
 */
public final class PerformForwardProcessor extends PerformForward {

	/**
	 * @see org.apache.struts.chain.commands.servlet.PerformForward
	 * #perform(org.apache.struts.chain.contexts.ActionContext,
	 * org.apache.struts.config.ForwardConfig)
	 */
	@Override
	protected void perform(final ActionContext<String, Object> context,
			final ForwardConfig forwardConfig) {
		if (ServletActionContext.class.isInstance(context)) {
			final var sac = ServletActionContext.class.cast(context);
			if (redirect(sac)) {
				return;
			}
		}

		super.perform(context, forwardConfig);
	}

	/**
	 * リダイレクト
	 *
	 * @param sac ServletActionContext
	 * @return リダイレクトした場合 true を返す。
	 */
	private boolean redirect(final ServletActionContext sac) {
		final var af = sac.getForwardConfig();
		if (af != null) {
			if (!RequestMapping.isJsp(af) && RequestMapping.isRedirect(af)) {
				final var ac = sac.getActionConfig();
				if (RequestMapping.class.isInstance(ac)) {
					// リダイレクト(303)
					handleRedirect(sac, RequestMapping.class.cast(ac), af.getPath());
					return true;
				}
			} else if (af.getPath() != null && FilterUtil.isInclude(sac.getRequest())) {
				handleInclude(sac);
				return true;
			}
		}
		return false;
	}

	/**
	 * リダイレクト
	 * @param sac ServletActionContext
	 * @param mapping マッピング
	 * @param path パス
	 */
	private void handleRedirect(final ServletActionContext sac,
			final RequestMapping mapping, final String path) {
		try {
			sac.getResponse().setStatus(HttpServletResponse.SC_SEE_OTHER);
			sac.getResponse().setHeader("Location", sac.getResponse().encodeRedirectURL(
							getLocation(mapping, sac.getRequest(), path)));
			sac.getResponse().setContentLength(0);
			sac.getResponse().flushBuffer();
		} catch (final IOException ex) {
			LogManager.getLogger().info(ex.getMessage());
		}
	}

	/**
	 * フォワード先取得
	 * @param mapping マッピング
	 * @param request リクエスト
	 * @param path パス
	 * @return フォワード先
	 */
	private String getLocation(final RequestMapping mapping,
			final HttpServletRequest request, final String path) {
		var ret = mapping.addPathInfo(request, path);
		if (ret.startsWith("/")) {
			ret = request.getContextPath() + ret;
		}
		return ret;
	}

	/**
	 * インクルード
	 * @param sac ServletActionContext
	 */
	private void handleInclude(final ServletActionContext sac) {
		final var fc = sac.getForwardConfig();
		final var rd = sac.getContext().getRequestDispatcher(fc.getPath());
		try {
			rd.include(sac.getRequest(), sac.getResponse());
		} catch (final IOException ex) {
			LogManager.getLogger().info(ex.getMessage());
		} catch (final ServletException ex) {
			LogManager.getLogger().info(ex.getMessage());
			throw new RuntimeException(ex);
		}
	}
}
