package jp.ossc.petstore.util.filter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.Globals;

import jp.ossc.petstore.OpenstandiaConst;

/**
 * ログインチェックフィルタ
 * @author ossc
 * @version $Revision: 1.2 $
 */
public class CheckLoginFilter implements Filter {

	/**
	 * 初期化パラメータ項目名：エラー時のフォワード先ページ
	 */
	private static final String INIT_PARAM_NAME_FORWARD_PAGE = "forwardPage";
	
	/**
	 * 初期化パラメータ項目名：チェック対象外のアクション名
	 */
	private static final String INIT_PARAM_NAME_SKIP_ACTION = "skipAction";
	
	/**
	 * 初期化エラーメッセージ
	 */
	private static final String ERR_MSG_INIT_PARAM_NOT_EXIST = "init-param is not exist :";

	/**
	 * ログインチェックエラーメッセージキー
	 */
	private static final String ERR_MSG_KEY_NOT_LOGIN = "errors.not_authenticated";

	/**
	 * 空文字
	 */
	private static final String EMPTY_STR = "";
	
	/**
	 * スラッシュ
	 */
	private static final String SLASH = "/";
	
	/**
	 * 初期化パラメータ区切り文字
	 */
	private static final String INIT_PARAM_DELIM = ",";
	
	/**
	 * エラー時のフォワード先ページ
	 */
    private String forwardPage = null;

    /**
     * チェック対象外のアクション名
     */
    private String[] skipActions = null;
    
    
    /**
     * 開放処理
     */
    public void destroy() {
        this.forwardPage = null;
    }

    /**
     * 初期化処理
     * @exception RuntimeException 初期化パラメータ「forwardPage」が設定されていない場合
     */
    public void init(FilterConfig filterConfig) throws ServletException {

    	// エラー時のフォワード先ページの取得
        String forwardPage = filterConfig.getInitParameter(INIT_PARAM_NAME_FORWARD_PAGE);
        if (forwardPage == null || forwardPage.trim().equals(EMPTY_STR)) {
        	throw new RuntimeException(ERR_MSG_INIT_PARAM_NOT_EXIST + INIT_PARAM_NAME_FORWARD_PAGE);
        } else {
            this.forwardPage = forwardPage.trim();
        }
        
        // チェック対象外のアクション名の取得
        String skipAction = filterConfig.getInitParameter(INIT_PARAM_NAME_SKIP_ACTION);
        if (skipAction != null && !skipAction.trim().equals(EMPTY_STR)) {
        	this.skipActions = parseParams(skipAction);
        }
    }


    /**
     * フィルタ処理<br>
     * チェック対象外のアクションの場合は処理を終了する。<br>
     * セッションが存在しない場合、またはセッションに顧客情報が登録されていない場合は
     * 指定されたページにフォワードする。<br>
     */
    public void doFilter(
    		ServletRequest request,
    		ServletResponse response,
    		FilterChain chain)
    throws IOException, ServletException {

    	// HttpServletRequestにキャスト
    	HttpServletRequest httpServletRequest = (HttpServletRequest)request;
    	
    	// ServletPathを取得
    	String servletPath = httpServletRequest.getServletPath();
    	
    	// チェック対象外のアクションの場合は処理終了
    	if (isSkipAction(servletPath)) {
    		chain.doFilter(request, response);
    		return;
    	}
    	
    	// セッションを取得
    	HttpSession session = httpServletRequest.getSession(false);
    	
    	// 顧客情報がセッションに登録されている場合はＯＫ
    	if (session != null
    		&& session.getAttribute(OpenstandiaConst.ENV_SES_ATRBT_CUSTOMER) != null) {
            chain.doFilter(request, response);
    		
    	} else {
			// エラーメッセージを設定
			ActionMessage msg = new ActionMessage(ERR_MSG_KEY_NOT_LOGIN);
			ActionMessages msgs = new ActionMessages();
			msgs.add("Login", msg);
			httpServletRequest.setAttribute(Globals.MESSAGE_KEY, msgs);

			// 指定されたページにフォワード
    		httpServletRequest.getRequestDispatcher(forwardPage).forward(request, response);
    	}
    }

    
	/**
	 * カンマ区切りで与えられたパラメータを、カンマで区切って配列にして返す。<br>
	 * 空白文字はtrimする。<br>
	 * @param str カンマ区切りで連結されたパラメータ
	 * @return 分割されたパラメータが格納された配列
	 */
	protected String[] parseParams(String str) {
		
		List<String> params = new ArrayList<String>();
		StringTokenizer stringTokenizer = new StringTokenizer(str, INIT_PARAM_DELIM);
		String token = null;

		while (stringTokenizer.hasMoreTokens()) {
			token = stringTokenizer.nextToken().trim();
			if (token.length() == 0) {
				continue;
			}
			params.add(token);
		}

		return (String[])params.toArray(new String[0]);
	}

	
	/**
	 * 処理対象外アクション判定
	 * @param サーブレットパス
	 * @return true:処理対象外アクションの場合、false:処理対象アクションの場合
	 */
	protected boolean isSkipAction(String servletPath) {

		boolean result = false;

		if (skipActions == null) {
			return result;
		}

		String action = servletPath.substring(servletPath.lastIndexOf(SLASH));
		
		for (String skipAction : skipActions) {
			if (action.equals(skipAction)) {
				result = true;
				break;
			}
		}
		
		return result;
	}

}
