package jp.agentec.abook.abv.bl.logic;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;

import jp.agentec.abook.abv.bl.acms.client.AcmsClient;
import jp.agentec.abook.abv.bl.acms.client.json.AuthLevelJSON;
import jp.agentec.abook.abv.bl.acms.client.json.ServerTimeZoneJSON;
import jp.agentec.abook.abv.bl.acms.client.parameters.AcmsParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.AppStoreNewLoginParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.EnterpriseNewLoginParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.NewAppStoreLoginParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.PasswordChangeParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.ServerTimeParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.UpdateDeviceTokenParameters;
import jp.agentec.abook.abv.bl.acms.type.AcmsAuthenticationErrorCode;
import jp.agentec.abook.abv.bl.acms.type.DeleteDataType;
import jp.agentec.abook.abv.bl.acms.type.LoginStatus;
import jp.agentec.abook.abv.bl.acms.type.RequirePasswordChangeCode;
import jp.agentec.abook.abv.bl.acms.type.RequirePasswordChangeType;
import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.Constant;
import jp.agentec.abook.abv.bl.common.exception.ABVException;
import jp.agentec.abook.abv.bl.common.exception.ABVExceptionCode;
import jp.agentec.abook.abv.bl.common.exception.ABVRuntimeException;
import jp.agentec.abook.abv.bl.common.exception.AcmsException;
import jp.agentec.abook.abv.bl.common.exception.NetworkDisconnectedException;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.common.util.SecurityUtil;
import jp.agentec.abook.abv.bl.data.ABVDataCache;
import jp.agentec.abook.abv.bl.data.dao.AbstractDao;
import jp.agentec.abook.abv.bl.data.dao.MemberInfoDao;
import jp.agentec.abook.abv.bl.data.dao.PasswordLockInfoDao;
import jp.agentec.abook.abv.bl.download.ContentDownloader;
import jp.agentec.abook.abv.bl.dto.MemberInfoDto;
import jp.agentec.abook.abv.bl.dto.PasswordLockInfoDto;
import jp.agentec.adf.util.DateTimeFormat;
import jp.agentec.adf.util.DateTimeUtil;
import jp.agentec.adf.util.StringUtil;

/**
 * ユーザ認証に関する機能を提供します。
 * @author Taejin Hong
 * @version 1.1.0
 */
public class UserAuthenticateLogic extends AbstractLogic {
//	private static final String TAG = "UserAuthenticateLogic";
	
	private MemberInfoDao memberInfoDao = AbstractDao.getDao(MemberInfoDao.class);
	private GroupLogic groupLogic = AbstractLogic.getLogic(GroupLogic.class);
	private ContractLogic contractLogic = AbstractLogic.getLogic(ContractLogic.class);
	private PasswordLockInfoDao passwordLockInfoDao = AbstractDao.getDao(PasswordLockInfoDao.class);

    /**
	 * {@link UserAuthenticateLogic} クラスのインスタンスを初期化します。
	 * Androidとの互換性の為のプロパティです。Androidの android.content.Context のインスタンスを指定します。<br>
	 * UIがAndroidでない場合、何かのオブジェクトを指定しても、nullと見なします。
	 * @since 1.0.0
	 */
	/*package*/ UserAuthenticateLogic() {
	}
	
	// ログイン情報の取得  FIXME:このロジック意味不明すぎる。要書き直し
	// ABVDataCacheのgetMemberInfo()との違いが分からない.
	public MemberInfoDto getMemberInfo() throws ABVException {
		MemberInfoDto dto;
//		GroupLogic groupLogic = AbstractLogic.getLogic(GroupLogic.class);
		String urlPath = contractLogic.getUrlPath();
		
		dto = memberInfoDao.getMemberInfo(LoginStatus.LoggedIn.statusCode());
		if (!StringUtil.isNullOrEmpty(urlPath)) {
			
			if (dto != null && cache.getUrlPath() != null) {
				cache.refreshMemberInfo(dto);
				
//				if (networkAdapter.isNetworkConnected()) { // FIXME：なぜここで？？？？
//					//	待機中のダウンロードがあれば開始する。
//					ContentDownloader.getInstance().kickTask();
//				}
			}
		}
		
		return dto;
	}

	/**
	 * ユーザ認証を行い、ユーザ情報を返します。初回ログインの場合は端末の登録も行います。<br>
	 * 認証に成功したユーザの情報はキャッシュされます。
	 * @param urlPath 契約事業者のアカウント文字列です。
	 * @param loginId ユーザのログインIDです。
	 * @param password ユーザのログインパスワードです。
	 * @param deviceToken
	 * @return 認証に成功すると、ユーザ情報を格納した {@link MemberInfoDto} オブジェクトを返します。
	 * @throws NetworkDisconnectedException インターネットにつながっていないため、ログインできません。
	 * @throws ABVException インターネットにはつながっているが、ログインは失敗しました。失敗した原因は、{@link ABVException#getCode()} から確認できます。
	 * @throws Exception その他、例外です。
	 */
	public MemberInfoDto serverLogin(String urlPath, String loginId, String password, String deviceToken) throws Exception {
		MemberInfoDto dto = newAppStoreLogin(urlPath, loginId, password, false, deviceToken);
		cache.refreshMemberInfo(dto);
		contractLogic.initializeContractServiceOption();
		return dto;
	}

	/**
	 * 認証なしログイン。
	 * 
	 * @param urlPath
	 * @param loginId
	 * @param password
	 * @throws NetworkDisconnectedException
	 * @throws ABVException
	 * @throws Exception
	 */
	public void noAuthenticatedLogin(String urlPath, String loginId, String password, String deviceToken, boolean isGuestLogin) throws Exception {
		// 認証なしログインであるがサーバ認証は行う
		MemberInfoDto dto = enterpriseNewLogin(urlPath, loginId, password, false, deviceToken, isGuestLogin);
		cache.refreshMemberInfo(dto);
		contractLogic.initializeContractServiceOption();
		// ログイン状態でユーザ情報を保存する
		dto.loginStatus = LoginStatus.LoggedIn.statusCode();
		saveMemberInfo(dto, urlPath);
	}

	/**
	 * ユーザ情報の保存
	 * 
	 * @param dto
	 * @param urlPath
	 */
	public void saveMemberInfo(MemberInfoDto dto, String urlPath) {
		// ユーザを全て削除して、新規にユーザ追加
		memberInfoDao.deleteMemberInfo();
		memberInfoDao.insertMemberInfo(dto);

		//	契約事業者のアカウント文字列を保存
		contractLogic.initializeUrlPath(urlPath);

		// キャッシュの更新
		cache.refreshMemberInfo(dto);

		if (dto.loginStatus == LoginStatus.LoggedIn.statusCode()) {
			// グループの初期化
			try {
				groupLogic.initializeGroups();
			} catch (Exception e) {
				// コンテンツリフレッシュでも同様の処理を行うのでここでは無視する
				Logger.e("groupLogic.initializeGroups() failed", e.toString());
			}

			//	待機中のダウンロードがあれば開始する。
			if (networkAdapter.isNetworkConnected()) {
				ContentDownloader.getInstance().kickTask();
			}
		}
	}

	public void updateDeviceTokenByMacAdress(String deviceToken) throws AcmsException, NetworkDisconnectedException {
		AcmsClient acms = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter);
		acms.updateDeviceToken(new UpdateDeviceTokenParameters(cache.getMemberInfo().sid, null, ABVEnvironment.getInstance().deviceId, deviceToken, ABVEnvironment.AppId, ABVEnvironment.getInstance().appVersion));
	}

	public void updateDeviceTokenByUUID(String deviceToken) throws AcmsException, NetworkDisconnectedException {
		AcmsClient acms = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter);
		acms.updateDeviceToken(new UpdateDeviceTokenParameters(cache.getMemberInfo().sid, ABVEnvironment.getInstance().deviceId, null, deviceToken, ABVEnvironment.AppId, ABVEnvironment.getInstance().appVersion));
	}

	/**
	 * ChangePasswordTypeをLoginStatusに変換する。
	 * 
	 * @param changePasswordType
	 * @return
	 */
	public int convertLoginStatusFromChangePasswordType(int changePasswordType) {
		int loginStatus;
		switch (changePasswordType) {
		case RequirePasswordChangeType.NEWLOGIN_ENFORCEMENT:
		case RequirePasswordChangeType.REMINDER_ENFORCEMENT:
			// パスワード強制変更の場合、ログアウトとする
			loginStatus = LoginStatus.LoggedOut.statusCode();
			break;
		default:
			loginStatus = LoginStatus.LoggedIn.statusCode();
			break;
		}
		return loginStatus;
	}

	/**
	 * ユーザ変更によるデータ削除が必要か返します
	 * 
	 * @param dto
	 * @return
	 * @throws NetworkDisconnectedException
	 * @throws ABVException
	 */
	public int isRequiredDeleteLocalData(MemberInfoDto dto) throws NetworkDisconnectedException, ABVException {
		int result;
		MemberInfoDto oldMemberInfoDto = memberInfoDao.getMemberInfo();

		if (oldMemberInfoDto == null) {
			// 既存ユーザがいない場合、警告なし
			result = DeleteDataType.None;
		} else {
			if (oldMemberInfoDto.loginId.equals(dto.loginId)) {
				// 同じユーザの場合、警告なし
				result = DeleteDataType.None;
			} else {
				if (contractLogic.getUDIDOverwiteFlag()) {
					if (isDiffUserGroup(dto.sid)) {
						// 異なるグループの場合、全て削除
						result = DeleteDataType.All;
					} else {
						// 同一グループの場合
						// 警告なし
						result = DeleteDataType.None;
					}
				} else {
					// UDID上書きがfalseの場合、全て削除
					result = DeleteDataType.All;
				}
			}
		}
		return result;
	}

	private boolean isDiffUserGroup(String sid) throws NetworkDisconnectedException, ABVException {
		boolean result = false;
		// 新しいユーザとのグループ比較
		Integer[] oldUserGroupIds = groupLogic.getLocalUserGroupIds();
		Integer[] newUserGroupIds = groupLogic.getServerUserGroupIds(sid);

		if (oldUserGroupIds == null || newUserGroupIds == null) {
			result = true;
		} else {
			if (oldUserGroupIds.length == newUserGroupIds.length) {
				for (int i = 0; i < oldUserGroupIds.length; i++) {
					boolean hitFlg = false;
					for (int j = 0; j < newUserGroupIds.length; j++) {
						if (oldUserGroupIds[i].equals(newUserGroupIds[j])) {
							hitFlg = true;
							break;
						}
					}
					if (hitFlg == false) {
						result = true;
						break;
					}
				}
			} else {
				result = true;
			}
		}
		return result;
	}

	/**
	 * パスワードのチェックを行い、正しければログイン状態にユーザ情報を更新する<br>
	 * パスワードが誤っている場合ABVExceptionをthrowする。
	 * 
	 * @param dto
	 * @param password
	 * @throws ABVException
	 */
	public void offLineLogin(MemberInfoDto dto, String password) throws ABVException {
		// パスワードチェック
		if(checkPassword(dto, password)){
			setPasswordLockInfo(true);
			dto.loginStatus = LoginStatus.LoggedIn.statusCode();
			memberInfoDao.updateMemberInfo(dto);
			cache.refreshMemberInfo(dto);
		} else {
			setPasswordLockInfo(false);
			throw new ABVException(ABVExceptionCode.S_E_ACMS_L001, "E129");
		}
	}

	/**
	 * ロック解除のためパスワードをチェックします。
	 * @param password パスワードです。
	 * @return パスワード変更に成功するとtrueを返します。
	 * @throws ABVException　キャッシュにログイン中のユーザ情報がありません。ログインしていない可能性があります。
	 * @throws Exception その他、例外です。
	 * @since 1.0.0
	 */
	public boolean checkPassword(String password) {
		boolean result;
		
		MemberInfoDto dto = cache.getMemberInfo();
		if (dto != null) {
			if (StringUtil.isNullOrWhiteSpace(password)) {
				throw new IllegalArgumentException("argument oldPassword and newPassword not allowed null or white space. ");
			}			
			String encryptedPassword = SecurityUtil.encryptPassword(password, ABVEnvironment.LoginPasswordAESKey);
			result = dto.password.equals(encryptedPassword);
		} else {
			throw new ABVRuntimeException(ABVExceptionCode.C_E_SECURITY_1004);
		}
		
		return result;
	}

	public boolean checkPassword(MemberInfoDto dto, String password) {
		String encryptedPassword = SecurityUtil.encryptPassword(password, ABVEnvironment.LoginPasswordAESKey);
		return dto.password.equals(encryptedPassword);
	}

	/**
	 * パスワードを変更します。
	 * @param oldPassword 既存のパスワードです。
	 * @param newPassword　新しいパスワードです。
	 * @return パスワード変更に成功するとtrueを返します。
	 * @throws ABVException　キャッシュにログイン中のユーザ情報がありません。ログインしていない可能性があります。
	 * @throws Exception その他、例外です。
	 * @since 1.0.0
	 */
	public boolean changePassword(String oldPassword, String newPassword, boolean UpdateFlg) throws NetworkDisconnectedException, ABVException {
		boolean result;
		
		MemberInfoDto dto = cache.getMemberInfo();
		
		if (dto != null) {
			if (StringUtil.isNullOrWhiteSpace(oldPassword) || StringUtil.isNullOrWhiteSpace(newPassword)) {
				throw new IllegalArgumentException("argument oldPassword and newPassword not allowed null or white space. ");
			}
			
			String oldEncryptedPassword = SecurityUtil.encryptPassword(oldPassword, ABVEnvironment.LoginPasswordAESKey);
			String newEncryptedPassword;
			
			if (!dto.password.equals(oldEncryptedPassword)) {
				throw new ABVException(ABVExceptionCode.C_E_SECURITY_1005);
			}
			//サーバーでチェックするのでここではチェックを行わない
			//SecurityUtil.validatePassword(newPassword);
			
			PasswordChangeParameters param = new PasswordChangeParameters(dto.sid, dto.loginId, oldPassword, newPassword, env.appVersion, ABVEnvironment.AppId);
			result = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).passwordChange(param);
					
			if (result) {
				newEncryptedPassword = SecurityUtil.encryptPassword(newPassword, ABVEnvironment.LoginPasswordAESKey);
				
				dto.password = newEncryptedPassword;
				dto.loginStatus = LoginStatus.LoggedIn.statusCode();
				
				// UpdateFlgがtrueの時だけ更新を行うと、パスワード変更してもローカルのDBが更新されないのでコメントアウト
//				if (UpdateFlg) {
				if (memberInfoDao.updateMemberInfo(dto) == false) {
					// 初回ログイン時のパスワード変更
					memberInfoDao.insertMemberInfo(dto);
				}
					
//				}
				
				cache.refreshMemberInfo(dto);
			}
		} else {
			throw new ABVException(ABVExceptionCode.C_E_SECURITY_1004);
		}
		
		return result;
	}
	
	/**
	 * ログアウトします。キャッシュデータは全て削除されます。
	 * @throws ABVException　ログアウトできません。キャッシュにログイン中のユーザ情報がありません。ログインしていない可能性があります。
	 * @throws Exception その他、例外です。
	 * @since 1.0.0
	 */
	public void signoutUser() throws ABVException {
		MemberInfoDto dto = cache.getMemberInfo();
		
		if (dto != null) {
			dto.loginStatus = LoginStatus.LoggedOut.statusCode();
			memberInfoDao.updateMemberInfo(dto);
		} else {
			throw new ABVException(ABVExceptionCode.C_E_SECURITY_1004);
		}
		
		cache.removeCache();
	}
	
	/**
	 * 一定期間経過後アプリロックによってログアウトします。キャッシュデータは全て削除されます。
	 * @throws ABVException　ログアウトできません。キャッシュにログイン中のユーザ情報がありません。ログインしていない可能性があります。
	 * @throws Exception その他、例外です。
	 * @since 1.0.0
	 */
	public void limitLoginSignoutUser() throws ABVException {
		MemberInfoDto dto = cache.getMemberInfo();
		
		if (dto != null) {
			dto.loginStatus = LoginStatus.LimitLogin.statusCode();
			memberInfoDao.updateMemberInfo(dto);
		} else {
			throw new ABVException(ABVExceptionCode.C_E_SECURITY_1004);
		}
		
		cache.removeCache();
	}

	/**
	 * 一定期間経過後強制ログアウトによってログアウトします。キャッシュデータは全て削除されます。
	 * @throws ABVException　ログアウトできません。キャッシュにログイン中のユーザ情報がありません。ログインしていない可能性があります。
	 * @throws Exception その他、例外です。
	 * @since 1.0.0
	 */
	public void forceSignoutUser() throws ABVException {
		MemberInfoDto dto = cache.getMemberInfo();

		if (dto != null) {
			dto.loginStatus = LoginStatus.ForceLoggedOut.statusCode();
			memberInfoDao.updateMemberInfo(dto);
//		} else {
//			throw new ABVException(ABVExceptionCode.C_E_SECURITY_1004); // これはすでにログアウト状態のため不要
		}

		cache.removeCache();
	}

	public MemberInfoDto findMemberInfoByLoginStatus(LoginStatus loginStatus) {
		MemberInfoDto dto = memberInfoDao.getMemberInfo(loginStatus.statusCode());
		return dto;
	}
	
	/**
	 * サーバからユーザ認証を行います。初回ログイン時のみ使います。
	 * @param urlPath 事業者ACCOUNT
	 * @param loginId ユーザのログインID
	 * @param password ユーザのログインパスワード
	 * @return ログインに成功するとユーザ情報を格納した {@link MemberInfoDto} のインスタンスを返します。失敗すると例外を投げます。
	 * @throws NetworkDisconnectedException インターネットにつながっていないため、ログインできません。
	 * @throws ABVException インターネットにはつながっているが、ログインは失敗しました。失敗した原因は、{@link ABVException#getCode()} から確認できます。
	 * @throws Exception その他、例外です。
	 * @since 1.0.0
	 */
	private MemberInfoDto newAppStoreLogin(String urlPath, String loginId, String password, boolean retry, String deviceToken) throws Exception {
		MemberInfoDto dto;
		
		try {
			AcmsClient acms = AcmsClient.getInstance(urlPath, networkAdapter);
			
			String deviceId = ABVEnvironment.getInstance().deviceId;
			if (StringUtil.isNullOrEmpty(deviceId)) {
				throw new ABVException(ABVExceptionCode.C_E_SYSTEM_0004);
			}

			dto = new MemberInfoDto();

			cache.setUrlPath(urlPath);

			if(ABVEnvironment.getInstance().deviceIdType == Constant.DeviceIdType.MAC_ADDRESS){
				NewAppStoreLoginParameters loginParam = new NewAppStoreLoginParameters(urlPath, loginId, password, ABVEnvironment.AppId , ABVEnvironment.getInstance().appVersion, ABVEnvironment.DeviceTypeId, deviceToken, deviceId);
				dto = acms.newAppStoreLogin(loginParam, dto);
			} else {
				AppStoreNewLoginParameters loginParam = new AppStoreNewLoginParameters(urlPath, loginId, password, ABVEnvironment.AppId , ABVEnvironment.getInstance().appVersion, ABVEnvironment.DeviceTypeId, deviceToken, deviceId);
				dto = acms.appNewStoreLogin(loginParam, dto);
			}

			if (dto.lastChangePasswordDate == null) {
				dto.setLastChangePasswordDate(DateTimeUtil.getCurrentSqlDate());
			}
			setPasswordLockInfo(true);
			
		} catch (NetworkDisconnectedException e) {
			//	インターネットに繋がっていないため、ログインできない。
			throw e;
		} catch (AcmsException e) {
			if (e.getMessage().equals(AcmsAuthenticationErrorCode.L001.name())) {
				//	ユーザＩＤ又はパスワードが違う
				//	UIに知らせる
				setPasswordLockInfo(false);
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L001, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L002.name())) {
				//	別のユーザが端末を利用中
				//	UIに知らせる
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L002, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L003.name())) {
				//	UDID未登録
				//	デバイストークン登録から再度行う。
				//	※L003はACMS1.6でdeprecated
                if (retry) {
                    //	リトライは一回のみとする。
                    throw new ABVException(ABVExceptionCode.C_E_SECURITY_1001, e);
                } else {
                    dto = newAppStoreLogin(urlPath, loginId, password, true, deviceToken);
                }
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L004.name())) {
				//	ユーザが別の端末を使用中
				//	UIに知らせる
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L004, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L005.name())) {
				//	ユーザが属する事業者がサービス利用中ではない
				//	UIに知らせる
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L005, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L006.name())) {
				//	端末未登録（自動登録無し）
				//	UIに知らせる
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L006, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L007.name())) {
				//	UDID登録済み・デバイストークン未登録（UDID自動登録あり）
				//	デバイストークン登録から再度行う。
				//	※L007はACMS1.6でdeprecated
                if (retry) {
                    //	リトライは一回のみとする。
                    throw new ABVException(ABVExceptionCode.C_E_SECURITY_1001, e);
                } else {
                    dto = newAppStoreLogin(urlPath, loginId, password, true, deviceToken);
                }
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L008.name())) {
				//	利用中止の事業者
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L008, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L009.name())) {
				//	ユーザが別の端末を使用中
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L009, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L010.name())) {
				//	端末IDが特定できない。
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L010, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L012.name())) {
				//	デバイスのアクティブ数を超過。
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L012, e);
			} else {
				//	不明なエラー
				throw new ABVException(ABVExceptionCode.C_E_SECURITY_1001, e);
			}
		} catch (Exception e) {
			throw e;
		}
		
		return dto;
	}
	private MemberInfoDto enterpriseNewLogin(String urlPath, String loginId, String password, boolean retry, String deviceToken) throws Exception {
		return enterpriseNewLogin(urlPath, loginId, password, retry, deviceToken, false);
	}
	
	private MemberInfoDto enterpriseNewLogin(String urlPath, String loginId, String password, boolean retry, String deviceToken, boolean isGuestLogin) throws Exception {
		MemberInfoDto dto;

		try {
			AcmsClient acms = AcmsClient.getInstance(urlPath, networkAdapter);

			String macAddress = ABVEnvironment.getInstance().deviceId;
			if (StringUtil.isNullOrEmpty(macAddress)) {
				throw new ABVException(ABVExceptionCode.C_E_SYSTEM_0004);
			}
			cache.setUrlPath(urlPath);
			dto = new MemberInfoDto();
			int guest = isGuestLogin ? 1 : 0;
			EnterpriseNewLoginParameters loginParam = new EnterpriseNewLoginParameters(loginId, password, ABVEnvironment.AppId, ABVEnvironment.getInstance().appVersion, ABVEnvironment.DeviceTypeId, deviceToken, macAddress, guest);

			dto = acms.enterpriseNewLogin(loginParam, dto);

			if (dto.lastChangePasswordDate == null) {
				dto.setLastChangePasswordDate(DateTimeUtil.getCurrentSqlDate());
			}
			setPasswordLockInfo(true);
		} catch (NetworkDisconnectedException e) {
			//	インターネットに繋がっていないため、ログインできない。
			throw e;
		} catch (AcmsException e) {
			if (e.getMessage().equals(AcmsAuthenticationErrorCode.L001.name())) {
				//	ユーザＩＤ又はパスワードが違う
				//	UIに知らせる
				setPasswordLockInfo(false);
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L001, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L002.name())) {
				//	別のユーザが端末を利用中
				//	UIに知らせる
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L002, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L003.name())) {
				//	UDID未登録
				//	デバイストークン登録から再度行う。
				//	※L003はACMS1.6でdeprecated
                if (retry) {
                    //	リトライは一回のみとする。
                    throw new ABVException(ABVExceptionCode.C_E_SECURITY_1001, e);
                } else {
                    dto = enterpriseNewLogin(urlPath, loginId, password, true, deviceToken);
                }
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L004.name())) {
				//	ユーザが別の端末を使用中
				//	UIに知らせる
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L004, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L005.name())) {
				//	ユーザが属する事業者がサービス利用中ではない
				//	UIに知らせる
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L005, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L006.name())) {
				//	端末未登録（自動登録無し）
				//	UIに知らせる
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L006, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L007.name())) {
				//	UDID登録済み・デバイストークン未登録（UDID自動登録あり）
				//	デバイストークン登録から再度行う。
				//	※L007はACMS1.6でdeprecated
                if (retry) {
                    //	リトライは一回のみとする。
                    throw new ABVException(ABVExceptionCode.C_E_SECURITY_1001, e);
                } else {
                    dto = enterpriseNewLogin(urlPath, loginId, password, true, deviceToken);
                }
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L008.name())) {
				//	利用中止の事業者
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L008, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L009.name())) {
				//	ユーザが別の端末を使用中
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L009, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L010.name())) {
				//	端末IDが特定できない。
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L010, e);
			} else if (e.getMessage().equals(AcmsAuthenticationErrorCode.L012.name())) {
				//	デバイスのアクティブ数を超過。
				throw new ABVException(ABVExceptionCode.S_E_ACMS_L012, e);
			} else {
				//	不明なエラー
				throw new ABVException(ABVExceptionCode.C_E_SECURITY_1001, e);
			}
		} catch (Exception e) {
			throw e;
		}

		return dto;
	}

	/**
	 * ログイン失敗情報の更新
	 * 
	 * @param success
	 */
	private void setPasswordLockInfo(boolean success) {
		PasswordLockInfoDao plidao = AbstractDao.getDao(PasswordLockInfoDao.class);
		PasswordLockInfoDto plidto;
		List<PasswordLockInfoDto> list = plidao.getPasswordLockInfo();
		if (list != null && list.size() > 0) {
			for (PasswordLockInfoDto passwordLockInfoDto : list) {
				if (passwordLockInfoDto.id == 1) {
					plidto = passwordLockInfoDto;
					if (success) {
						// ログイン成功の場合
						plidto.invalidPasswordCount = (short) 0;
						plidto.passwordLockDate = null;
					} else {
						if ((short) (plidto.invalidPasswordCount + 1) >= ABVEnvironment.InvalidPasswordLimit) {
							plidto.invalidPasswordCount = (short) 0;

							Date serverDate;
							try {
								serverDate = getServerTime();
							} catch (Exception e) {
								serverDate = DateTimeUtil.getCurrentDate();
							}

							plidto.passwordLockDate = serverDate;
						} else {
							plidto.invalidPasswordCount = (short) (plidto.invalidPasswordCount + 1);
							plidto.passwordLockDate = null;
						}
					}
					if (ABVEnvironment.getInstance().isCheckInvalidPasswordLimit) {
						// パスワード失敗チェックがＯＮの場合にのみ更新する⇒最終的にロックされる
						plidao.updatePasswordLockInfo(plidto);
					}
					
					break;
				}
			}
		} else {
			plidto = new PasswordLockInfoDto();
			plidto.id = 1;
			if (success) {
				plidto.invalidPasswordCount = (short) 0; // ログイン成功の場合
			} else {
				plidto.invalidPasswordCount = (short) 1;
			}
			plidto.passwordLockDate = null;
			plidao.insertPasswordLockInfo(plidto);
		}
	}
	
	public boolean isPasswordLocked(Date lockDate) {
		Calendar calUnLockDate = new GregorianCalendar(new Locale("ja", "JP"));
		calUnLockDate.setTime(lockDate);
		calUnLockDate.add(Calendar.HOUR, 1);
		Calendar calNow = new GregorianCalendar(new Locale("ja", "JP"));
		Date serverDate;
		try {
			serverDate = getServerTime();
		} catch (Exception e) {
			serverDate = DateTimeUtil.getCurrentDate();
		}
		calNow.setTime(serverDate);
		return calUnLockDate.after(calNow);
	}
    
    /**
     * サーバとの最後の通信で取得したサーバ時間(UTC)を返します。<br>
     * この時間がサーバの現在の時間を意味することではありません。<br>
     * また、ファイルのダウンロードを行う通信ではサーバ時間を取得しません。
     * @return サーバから最後に取得したサーバ時間です。
     * @throws NetworkDisconnectedException 
     * @throws AcmsException 
     */
    public Date getServerTime() throws AcmsException, NetworkDisconnectedException {
    	ServerTimeZoneJSON json = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).serverTimeZone(new ServerTimeParameters(env.getServerTimeKey()));
    	ABVDataCache.getInstance().setDiffMinFromUTC(json.timeZone);
    	return DateTimeUtil.toDate(json.presentTimeUTC, "UTC", DateTimeFormat.yyyyMMddHHmmss_TZ);
	}
    
    public RequirePasswordChangeCode requirePasswordChange() {
    	RequirePasswordChangeCode code = RequirePasswordChangeCode.None;
    	
    	try {
    		AcmsParameters param = new AcmsParameters(cache.getMemberInfo().sid);
    		code = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).requirePasswordChange(param);
		} catch (Exception e) {
			Logger.e("requirePasswordChange error", e.getLocalizedMessage()); // ignore
		}
    	
    	return code;
    }

	public int getAuthLevel() throws AcmsException, NetworkDisconnectedException {
		AcmsParameters param = new AcmsParameters(cache.getMemberInfo().sid);
		AuthLevelJSON json = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).getAuthLevel(param);
		return json.authLevel;
	}
	
	/**
	 * パスワード誤りによるログイン制限情報を取得します。<br>
	 * 無ければ新たに作成します。
	 * 
	 * @return
	 */
	public PasswordLockInfoDto getPasswordLockInfoDto() {
		PasswordLockInfoDto passwordLockInfoDto = null;

		List<PasswordLockInfoDto> passwordLockInfoList = passwordLockInfoDao.getPasswordLockInfo();
		if (passwordLockInfoList != null && passwordLockInfoList.size() > 0) {
			for (PasswordLockInfoDto dto : passwordLockInfoList) {
				if (dto.id == 1) {
					passwordLockInfoDto = dto;
					break;
				}
			}
		} else {
			passwordLockInfoDto = new PasswordLockInfoDto();
			passwordLockInfoDto.id = 1;
			passwordLockInfoDto.invalidPasswordCount = (short) 0;
			passwordLockInfoDto.passwordLockDate = null;
			passwordLockInfoDao.insertPasswordLockInfo(passwordLockInfoDto);
		}

		return passwordLockInfoDto;
	}

}
