package jp.co.y_net.amm.page;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

import javax.servlet.http.Cookie;

import jp.co.y_net.amm.AppSession;
import jp.co.y_net.amm.common.AppLogger;
import jp.co.y_net.amm.common.ResourceReader;
import jp.co.y_net.amm.dao.GrpDao;
import jp.co.y_net.amm.dao.GrpusrDao;
import jp.co.y_net.amm.dao.InfDao;
import jp.co.y_net.amm.dao.OrgDao;
import jp.co.y_net.amm.dao.PwtcktDao;
import jp.co.y_net.amm.dao.Usr;
import jp.co.y_net.amm.dao.UsrDao;
import jp.co.y_net.amm.service.BatchUsrCreate;
import jp.co.y_net.amm.service.Challengestring;
import jp.co.y_net.amm.service.Logincheck;
import jp.co.y_net.amm.service.Logout;
import jp.co.y_net.amm.service.MailAddressCheck;
import jp.co.y_net.amm.service.PasswordTicketManager;

import org.apache.commons.lang.StringUtils;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.request.http.WebRequest;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.spring.injection.annot.SpringBean;

/**
 * このアプリケーションの最上位のPageクラス
 * pageパッケージ配下のPageクラスは必ず継承しなければならない。
 * 
 * CommonFrameXを経由して継承することを想定するため、
 * このPageクラスは画面のレイアウトは決定せず、
 * 共通処理を責務とする。
 * 
 * @author k_inaba
 */
public abstract class AppPage extends WebPage{

    /*
     * SpringBean宣言
     */
    
    @SpringBean(name="usrDao")
    protected UsrDao usrDao;
    
    @SpringBean(name="orgDao")
    protected OrgDao orgDao;
    
    @SpringBean(name="pwtcktDao")
    protected PwtcktDao pwtcktDao;
    
    @SpringBean(name="infDao")
    protected InfDao infDao;
    
    @SpringBean(name="grpDao")
    protected GrpDao grpDao;
    
    @SpringBean(name="grpusrDao")
    protected GrpusrDao grpusrDao;
    
    @SpringBean(name="challengestring")
    protected Challengestring challengestring;
    
    @SpringBean(name="logincheck")
    protected Logincheck logincheck;
    
    @SpringBean(name="logout")
    protected Logout logout;
    
    @SpringBean(name="passwordTicketManager")
    protected PasswordTicketManager passwordTicketManager;
    
    @SpringBean(name="mailAddressCheck")
    protected MailAddressCheck mailAddressCheck;
    
    @SpringBean(name="batchUsrCreate")
    protected BatchUsrCreate batchUsrCreate;
    
    public AppPage() {
        /* ステートレスなページでも、
         * JavaEEの実際のSessionオブジェクトと関連付けする */
        AppSession.get().bind();
    }
    
    /**
     * コンストラクタ呼び出しが行われなくても処理対象となるメソッド
     * 戻るボタンによる遷移でも呼び出される。
     * @see org.apache.wicket.Component#onConfigure()
     */
    @Override
    protected void onConfigure() {
        /* ログインチェック */
        if (validateLogin() == false || existRequestLogout() || missmatchLoginId()) {
            
            /* 戻り画面を設定 */
            AppSession.get().returnPageClass = getPageClass();
            AppSession.get().returnPageParameters = getPageParameters();
            /* ログイン画面へ遷移 */
            setResponsePage(LoginPage.class, getPageParameters());
            
            return;
        }
    }

    /**
     * @return ログアウト要求が登録されていればTrue
     */
    private boolean existRequestLogout() {
        if (AppSession.get().isLogin()) {
            
            Integer usrId = AppSession.get().getLoginUser().getUsrid();
            if (usrId != null) {
                Usr usr = usrDao.getByUsrid(usrId);
                if (usr != null) {
                    if (usr.getRequestlogoutdate() != null && usr.getRequestlogoutdate() != -1L) {
                        
                        /* ログアウトを実行する */
                        logout.execute(AppSession.get().getLoginUser().getLoginid());
//                      AppSession.get().invalidateNow(); // セッション無効化
                        AppSession.get().clearLoginUser();
                        
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * 2016/06/02 
     * @return パラメータに付与されているログインIDとセッション上のログインIDが一致していなければTrue
     */
    private boolean missmatchLoginId() {
        if (AppSession.get().isLogin()) {
            /* 
             * パラメータにログインIDが含まれている場合の処理
             */
            PageParameters params = getPageParameters();
            String paramLoginid = params.get("loginid").toString();
            if (StringUtils.isEmpty(paramLoginid) == false) {
                if (AppSession.get().getLoginUser().getLoginid().equals(paramLoginid) == false) {
                    /* パラメータのログインIDとセッション情報のログインIDが一致していない。*/

                    /* ログアウトを実行する */
                    logout.execute(AppSession.get().getLoginUser().getLoginid());
//                    AppSession.get().invalidateNow(); // セッション無効化
                    AppSession.get().clearLoginUser();
                    
                    return true;
                }
            }

        }
        return false;

    }

    /**
     * ログインチェック
     * 　ログイン認証が必要な画面ではOverrideして実装すること
     * @return Pageへのアクセスを認める場合はTrue
     */
    protected boolean validateLogin() {
        return true; // 既定では必ず「チェックOK」となる。
    }

    

    
    /* ---------------------------------------------------------------------------------------------------------------
     * 入力値検査
     */
    
    
    /**
     * 【文字列】
     * valueが妥当な値でなければ error でメッセージをセットする。
     * @param value 検査対象
     * @param itemname メッセージで使用する項目名
     * @param min 許容される最小値（必須入力の場合は1以上の値を設定すること）
     * @param max 許容される最大値
     */
    protected void validItem(String value, String itemname, int min, int max) {
        if(StringUtils.isEmpty(value)) {
            if(min > 0) {
                error(itemname + "を入力してください。");
            } else {
                /* 処理なし */
            }
        } else {
            if ((value.length() >= min &&  value.length() <= max) == false ) {
                if(min == max) {
                    error(itemname + "は" + min + "文字で入力してください。");
                } else {
                    if(min == 1 || min == 0) {
                        error(itemname + "は" + max + "文字以下で入力してください。");
                    } else {
                        error(itemname + "は" + min + "～" + max + "文字で入力してください。");
                    }
                }
            }
        }
    }
    /**
     * 【数値】
     * valueが妥当な値でなければ error でメッセージをセットする。
     * @param value 検査対象
     * @param itemname メッセージで使用する項目名
     * @param min 許容される最小値（必須入力の場合は1以上の値を設定すること）
     * @param max 許容される最大値
     */
    protected void validItemNum(String value, String itemname, int min, int max) {
        validItem(value, itemname, min, max);
        if(hasInputError()) return; // ここまでにエラーがあれば中断
        if(StringUtils.isEmpty(value) == false) {
            if(isNumeric(value) == false) {
                error(itemname + "は数字で入力してください。");
            }
        }
    }
    /**
     * 【電話番号】
     * valueが妥当な値でなければ error でメッセージをセットする。
     * @param value 検査対象
     * @param itemname メッセージで使用する項目名
     * @param min 許容される最小値（必須入力の場合は1以上の値を設定すること）
     * @param max 許容される最大値
     */
    protected void validItemTel(String value, String itemname, int min, int max) {
        validItem(value, itemname, min, max);
        if(hasInputError()) return; // ここまでにエラーがあれば中断
        if(StringUtils.isEmpty(value) == false) {
            value = value.replaceAll("-", "");
            if(isNumeric(value) == false) {
                error(itemname + "は数字とハイフンのみ使用できます。");
            }
        }
    }
    /**
     * 【英数記号】
     * valueが妥当な値でなければ error でメッセージをセットする。
     * @param value 検査対象
     * @param itemname メッセージで使用する項目名
     * @param min 許容される最小値（必須入力の場合は1以上の値を設定すること）
     * @param max 許容される最大値
     */
    protected void validItemAscii(String value, String itemname, int min, int max) {
        validItem(value, itemname, min, max);
        if(hasInputError()) return; // ここまでにエラーがあれば中断
        if(StringUtils.isEmpty(value) == false) {
            if(StringUtils.isAsciiPrintable(value) == false) {
                error(itemname + "に使用できない文字が含まれています。");
            }
        }
    }
    /**
     * 【メールアドレス】
     * @param value 検査対象
     * @param itemname メッセージで使用する項目名
     * @param min 許容される最小値（必須入力の場合は1以上の値を設定すること）
     * @param max 許容される最大値
     */
    protected void validItemMail(String value, String itemname) {
        validItemMail(value, itemname, false);
    }
    /**
     * 【メールアドレス】
     * @param value 検査対象
     * @param itemname メッセージで使用する項目名
     * @param min 許容される最小値（必須入力の場合は1以上の値を設定すること）
     * @param max 許容される最大値
     */
    protected void validItemMail(String value, String itemname, boolean freemailCheck) {
        
        if(StringUtils.isEmpty(value)) {
            error(itemname + "を入力してください。");
            return;
        }
        if (value.length() > 256 ) {
            error(itemname + "は256文字以内で入力してください。");
            return;
        }
        if(StringUtils.isAsciiPrintable(value) == false) {
            error(itemname + "に使用できない文字が含まれています。");
            return;
        }
        if(value.indexOf('@') == -1) {
            /* @がないメールアドレスは不正とする */
            error(itemname + "は形式が正しくありません。");
            return;
        }
        if(freemailCheck) {
            /* フリーメールチェック */
            for (String ng: ResourceReader.getLines("omitmaildomains")) {
                if(ng == null) ng = "";
                ng = ng.trim();
                if(StringUtils.isEmpty(ng) == false) {
                    if(value.endsWith(ng)) {
                        error(itemname + "は利用できないドメインです。");
                        
                        /* 攻撃時の調査のため、ログ出力 */
                        AppLogger.info("次のメールアドレスで申請されましたが、拒否リストに含まるため受け付けませんでした。：" + value);
                        
                        return;
                    }
                }
            }
        }
    }
    
    /**
     * @return エラーが検知されているときにTrue
     */
    protected boolean hasInputError() {
        return AppSession.get().getFeedbackMessages().isEmpty() == false;
    }
    
    
    
    /**
    * 文字列が数値かどうかチェックする
    * @param value   対象文字列
    * @return  数値 true   数値以外 false
    */
    public static boolean isNumeric(String value) {
//        try { 以下の実装では Intの最大値以上で不正な結果を返す。
//            Integer.parseInt(value);
//        } catch (NumberFormatException e) {
//            return false;
//        }
//        return true;
        if("-1".equals(value)) {
            return true; // 「指定なし」を示す数値として許可する
        }
        if(StringUtils.isNumeric(value) == false) {
            return false;
        }
        
        if(StringUtils.isEmpty(value) != false) {
            /* 全角の数字をNGとする。*/
            if(value.indexOf('０') != -1) return false;
            if(value.indexOf('１') != -1) return false;
            if(value.indexOf('２') != -1) return false;
            if(value.indexOf('３') != -1) return false;
            if(value.indexOf('４') != -1) return false;
            if(value.indexOf('５') != -1) return false;
            if(value.indexOf('６') != -1) return false;
            if(value.indexOf('７') != -1) return false;
            if(value.indexOf('８') != -1) return false;
            if(value.indexOf('９') != -1) return false;
        }
        return true;
    }
    
    
    
    
    
    /*
     * ユーティリティ ---------------------------------------------------------------------------------------------
     */
    /** @return 機関会員がログインしているときにTrue */
    protected boolean isMember() {
        return AppSession.get().isLogin()
                && AppSession.get().getLoginUser().getKind() != null 
                && AppSession.get().getLoginUser().getKind().equals(Usr.KIND_機関会員);
    }
    /** @return 機関管理者がログインしているときにTrue */
    protected boolean isOrgAdmin() {
        return AppSession.get().isLogin()
                && AppSession.get().getLoginUser().getKind() != null 
                && AppSession.get().getLoginUser().getKind().equals(Usr.KIND_機関管理者);
    }
    /** @return 運営管理者がログインしているときにTrue */
    protected boolean isSystemAdmin() {
        return AppSession.get().isLogin()
                && AppSession.get().getLoginUser().getKind() != null 
                && AppSession.get().getLoginUser().getKind().equals(Usr.KIND_運営管理者);
    }
    protected boolean isSystemAdmin代表者() {
        return AppSession.get().isLogin()
                && AppSession.get().getLoginUser().getKind() != null 
                && AppSession.get().getLoginUser().getKind().equals(Usr.KIND_運営管理者)
                && AppSession.get().getLoginUser().getOrgrole() != null
                && AppSession.get().getLoginUser().getOrgrole().equals(Usr.ROLE_管理者);
    }
    /**
     * @param usr 
     * @return usr のログインIDが、ログインユーザのIDと一致するときにTrue
     */
    protected boolean isLoginUser(Usr usr) {
        return AppSession.get().isLogin()
                && AppSession.get().getLoginUser().getUsrid().equals(usr.getUsrid());
    }
    
//    protected String createFullUrl(String url, boolean https) {
//        /* URLを作成する */
//        Url urlMe = getRequestCycle().getRequest().getOriginalUrl();
//        /* 必要に応じてプロトコルを変更する */
//        String protocol = https ? "https://" : "http://";
//        String host = urlMe.getHost(); // 現在のホスト名を引き継ぐ
//        String port = String.valueOf(urlMe.getPort()); // 現在のポートを引き継ぐ
//        if (StringUtils.isEmpty(port) || "80".equals(port)) {
//            port = "";
//        } else {
//            port = ":" + port;
//        }
//        String contextPath = getRequestCycle().getRequest().getContextPath(); // 現在のコンテキストパスを引き継ぐ
//        String urlBase = protocol + host + port + contextPath + "/" + url;
//        return urlBase;
//    }

    protected String getScreenId() {
        return getPage().getClass().getSimpleName();
    }
    protected PageParameters getParams() {
        return super.getPageParameters();
    }
    protected void addCookie(String name, String value, int time) {
        Cookie cookie = new Cookie(name, value);
        cookie.setPath("/");
        cookie.setMaxAge(time); // 保存期間
        ((WebResponse) getRequestCycle().getResponse()).addCookie(cookie);
    }
    protected String getCookie(String name) {
        List<Cookie> cookies = ((WebRequest) getRequestCycle().getRequest()).getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (name.equals(cookie.getName())) {
                    return cookie.getValue();
                }
            }
        }
        return "";
    }
// TODO 実装箇所再検討
    protected static boolean validFromToDate(String value) {
        if("00000000".equals(value)){
            return true; // 未指定として正しい値
        }
        final DateFormat DF = new SimpleDateFormat("yyyyMMdd");
        DF.setLenient(false); // 厳密な変換
        try {
            DF.parse(value);
            return true;
        } catch (ParseException e) {
            return false;
        }
    }
    
    




}
