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

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import jp.agentec.abook.abv.bl.acms.type.LoginStatus;
import jp.agentec.abook.abv.bl.acms.type.SecurityPolicyType;
import jp.agentec.abook.abv.bl.acms.type.ServiceOption.ServiceOptionId;
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.dao.AbstractDao;
import jp.agentec.abook.abv.bl.data.dao.AcmsDao;
import jp.agentec.abook.abv.bl.data.dao.MemberInfoDao;
import jp.agentec.abook.abv.bl.data.dao.ServiceOptionDao;
import jp.agentec.abook.abv.bl.dto.MemberInfoDto;
import jp.agentec.abook.abv.bl.dto.ServiceOptionDto;
import jp.agentec.abook.abv.bl.logic.ContractLogic;
import jp.agentec.adf.util.DateTimeUtil;
import jp.agentec.adf.util.NumericUtil;
import jp.agentec.adf.util.StringUtil;

// TODO: clone()使っているところは不要なので次のバージョンアップのタイミングで取る
public class ABVDataCache {
	private static final String TAG = "Cache";

	private static ABVDataCache instance = null;

	public ServiceOption serviceOption = null;

	private MemberInfoDto memberInfoDto = null;
	private List<ServiceOptionDto> serviceOptionDtoList = null;

	private String urlPath;
	private Integer diffMinFromUTC; // ACMSサーバ時間におけるUTCから差分 deliveryStartDateUTCを含まない旧コンテンツ用に必要


	private Date lastPresentTime = DateTimeUtil.getCurrentDate(); //	最後の通信時に取得したサーバの時間
	private static final int SEVER_ALERT_INTERVAL = 30; //システム日付チェック前後期間(分)

	// Serverから取得したmasterDataのfetchDateを一時的に保存するための変数
	public String tempMasterDataFetchDate = null;

	/**
	 * 未指定ジャンルのID
	 * @since 1.0.0
	 */
	private int defaultCategoryId = -1;
	/**
	 * ルートグループのID
	 * @since 1.0.0
	 */
	private int defaultGroupId = -1;

	public static synchronized ABVDataCache getInstance() {
		if (instance == null) {
			instance = new ABVDataCache();
		}

		return instance;
	}

	private ABVDataCache() {
		serviceOption = new ServiceOption();
	}

	/**
	 * 指定したserviceOptionIdに当てはまるサービスオプション情報を返します。
	 * @param serviceOptionId　サービスオプションリストから取り出す情報の番号です。
	 * @return サービスオプション情報です。指定したserviceOptionIdがサービスオプションリストに存在しない場合はnullを返します。
	 * @since 1.0.0
	 */
	public ServiceOptionDto getServiceOption(int serviceOptionId) {
		if (serviceOptionDtoList != null) {
			for (ServiceOptionDto serviceOptionDto : Collections.unmodifiableCollection(serviceOptionDtoList)) {
				if (serviceOptionDto.serviceOptionId == serviceOptionId) {
					return serviceOptionDto;
				}
			}
		}

		return null;
	}

	/**
	 * キャッシュに格納されているユーザ情報を返します。
	 * @return ユーザ情報を格納した {@link MemberInfoDto} のインスタンスを返します。
	 * @since 1.0.0
	 */
	public MemberInfoDto getMemberInfo() {
		if (memberInfoDto == null) {
			refreshMemberInfo();
		}
		return memberInfoDto;
	}

	public int getDefaultCategoryId() {
		return defaultCategoryId;
	}

	public int getDefaultGroupId() {
		return defaultGroupId;
	}

	/**
	 * キャッシュのサービスオプション情報をキャッシュに追加します。既に同じサービスオプション情報が存在する場合は更新します。
	 * @param serviceOptionDto サービスオプション情報です。
	 * @since 1.0.0
	 */
	public synchronized void addServiceOption(ServiceOptionDto serviceOptionDto) {
		boolean exist = false;

		if (serviceOptionDtoList == null) {
			serviceOptionDtoList = new ArrayList<ServiceOptionDto>();
		} else {
			for (int i = 0; i < serviceOptionDtoList.size(); i++) {
				ServiceOptionDto dto = serviceOptionDtoList.get(i);
				if (dto.serviceOptionId == serviceOptionDto.serviceOptionId) {
					serviceOptionDtoList.set(i, serviceOptionDto);
					exist= true;
					break;
				}
			}
		}

		if (!exist) {
			serviceOptionDtoList.add((ServiceOptionDto)serviceOptionDto.clone());
		}
	}

	public void addServiceOptions(List<ServiceOptionDto> list) {
		if (list != null) {
			for (ServiceOptionDto dto : list) {
				addServiceOption((ServiceOptionDto)dto.clone());
			}
		}
	}

	/**
	 * キャッシュのユーザ情報を更新します。
	 * @param dto　更新するユーザ情報です。
	 * @since 1.0.0
	 */
	public void refreshMemberInfo(MemberInfoDto dto) {
		memberInfoDto = dto;
	}

	private void refreshMemberInfo() {
		MemberInfoDao dao = AbstractDao.getDao(MemberInfoDao.class);
		memberInfoDto = dao.getMemberInfo(LoginStatus.LoggedIn.statusCode());
	}

	public void refreshDefaultCategoryId(int categoryId) {
		defaultCategoryId = categoryId;
	}

	public void refreshDefaultGroupId(int groupId) {
		defaultGroupId = groupId;
	}

	public boolean refreshServiceOptions() {
		boolean refreshed = false;

		try {
			ServiceOptionDao dao = AbstractDao.getDao(ServiceOptionDao.class);
			List<ServiceOptionDto> list = dao.getServiceOptions();

			if (list != null && list.size() > 0) {
				for (ServiceOptionDto dto : list) {
					addServiceOption(dto);
				}

				refreshed = true;
			}
		} catch (Exception e) {
			Logger.e(TAG, "refreshServiceOptions failed.", e);
		}

		return refreshed;
	}

	public void setUrlPath(String urlPath) {
		this.urlPath = urlPath;
	}

	public String getUrlPath() {
		boolean firstLogin = false;

		if (memberInfoDto == null) {
			try {
				MemberInfoDao dao = AbstractDao.getDao(MemberInfoDao.class);
				MemberInfoDto dto = dao.getMemberInfo(LoginStatus.LoggedIn.statusCode());

				if (dto == null) {
					firstLogin = true;
				}
			} catch (Exception e) {
				//	何もしない
			}
		}

		if (firstLogin) {
			urlPath = null;
		} else {
			if (StringUtil.isNullOrEmpty(urlPath)) {
				refreshUrlPath();
			}
		}


		return urlPath;
	}

	public boolean refreshUrlPath() {
		boolean refreshed = false;

		try {
			AcmsDao dao = AbstractDao.getDao(AcmsDao.class);
			urlPath = dao.getUrlPath();
			refreshed = true;
		} catch (Exception e) {
			Logger.e(TAG, "refreshUrlPath failed.", e);
		}

		return refreshed;
	}

	/**
	 * キャッシュのデータを全て削除します。
	 * @since 1.0.0
	 */
	public synchronized void removeCache() {
		memberInfoDto = null;
		serviceOptionDtoList = null;
		defaultCategoryId = -1;
		defaultGroupId = -1;
		urlPath = null;
		tempMasterDataFetchDate = null;
	}

	public void setLastPresentTime(Date lasttime) {
		if (lasttime != null) {
			this.lastPresentTime = lasttime;
		}
	}

public class ServiceOption {
	public boolean isUndelivarableDelete() {
		return isServiceOptionEnable(ServiceOptionId.UndeliverableDelete);
	}

	public boolean isNoAuthDelete() {
		return isServiceOptionEnable(ServiceOptionId.NoauthDelete);
	}

	public SecurityPolicyType getForcePwChangeOnLogin() {
		SecurityPolicyType code = SecurityPolicyType.None;

		ServiceOptionDto dto = getServiceOption(ServiceOptionId.ForcePwChangeOnLogin);

		if (dto != null) {
			code = SecurityPolicyType.parse(NumericUtil.parseInt(dto.val));
		}

		return code;
	}

	public SecurityPolicyType getForcePwChangePeriodically() {
		SecurityPolicyType code = SecurityPolicyType.None;

		ServiceOptionDto dto = getServiceOption(ServiceOptionId.ForcePwChangePeriodically);

		if (dto != null) {
			code = SecurityPolicyType.parse(NumericUtil.parseInt(dto.val));
		}

		return code;
	}

	public boolean isForceLoginPeriodically() {
		return isServiceOptionEnable(ServiceOptionId.ForceLoginPeriodically);
	}

	public boolean isUseridUdidOverwrite() {
		return isServiceOptionEnable(ServiceOptionId.UseridUdidOverwrite);
	}

	public boolean isMarking() {
		return isServiceOptionEnable(ServiceOptionId.Marking);
	}

	public boolean isPdfLinkDisp() {
		return isServiceOptionEnable(ServiceOptionId.PdfLinkDisp);
	}

	public boolean isCatalogEdition() {
		return isServiceOptionEnable(ServiceOptionId.CatalogEdition);
	}

	public boolean isBizEdition() {
		return !isServiceOptionEnable(ServiceOptionId.CatalogEdition) && !isServiceOptionEnable(ServiceOptionId.Publisher);
	}

	public boolean isReportGPS() {
		return isServiceOptionEnable(ServiceOptionId.REPORT_GPS);
	}

	public boolean isRoutineTaskReport() {
		return isServiceOptionEnable(ServiceOptionId.OperationReportType);
	}

	public boolean isEnquete() {
		return isServiceOptionEnable(ServiceOptionId.Enquete);
	}

	public boolean isExam() {
		return isServiceOptionEnable(ServiceOptionId.Exam);
	}

	public boolean isQuiz() {
		return isServiceOptionEnable(ServiceOptionId.Quiz);
	}

	public boolean isTransmitExamAtOffline() {
		return isServiceOptionEnable(ServiceOptionId.TransmitExamAtOfflLine);
	}

	public boolean isUseGroup() {
		return isServiceOptionEnable(ServiceOptionId.UseGroup);
	}

	public boolean isUseGenre() {
		return isServiceOptionEnable(ServiceOptionId.UseGenre);
	}

	public boolean isPanoImage() {
		return isServiceOptionEnable(ServiceOptionId.PanoImage);
	}

	public boolean isServiceOptionEnable(int serviceOptionId) {
		ServiceOptionDto dto = getServiceOption(serviceOptionId);
		if (dto != null) {
			return ContractLogic.getBooleanValue(dto.val);
		}
		return false;
	}

	public String operationChangeTime() {
		ServiceOptionDto dto = getServiceOption(ServiceOptionId.OperationChangeTime);
		return dto.val;
	}

	/**
	 * 作業種別のサービスオプション情報取得
	 * @return
	 */
	public boolean isOperationGroupMaster() {
		return isServiceOptionEnable(ServiceOptionId.OperationGroupMaster);
	}

	/**
	 * I/O帳票使用のサービスオプション情報取得
	 * @return
	 */
	public boolean isUnableIOReport() {
		return isServiceOptionEnable(ServiceOptionId.UsableIOReport);
	}
}

	public void setDiffMinFromUTC(String timeZone) {
		// UserAuthenticateLogic.getServerTimeから移行
		// "timeZone":"GMT +09:00"から抽出して、差分の分数を設定
		if(timeZone.equals("GMT 00:00")){
			// GMT 00:00の場合GMT +00:00に置き換え
			timeZone = "GMT +00:00";
		}
		diffMinFromUTC = (timeZone.charAt(4)=='-'?-1:1) * Integer.parseInt(timeZone.substring(5, 7)) * 60 + Integer.parseInt(timeZone.substring(8, 10));
	}

	public Integer getDiffMinFromUTC() {
		return diffMinFromUTC;
	}

	/**
	 * APIのURLにユーザ情報付与の特定キー作成
	 * @return 「事業者文字列」＋「ログインID」文字列をMD5変換を返します。nullの場合もあります。
	 */
	public String getAdummyKey() {
		if (getUrlPath() != null && getMemberInfo().loginId != null) {
			return SecurityUtil.getMd5Hash(getUrlPath() + getMemberInfo().loginId);
		}
		return null;
	}
}
