package jp.agentec.abook.abv.bl.acms.client;

import org.json.adf.JSONObject;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Observer;

import jp.agentec.abook.abv.bl.acms.client.json.AcmsBooleanResultJSON;
import jp.agentec.abook.abv.bl.acms.client.json.AcmsCommonJSON;
import jp.agentec.abook.abv.bl.acms.client.json.AcmsMessageJSON;
import jp.agentec.abook.abv.bl.acms.client.json.AppLatestVersionJSON;
import jp.agentec.abook.abv.bl.acms.client.json.AuthLevelJSON;
import jp.agentec.abook.abv.bl.acms.client.json.CategoriesJSON;
import jp.agentec.abook.abv.bl.acms.client.json.ContentCheckDeliverableJSON;
import jp.agentec.abook.abv.bl.acms.client.json.ContentVersionsJSON;
import jp.agentec.abook.abv.bl.acms.client.json.DashboardStatusJSON;
import jp.agentec.abook.abv.bl.acms.client.json.GroupsJSON;
import jp.agentec.abook.abv.bl.acms.client.json.LockReportJSON;
import jp.agentec.abook.abv.bl.acms.client.json.LogSendFlagJSON;
import jp.agentec.abook.abv.bl.acms.client.json.MasterDataJSON;
import jp.agentec.abook.abv.bl.acms.client.json.NewAppStoreLoginJSON;
import jp.agentec.abook.abv.bl.acms.client.json.OperationGroupMasterJSON;
import jp.agentec.abook.abv.bl.acms.client.json.PushMessageListJSON;
import jp.agentec.abook.abv.bl.acms.client.json.RequirePasswordChangeJSON;
import jp.agentec.abook.abv.bl.acms.client.json.ResultJSON;
import jp.agentec.abook.abv.bl.acms.client.json.ServerTimeZoneJSON;
import jp.agentec.abook.abv.bl.acms.client.json.ServiceOptionsJSON;
import jp.agentec.abook.abv.bl.acms.client.json.FixPushMessageJSON;
import jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON;
import jp.agentec.abook.abv.bl.acms.client.json.OperationListJSON;
import jp.agentec.abook.abv.bl.acms.client.json.SceneEntryJSON;
import jp.agentec.abook.abv.bl.acms.client.json.UnlockReportJSON;
import jp.agentec.abook.abv.bl.acms.client.json.WorkerGroupJSON;
import jp.agentec.abook.abv.bl.acms.client.parameters.AbstractAcmsLoginParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.AcmsContentParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.AcmsParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.AddMemberGroupParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.AppStoreNewLoginParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.CheckSendLogParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.ContentReadingLogParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.EnqueteReplyParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.EnterpriseLoginParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.EnterpriseNewLoginParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.GetContentParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.GetEnqueteReplyParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.GetOperationDataParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.GetTaskFileParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.LockReportParameters;
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.PostEnqueteReplyParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.ReadPushMessageParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.SendPushMessageParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.ServerTimeParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.UnlockReportParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.UpdateDeviceTokenParameters;
import jp.agentec.abook.abv.bl.acms.type.AcmsApis;
import jp.agentec.abook.abv.bl.acms.type.LoginStatus;
import jp.agentec.abook.abv.bl.acms.type.RequirePasswordChangeCode;
import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.Constant;
import jp.agentec.abook.abv.bl.common.Constant.ExceptionDetailMessage;
import jp.agentec.abook.abv.bl.common.constant.ABookKeys;
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.AcmsException;
import jp.agentec.abook.abv.bl.common.exception.JSONValidationException;
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.nw.NetworkAdapter;
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.MemberInfoDao;
import jp.agentec.abook.abv.bl.dto.CategoryDto;
import jp.agentec.abook.abv.bl.dto.ContentDto;
import jp.agentec.abook.abv.bl.dto.GroupDto;
import jp.agentec.abook.abv.bl.dto.MemberInfoDto;
import jp.agentec.abook.abv.bl.dto.ServiceOptionDto;
import jp.agentec.abook.abv.bl.dto.TaskReportDto;
import jp.agentec.abook.abv.bl.logic.ReportStatusLogic;
import jp.agentec.adf.net.http.HttpDownloadState;
import jp.agentec.adf.net.http.HttpFileDownloader;
import jp.agentec.adf.net.http.HttpMultipart;
import jp.agentec.adf.net.http.HttpParameterObject;
import jp.agentec.adf.net.http.HttpRequestSender;
import jp.agentec.adf.net.http.HttpResponse;
import jp.agentec.adf.util.DateTimeUtil;
import jp.agentec.adf.util.StringUtil;

import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.ReportStatus;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.Message;

/**
 * ACMSのAPIにアクセスするにはこのクラスを継承します。
 * @author Taejin Hong
 * @version　1.1.1 
 */
public class AcmsClient implements AcmsClientResponseListener {
	private static final String TAG = "ACMS";
	
	private static AcmsClient instance = null;
	private NetworkAdapter networkAdapter = null;
	protected ABVEnvironment env = null;
	
	private String urlPath;
	
	private Date lastPresentTime = DateTimeUtil.getCurrentDate();	//	最後の通信時に取得したサーバの時間

	/** masterData 専用の join タイムアウト倍率（並列 fetch 時もスレッドごとに独立） */
	private static final ThreadLocal<Integer> masterDataJoinMultiplier = new ThreadLocal<Integer>();

	/** 並列 API 時も lastCMSAccess 更新だけ直列化し、SQLite 競合を避ける */
	private static final Object ACMS_MEMBER_LAST_ACCESS_LOCK = new Object();
	/**
	 * {@link AcmsClient} のインスタンスを初期化します。
	 * @since 1.0.0
	 */
	private AcmsClient(NetworkAdapter networkAdapter) {
		env = ABVEnvironment.getInstance();
		
		this.networkAdapter = networkAdapter;
		//initializeApiUrl();
	}
	
	public static AcmsClient getInstance(NetworkAdapter networkAdapter) {
		if (instance == null) {
			instance = new AcmsClient(networkAdapter);
		}
		
		return instance;
	}
	
	public static AcmsClient getInstance(String urlPath, NetworkAdapter networkAdapter) {
		if (instance == null) {
			instance = new AcmsClient(networkAdapter);
		}
		
		instance.urlPath = urlPath;
		
		return instance;
	}
		

	/**
	 * ACMSからユーザ認証を行い、ユーザ情報を取得します。(初回ログイン)
	 * @param param {@link NewAppStoreLoginParameters} オブジェクトです。
	 * @param dto ユーザ情報を格納する{@link MemberInfoDto} オブジェクトです。
	 * @return ユーザ情報を格納した {@link MemberInfoDto} オブジェクトを返します。
	 * @since 1.1.0
	 */
	public MemberInfoDto newAppStoreLogin(NewAppStoreLoginParameters param, MemberInfoDto dto) throws NetworkDisconnectedException, ABVException {
		HttpResponse response = send(AcmsApis.ApiUrlNewAppStoreLogin, param);

		NewAppStoreLoginJSON json = new NewAppStoreLoginJSON(response.httpResponseBody);
		lastPresentTime = json.presentTime;
		
		return login(param, dto, json);
	}

    /**
     * ACMSからユーザ認証を行い、ユーザ情報を取得します。(初回ログイン) Versionが 6以上の専用
     * @param param {@link NewAppStoreLoginParameters} オブジェクトです。
     * @param dto ユーザ情報を格納する{@link MemberInfoDto} オブジェクトです。
     * @return ユーザ情報を格納した {@link MemberInfoDto} オブジェクトを返します。
     */
	public MemberInfoDto appNewStoreLogin(AppStoreNewLoginParameters param, MemberInfoDto dto) throws NetworkDisconnectedException, ABVException {
		HttpResponse response = send(AcmsApis.ApiUrlAppStoreNewLogin, param);

		NewAppStoreLoginJSON json = new NewAppStoreLoginJSON(response.httpResponseBody);
		lastPresentTime = json.presentTime;

		return login(param, dto, json);
	}
	
	/**
	 * ACMSからユーザ認証を行い、ユーザ情報を取得します。(２回目以降ログイン)
	 * @param param {@link EnterpriseLoginParameters} オブジェクトです。
	 * @param dto ユーザ情報を格納する{@link MemberInfoDto} オブジェクトです。
	 * @return ユーザ情報を格納した {@link MemberInfoDto} オブジェクトを返します。
	 * @since 1.1.0
	 */
	public MemberInfoDto enterpriseLogin(EnterpriseLoginParameters param, MemberInfoDto dto) throws NetworkDisconnectedException, ABVException {
		HttpResponse response = send(AcmsApis.ApiUrlEnterpriseLogin, param);

		NewAppStoreLoginJSON json = new NewAppStoreLoginJSON(response.httpResponseBody);
		lastPresentTime = json.presentTime;
		
		return login(param, dto, json);
	}
	
	public MemberInfoDto enterpriseNewLogin(EnterpriseNewLoginParameters param, MemberInfoDto dto) throws NetworkDisconnectedException, ABVException {
		HttpResponse response = send(AcmsApis.ApiUrlEnterpriseNewLogin, param);

		NewAppStoreLoginJSON json = new NewAppStoreLoginJSON(response.httpResponseBody);
		lastPresentTime = json.presentTime;

		return login(param, dto, json);
	}

	private <P extends AbstractAcmsLoginParameters, J extends NewAppStoreLoginJSON> MemberInfoDto login(P param, MemberInfoDto dto, J json)
			throws NetworkDisconnectedException, ABVException {
		dto.sid = json.sid;
		dto.loginId = param.getLoginId();
		dto.memberName = json.userName;
		dto.requiredPasswordChange = json.requirePasswordChange;
		dto.userId = json.userId;
		dto.workerCode = json.workerCode;

//		dto.resourcePatternType = json.resourcePatternType;  // 文言リソースパターンを取得する。

		if (json.result) {
			dto.invalidPasswordCount = (short) 0;
			dto.lastLoginDate = json.presentTime;
			
			//	パスワードは暗号化して保存する。
			dto.password = SecurityUtil.encryptPassword(param.getPassword(), ABVEnvironment.LoginPasswordAESKey);
		} else {
			if (json.requirePasswordChange == RequirePasswordChangeCode.FirstTime
					|| json.requirePasswordChange == RequirePasswordChangeCode.Required) {
				//	初回ログイン or 定期変更
				dto.loginStatus = LoginStatus.LoggedOut.statusCode();
			} else {
				//	変更不要なのにjson.resultがfalseであることは、サーバに何らかの間違いがあるので、例外処理。
				throw new ABVException(ABVExceptionCode.C_E_SECURITY_1001);
			}
		}
		
		return dto;
	}
	
	public void updateDeviceToken(UpdateDeviceTokenParameters param) throws AcmsException, NetworkDisconnectedException{
		send(AcmsApis.ApiUrlUpdateDeviceToken, param);
	}
	
	public boolean passwordChange(PasswordChangeParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiUrlPasswordChange, param);
		AcmsBooleanResultJSON json = new AcmsBooleanResultJSON(response.httpResponseBody);
		lastPresentTime = json.presentTime;
		
		return json.result;
	}
	
	public AcmsMessageJSON addMemberGroup(AddMemberGroupParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiUrlAddMemberGroup, param);
		return new AcmsMessageJSON(response.httpResponseBody);
	}
	
	/**
	 * SIDがACMS側で有効かどうかをチェックします。
	 * 
	 * @param sid
	 * @return
	 * @throws NetworkDisconnectedException
	 * @throws AcmsException
	 * @throws JSONValidationException
	 */
	public boolean checkSid(String sid) throws NetworkDisconnectedException, AcmsException {
		try {
			HttpResponse response = send(AcmsApis.ApiUrlCheckSid, new AcmsParameters(sid));
			AcmsCommonJSON json = new AcmsCommonJSON(response.httpResponseBody);
			return json.httpStatus == 200;
			
		} catch (AcmsException e) {
			if (e.getHttpStatus() == 400 || e.getHttpStatus() == 403) {
				Logger.w(TAG, "checkSid error." + e.toString());
				return false;
			}
			throw e;
		}
	}
	
	/**
	 * ACMSから、サーバのタイムゾーンを取得します。
	 * 
	 * @param param
	 * @return
	 * @throws NetworkDisconnectedException
	 * @throws JSONValidationException
	 * @throws AcmsException
	 */
	public ServerTimeZoneJSON serverTimeZone(ServerTimeParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiUrlServerTimeZone, param);
		return new ServerTimeZoneJSON(response.httpResponseBody);
	}
	
	/**
	 * ACMSから、契約事業者のサービスオプションを取得します。
	 * @param param {@link AcmsParameters} オブジェクトです。
	 * @return 契約事業者のサービスオプションを格納した {@link ServiceOptionDto} のリストを返します。
	 * @since 1.0.0
	 */
	public List<ServiceOptionDto> serviceOption(AcmsParameters param) throws NetworkDisconnectedException, AcmsException {
		Logger.d(TAG, "try to get serviceOption");
		HttpResponse response = send(AcmsApis.ApiUrlServiceOption, param);
		ServiceOptionsJSON json = new ServiceOptionsJSON(response.httpResponseBody);
		lastPresentTime = json.presentTime;
		return json.serviceOptionList;
	}
	

	/**
	 * ACMSから、コンテンツ読み終わり情報を入力します。
	 * @param param {@link ContentReadingLogParameters} オブジェクトです。
	 * @return 結果をを返します。
	 * @since 1.0.0
	 */
	public boolean contentReadingLog(ContentReadingLogParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiUrlContentReadingLog, param);
		AcmsBooleanResultJSON json = new AcmsBooleanResultJSON(response.httpResponseBody);
		lastPresentTime = json.presentTime;
		return json.result;
	}
	


	/**
	 * ACMSから、コンテンツダウンロード情報を入力します。
	 * @param param {@link AcmsParameters} オブジェクトです。
	 * @return 結果をを返します。
	 * @since 1.0.0
	 */
	public boolean contentDownloadLog(AcmsParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiUrlContentDownloadLog, param);
		AcmsBooleanResultJSON json = new AcmsBooleanResultJSON(response.httpResponseBody);
		lastPresentTime = json.presentTime;
		return json.result;
	}
	
	/**
	 * ACMSから、グループ情報を取得します。
	 * @param param {@link AcmsParameters} オブジェクトです。
	 * @return　グループ情報を格納した {@link GroupDto} のリストを返します。
	 * @since 1.0.0
	 */
	public List<GroupDto> group(AcmsParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiUrlGroup, param);
		GroupsJSON json = new GroupsJSON(response.httpResponseBody);
		lastPresentTime = json.presentTime;
		return json.groupList;
	}
	
	/**
	 * ACMSから、ユーザが所属しているグループ情報を取得します。
	 * @param param {@link AcmsParameters} オブジェクトです。
	 * @return　グループ情報を格納した {@link GroupDto} のリストを返します。
	 * @since 1.0.0
	 */
	public Integer[] userGroup(AcmsParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiUrlGroup, param);
		GroupsJSON json = new GroupsJSON(response.httpResponseBody);
		return json.userGroupIds;
	}
	
	/**
	 * ACMSから、ジャンル情報を取得します。
	 * 以前に取得した時点から変更のあるジャンルの情報のみ取得
	 * @param param {@link AcmsParameters} オブジェクトです。
	 * @return　ジャンル情報を格納した {@link CategoryDto} のリストを返します。
	 * @since 1.8.1
	 */
	public CategoriesJSON category(AcmsParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiUrlCategory, param);
		CategoriesJSON json = new CategoriesJSON(response.httpResponseBody);
		lastPresentTime = json.presentTime;
		return json;
	}
	/**
	 * ACMSから、コンテンツバージョン情報を取得します。
	 * @param param {@link AcmsParameters} オブジェクトです。
	 * @return コンテンツバージョン情報を格納した {@link ContentDto} のリストを返します。
     *          情報の変更がない(HttpStatus 304)場合はnullを返す。
	 * @since 1.0.0
	 */
	public ContentVersionsJSON contentVersion(AcmsParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiUrlContentVersion, param);
        if (response.httpResponseCode == 304) {
            // 1.7.6.5
            return null;
        }
		return new ContentVersionsJSON(response.httpResponseBody);
	}

	public Thread getContentInfoAsync(GetContentParameters param, String outputFilePath, Observer observer, Long contentId)
			throws MalformedURLException {
		Logger.d(TAG, "call api : getContentForAndroid [getContentInfoAsync]");
		
		String downloadUrl = AcmsApis.getDownloadUrl(env.downloadServerAddress, urlPath, param.getContentId(), param.getSid(), param.getContentType());
		Logger.d(TAG, "[url : %s], [param : %s]", downloadUrl, param.toHttpParameterString());
		
		HttpFileDownloader downloader = new HttpFileDownloader(downloadUrl, outputFilePath, 0, observer, contentId);
		return downloader.downloadAsync();
	}
	
	/**
	 * ログインしたユーザの所属する事業者のアプリ最新バージョン情報を取得します。
	 * @param param {@link ServerTimeParameters} オブジェクトです。
	 * @return　サーバの {@link Date} オブジェクトを返します。
	 * @since 1.0.0 
	 */
	public String appLatestVersion(AcmsParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiUrlAppLatestVersion, param);
		AppLatestVersionJSON json = new AppLatestVersionJSON(response.httpResponseBody);
		lastPresentTime = json.presentTime;
		return json.appLatestVersion;
	}
	
	public int contentCheckDeliverable(AcmsContentParameters param) throws AcmsException, NetworkDisconnectedException {
		HttpResponse response = send(AcmsApis.ApiUrlContentCheckDeliverable, param);
		ContentCheckDeliverableJSON json = new ContentCheckDeliverableJSON(response.httpResponseBody);
		lastPresentTime = json.presentTime;
		int result = 0;
		Logger.d("ContentCheckDeliverable", "result：%s, comparisonResult:%s", json.result, json.comparisonResult);
		if (!json.result) {
			result = 1;
		} else if (!json.comparisonResult) {
			result = 2;
		}
		return result;
	}
	
	/**
	 * @throws JSONValidationException 
	 * @since 1.1.0
	 */
	public RequirePasswordChangeCode requirePasswordChange(AcmsParameters param) throws AcmsException, NetworkDisconnectedException {
		HttpResponse response = send(AcmsApis.ApiUrlRequirePasswordChange, param);
		RequirePasswordChangeJSON json = new RequirePasswordChangeJSON(response.httpResponseBody);
		
		return json.requirePasswordChange;
	}
	
	public AuthLevelJSON getAuthLevel(AcmsParameters param) throws AcmsException, NetworkDisconnectedException {
		HttpResponse response = send(AcmsApis.ApiUrlGetAuthLevel, param);
		AuthLevelJSON json = new AuthLevelJSON(response.httpResponseBody);
		
		Logger.d(TAG, "json : %s", json);
		
		return json;
	}

	public AcmsMessageJSON uploadLogFile(String sid, File uploadFile, String appVersion, String deviceTypeId, String uid, String ma) throws AcmsException, NetworkDisconnectedException, IOException {
		String apiUrl = AcmsApis.getApiUrl(env.acmsAddress, urlPath, AcmsApis.ApiUrlUploadLogFile);
		HttpMultipart part1 = new HttpMultipart("sid", sid);
		HttpMultipart part2 = new HttpMultipart("appVersion", appVersion);
        HttpMultipart part3 = new HttpMultipart("formFile", uploadFile);
		HttpMultipart part4 = new HttpMultipart("deviceTypeId", deviceTypeId);
        HttpMultipart part5 = new HttpMultipart("uid", uid);
        HttpMultipart part6 = new HttpMultipart("ma", ma);

        Logger.d(TAG, "[uploadLogFile]: deviceTypeId=%s, uid=%s, ma=%s", deviceTypeId, uid, ma);

        HttpResponse result = HttpRequestSender.post(apiUrl, new HttpMultipart[] {part1, part2, part3, part4, part5, part6});
		AcmsMessageJSON json = new AcmsMessageJSON(result.httpResponseBody);
		
		Logger.d(TAG, "uploadLogFile res: %s", json);
		
		return json;
	}

	// TODO: リファクタリングの結果をABook+ブランチにも反映
	public String enqueteReply(EnqueteReplyParameters postParam, GetEnqueteReplyParameters getParam) throws AcmsException, NetworkDisconnectedException {
		
		HashMap<String, String> map = new HashMap<String, String>(); // 文字列をパースする
		String enqueteParam = postParam.getEnqueteParam();
		enqueteParam = enqueteParam.replace("abook-api://?", "").replace("enqueteParam:", "");
		String[] enqueteParams = enqueteParam.split("&entry_field_"); 
		
		String qParam = enqueteParams[enqueteParams.length -1];
		String[] qParams = qParam.split("&q_"); 
		
		for (int i = 0; i < enqueteParams.length - 1; i++) {
			String item = enqueteParams[i];
			String[] result = item.split("=");
			try {
				map.put((i==0)?result[0]:"entry_field_"+result[0], result.length>1?URLEncoder.encode(result[1], "UTF-8").replace("%2B", "%20"):StringUtil.Empty);
			} catch (UnsupportedEncodingException e) {
				throw new RuntimeException(e);
			}
		}
		
		for(int i = 0; i < qParams.length; i++){
			String key = "";
			String item = qParams[i];
			String[] result = item.split("=");
			if(enqueteParams.length > 1) {
				key = "entry_field_";
			}
			try{
				map.put((i==0)?key+result[0]:"q_"+result[0], result.length>1?URLEncoder.encode(result[1], "UTF-8").replace("%2B", "%20"):StringUtil.Empty);
			} catch(UnsupportedEncodingException e) {
				throw new RuntimeException(e);
			}
		}
		String apiUrl = AcmsApis.getApiUrl(env.acmsAddress, urlPath, AcmsApis.ApiUrlEnqueteReply);
		apiUrl = String.format("%s?%s", apiUrl, getParam.toHttpParameterString());
		HttpResponse response = send(apiUrl, AcmsApis.ApiUrlEnqueteReply, new PostEnqueteReplyParameters(map));

		ResultJSON json = new ResultJSON(response.httpResponseBody);
		
		Logger.d(TAG, "앙케이트 전송 결과 : %s", json);

		return json.result;
	}

	public int checkSendLogFile(CheckSendLogParameters param) throws NetworkDisconnectedException, AcmsException {
        HttpResponse response = send(AcmsApis.CheckSendLogFileFormat, param);
        LogSendFlagJSON json = new LogSendFlagJSON(response.httpResponseBody);
        Logger.d(TAG, "[checkSendLogFile]: returnValue=" + json.logSendFlg);
        return json.logSendFlg;
    }

    //プロジェクト取得
	public OperationListJSON getOperationList(AcmsParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiOperationList, param);
		OperationListJSON json = new OperationListJSON(response.httpResponseBody);

		return json;
	}

	//作業者グループ取得
	public WorkerGroupJSON getWorkingGroupList(AcmsParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiWorkingGroupList, param);
		WorkerGroupJSON json = new WorkerGroupJSON(response.httpResponseBody);

		return json;
	}

	/**
	 * 作業報告データ送信
	 * @param sid
	 * @param operationId
	 * @param taskReportDto
	 * @param formFile
	 * @param taskReportSendSaveDate
	 * @param reportType
	 * @return
	 * @throws ABVException
	 * @throws NetworkDisconnectedException
	 * @throws IOException
	 */
	public AcmsMessageJSON sendTaskData(String sid, Long operationId, TaskReportDto taskReportDto, File formFile, Date taskReportSendSaveDate, int reportType, boolean hotspotChangeFlg) throws ABVException, NetworkDisconnectedException, IOException {
		if (networkAdapter != null && !networkAdapter.isNetworkConnected()) { // NWのチェック
			throw new NetworkDisconnectedException();
		}
		HttpResponse result;
		String apiUrl = AcmsApis.getApiUrl(env.acmsAddress, urlPath, AcmsApis.ApiSendTaskData);
		List<HttpMultipart> HttpMultipartList = new ArrayList<HttpMultipart>();
		HttpMultipartList.add(new HttpMultipart(ABookKeys.SID, sid));
		HttpMultipartList.add(new HttpMultipart(ABookKeys.OPERATION_ID, StringUtil.toString(operationId)));
		HttpMultipartList.add(new HttpMultipart(ABookKeys.TASK_KEY, taskReportDto.taskKey));
		HttpMultipartList.add(new HttpMultipart(ABookKeys.DEL_FLAG, taskReportDto.delFlg ? "1" : "0"));
		HttpMultipartList.add(new HttpMultipart(ABookKeys.TASK_REPORT_LEVEL, StringUtil.toString(taskReportDto.taskReportLevel)));
		HttpMultipartList.add(new HttpMultipart(ABookKeys.TASK_HOT_SPOT_INFO, taskReportDto.taskHotSpotInfo));
		HttpMultipartList.add(new HttpMultipart(ABookKeys.TASK_REPORT_INFO, taskReportDto.jsonData));
		HttpMultipartList.add(new HttpMultipart(ABookKeys.ATTACHED_CHANGE_FLAG, taskReportDto.attachedFileSendFlg ? "1" : "0"));
		HttpMultipartList.add(new HttpMultipart(ABookKeys.ROUTINE_TASK_FLAG, reportType == Constant.ReportType.RoutineTask ? "1" : "0"));
		HttpMultipartList.add(new HttpMultipart(ABookKeys.UPDATE_HOTSPOT_ONLY_FLAG, hotspotChangeFlg ? "1" : "0"));

		// 定期点検用
		if (reportType == Constant.ReportType.RoutineTask) {
			HttpMultipartList.add(new HttpMultipart(ABookKeys.TASK_REPORT_ID, StringUtil.toString(taskReportDto.taskReportId)));
			HttpMultipartList.add(new HttpMultipart(ABookKeys.TASK_REPORT_INFO_ID, StringUtil.toString(taskReportDto.taskReportInfoId)));
			HttpMultipartList.add(new HttpMultipart(ABookKeys.REPORT_START_DATE, DateTimeUtil.toStringForCmsGMT(taskReportDto.reportStartDate)));
		}

		if (formFile != null) {
			HttpMultipartList.add(new HttpMultipart(ABookKeys.FORM_FILE, formFile));
		}

		if (taskReportSendSaveDate != null) {
			HttpMultipartList.add(new HttpMultipart(ABookKeys.TASK_REPORT_SAVE_DATE, DateTimeUtil.toStringForCmsGMT(taskReportSendSaveDate)));
		}
		result = HttpRequestSender.post(apiUrl, HttpMultipartList.toArray(new HttpMultipart[HttpMultipartList.size()]));

		AcmsMessageJSON json = new AcmsMessageJSON(result.httpResponseBody);

		// JSのコールバックに渡すために必要な情報をストックしておく（暫定的な実装 fixme ）
		if (apiUrl.contains(AcmsApis.ApiSendTaskData)) {
			JSONObject res = new JSONObject(result.httpResponseBody);
			int reportStatus = res.has(ReportStatus) ? res.getInt(ReportStatus) : 0;
			String message = res.has(Message) ? res.getString(Message) : null;
			ReportStatusLogic.TaskReportExtParam.stock(String.valueOf(reportStatus), message);
		}

		Logger.d(TAG, "sendTaskData res: %s", json);

		if (json.errorMessage != null) {
			if (json.errorMessage[0].equals("P003")) {
				throw new AcmsException(ABVExceptionCode.P_E_ACMS_P003, json);
			} else if (json.errorMessage[0].equals("P004")) {
				throw new AcmsException(ABVExceptionCode.P_E_ACMS_P004, json);
			} else if (json.errorMessage[0].equals("P005")) {
				throw new AcmsException(ABVExceptionCode.P_E_ACMS_P005, json);
			} else if (json.errorMessage[0].equals("P006")) {
				throw new AcmsException(ABVExceptionCode.P_E_ACMS_P006, json);
			}
		}

		if (json.httpStatus != 200) {
			Logger.e("error code : " + json.httpStatus);
			throw new AcmsException(ABVExceptionCode.fromResponseCode(result.httpResponseCode), json);
		}
		return json;
	}

	/**
	 * プロジェクトの作業データ取得
	 * @param param
	 * @return
	 * @throws NetworkDisconnectedException
	 * @throws AcmsException
	 */
	public OperationDataJSON getOpereationData(GetOperationDataParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiGetOperationData, param);
		OperationDataJSON json = new OperationDataJSON(response.httpResponseBody);
		return json;
	}

	/**
	 * 作業データファイルの取得
	 * @param param
	 * @param operationId
	 * @param outputFilePath
	 * @throws InterruptedException
	 * @throws ABVException
	 */
	public void getTaskFile(GetTaskFileParameters param, Long operationId, String outputFilePath) throws InterruptedException, ABVException {
		String downloadUrl = AcmsApis.getApiUrl(env.acmsAddress, urlPath, AcmsApis.ApiGetTaskFile);

		HttpFileDownloader downloader = new HttpFileDownloader(downloadUrl, param, outputFilePath);
		downloader.setRequireFileSizeFirst(false);
		downloader.downloadSync();
		if (downloader.getState() != HttpDownloadState.finished) {
			throw new ABVException(ABVExceptionCode.C_E_CONTENT_1001, downloader.getError());
		}
	}

	/**
	 * パノラマで使用するシーンを登録
	 * @param sid
	 * @param FormFile シーンで使用する画像
	 * @return
	 * @throws IOException
	 * @throws AcmsException
	 */
	public SceneEntryJSON sceneEntry(String sid, File FormFile) throws IOException, AcmsException, NetworkDisconnectedException {
		if (networkAdapter != null && !networkAdapter.isNetworkConnected()) { // NWのチェック
			throw new NetworkDisconnectedException();
		}
        String apiUrl = AcmsApis.getApiUrl(env.acmsAddress, urlPath, AcmsApis.ApiSceneEntry);
        HttpMultipart part1 = new HttpMultipart(ABookKeys.SID, sid);
        HttpMultipart part2 = new HttpMultipart(ABookKeys.FORM_FILE, FormFile);
        HttpResponse result = HttpRequestSender.post(apiUrl, new HttpMultipart[]{part1, part2});
		SceneEntryJSON json = new SceneEntryJSON(result.httpResponseBody);
		if (json.httpStatus != 200) {
			throw new AcmsException(ABVExceptionCode.fromResponseCode(result.httpResponseCode), null);
		}

        Logger.d(TAG, "sceneEntry res: %s", json.toString());

        return json;
    }

	/**
	 * パノラマコンテンツを登録
	 * @param sid
	 * @param operationId
	 * @param contentName
	 * @param formFile
	 * @return
	 * @throws IOException
	 * @throws AcmsException
	 */
    public AcmsMessageJSON taskContentEntry(String sid, long operationId, String contentName, File formFile) throws IOException, ABVException {
		HttpResponse result;
		String apiUrl = AcmsApis.getApiUrl(env.acmsAddress, urlPath, AcmsApis.ApiTaskContentEntry);
		HttpMultipart part1 = new HttpMultipart(ABookKeys.SID, sid);
		HttpMultipart part2 = new HttpMultipart(ABookKeys.OPERATION_ID, String.valueOf(operationId));
		HttpMultipart part3 = new HttpMultipart(ABookKeys.CONTENT_NAME, contentName);
		HttpMultipart part4 = new HttpMultipart(ABookKeys.FORM_FILE, formFile);
		result = HttpRequestSender.post(apiUrl, new HttpMultipart[]{part1, part2, part3, part4});

		Logger.d(TAG, "[taskContentEntry]: sid=%s, operatoinId=%s, contentName=%s", sid, operationId, contentName);
		AcmsMessageJSON json = new AcmsMessageJSON(result.httpResponseBody);

		Logger.d(TAG, "taskContentEntry res: %s", json);
		if (json.errorMessage != null) {
			if (json.errorMessage[0].equals("P001")) {
				throw new ABVException(ABVExceptionCode.P_E_ACMS_P001);
			} else if (json.errorMessage[0].equals("P002")) {
				throw new ABVException(ABVExceptionCode.P_E_ACMS_P002);
			}
		}

		if (json.httpStatus != 200) {
			throw new AcmsException(ABVExceptionCode.S_E_ACMS_0001, json);
		}
		return json;
	}
	/**
	 * プッシュメッセージ送信依頼
	 * @param param 送信パラメータ情報
	 * @return 通信成功、失敗
	 * @throws NetworkDisconnectedException
	 * @throws AcmsException
	 */
	public AcmsMessageJSON sendPushMessageRequest(SendPushMessageParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiSendPushMessage, param);
		AcmsMessageJSON json = new AcmsMessageJSON(response.httpResponseBody);
		return json;
	}

	/**
	 * プッシュメッセージ定型文取得
	 * @param sid 認証キー
	 * @return FixPushMessageJSON 定型文リスト
	 * @throws NetworkDisconnectedException
	 * @throws AcmsException
	 */
	public FixPushMessageJSON getFixPushMessage(String sid) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiGetPushMessages, new AcmsParameters(sid));
		FixPushMessageJSON json = new FixPushMessageJSON(response.httpResponseBody);
		validateHttpResponse(response, AcmsApis.ApiGetPushMessages);
		return json;
	}

	/**
	 * 作業種別の取得API
	 * @param param
	 * @return
	 * @throws NetworkDisconnectedException
	 * @throws AcmsException
	 */
	public OperationGroupMasterJSON getOperationGroupMaster(AcmsParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiOperationGroupMaster, param);
		OperationGroupMasterJSON json = new OperationGroupMasterJSON(response.httpResponseBody);
		return json;
	}

	public LockReportJSON sendLockReport(LockReportParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiLockReport, param, true);
		String json = response.httpResponseBody;
		return new LockReportJSON(json);
	}

	public UnlockReportJSON sendUnlockReport(UnlockReportParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiUnlockReport, param);
		String json = response.httpResponseBody;
		return new UnlockReportJSON(json);
	}

	public DashboardStatusJSON getDashboardStatus(AcmsParameters sid) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiGetDashboardStatus, sid);
		String json = response.httpResponseBody;
		return new DashboardStatusJSON(json);
	}
	/**
	 * プッシュメッセージ既読情報送信
	 * @param param 送信パラメータ情報
	 * @return 通信結果JSON
	 * @throws NetworkDisconnectedException
	 * @throws AcmsException
	 */
	public AcmsMessageJSON sendReadPushMessageRequest(ReadPushMessageParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiReadPushMessage, param);
		return new AcmsMessageJSON(response.httpResponseBody);
	}

	/**********************************************************************************************/
	/** 以下、共用メソッド---------------------------------------------------------------------- **/
	/**********************************************************************************************/

	private void validateHttpResponse(HttpResponse response, String methodName) throws AcmsException {
		AcmsMessageJSON json = new AcmsMessageJSON(response.httpResponseBody);
		if (response.httpResponseCode == 404) {
			throw new AcmsException(ABVExceptionCode.S_E_ACMS_0404, json);
		}
		
		if (response.httpResponseCode == 503) {
			throw new AcmsException(ABVExceptionCode.S_E_ACMS_0500, json);
		}
		
		if (response.httpResponseCode < 200 || response.httpResponseCode >= 300) {
			
			if (response.httpResponseCode == 400) {
				throw new AcmsException(ABVExceptionCode.S_E_ACMS_0400, json);
			} else if (response.httpResponseCode == 403) {
				if (!methodName.equals(AcmsApis.ApiUrlNewAppStoreLogin) && !methodName.equals(AcmsApis.ApiUrlAppStoreNewLogin) && !methodName.equals(AcmsApis.ApiUrlEnterpriseLogin)) {
					//	ログイン済みなのに、認証失敗。
					//	サーバからユーザを削除したか、端末との紐付きを削除した場合。
					//	ログイン画面に戻して再度ログインするようにする。
					throw new AcmsException(ABVExceptionCode.S_E_ACMS_1403, json);
				} else {
					throw new AcmsException(ABVExceptionCode.S_E_ACMS_0403, json);
				}
			} else if (response.httpResponseCode == 500) {
				throw new AcmsException(ABVExceptionCode.S_E_ACMS_0500, json);
			} else {
				throw new AcmsException(ABVExceptionCode.S_E_ACMS_0001, json);
			}
		}
	}
	
	/**
	 * APIを呼び出します。この際、HTTP GET通信が行われます。
	 * @return APIの戻り値を返します。
	 * @throws JSONValidationException 
	 * @since 1.0.0
	 */
	private HttpResponse send(String methodName, HttpParameterObject param) throws NetworkDisconnectedException, AcmsException {
		return send(methodName, param, false);
	}

	private HttpResponse send(String methodName, HttpParameterObject param, boolean ignore404) throws NetworkDisconnectedException, AcmsException {
		Logger.d(TAG, "call api : %s", methodName);
		
		String apiUrl = AcmsApis.getApiUrl(env.acmsAddress, urlPath, methodName);
		
		HttpResponse response = send(apiUrl, methodName, param, ignore404);
		
		// 最終アクセス時間更新（並列リクエスト時の DB 競合を避けるため直列化）
		synchronized (ACMS_MEMBER_LAST_ACCESS_LOCK) {
			MemberInfoDao dao = AbstractDao.getDao(MemberInfoDao.class);
			MemberInfoDto dto = dao.getMemberInfo(LoginStatus.LoggedIn.statusCode());

			if (dto != null) {
				dto.lastCMSAccessDate = DateTimeUtil.getCurrentTimestamp();
				dao.updateMemberInfo(dto);
			}
		}

		return response;
	}

	public HttpResponse send(String apiUrl, String methodName, HttpParameterObject param) throws NetworkDisconnectedException, AcmsException {
		return send(apiUrl, methodName, param, false);
	}

	public HttpResponse send(String apiUrl, String methodName, HttpParameterObject param, boolean ignore404) throws NetworkDisconnectedException, AcmsException {
		if (!networkAdapter.isNetworkConnected()) {
			Logger.w(TAG, "network is disconnected.");
			throw new NetworkDisconnectedException();
		}

		Logger.d(TAG, "[url : %s], [param : %s]", apiUrl, param==null?"":param.toHttpParameterString());

		HttpTaskWorker<HttpParameterObject> httpTaskThread = new HttpTaskWorker<HttpParameterObject>(methodName, apiUrl, param);
		try {
			httpTaskThread.start();
			if (methodName.equals(AcmsApis.ApiGetMasterData)) { //マスタデータ取得失敗時、タイムアウト設定する
				Integer mult = masterDataJoinMultiplier.get();
				int m = mult != null ? mult.intValue() : 0;
				long connectionTimeout = HttpRequestSender.DefaultConnectionTimeout * (m + 1);
				Logger.i(TAG, "ApiGetMasterData joinMultiplier = " + m + ", time out = " + connectionTimeout);
				httpTaskThread.join(connectionTimeout);
			} else {
				httpTaskThread.join(HttpRequestSender.DefaultConnectionTimeout);
			}
		} catch (Exception e) {
			Logger.e(TAG, e);
			Logger.d(TAG, ExceptionDetailMessage.SERVER_IS_DISCONNECTED);
			//	サーバとの通信中、予期せぬ問題が発生しました。（1）
			throw new NetworkDisconnectedException(ExceptionDetailMessage.SERVER_IS_DISCONNECTED, e);
		}
		
		if (httpTaskThread.exception != null) {
			Logger.w(TAG, "call api failed.", httpTaskThread.exception);
			//throw httpTaskThread.exception;
			//	サーバとの通信中、予期せぬ問題が発生しました。（2）
			throw new NetworkDisconnectedException(ExceptionDetailMessage.SERVER_IS_DISCONNECTED, httpTaskThread.exception);
		}
		
		if (httpTaskThread.response == null) {
			Logger.d(TAG, "response from %s : %s", methodName, "null");
			//	サーバから応答がありません。しばらく時間をおいて再度操作してください。
			throw new NetworkDisconnectedException(ExceptionDetailMessage.SERVER_IS_DISCONNECTED);
		} else {
			Logger.v(TAG, "response from %s : %s", methodName, httpTaskThread.response);
		}
		
		if (httpTaskThread.response.httpResponseCode == 401 && httpTaskThread.response.httpResponseBody == null) { // 401の場合bodyが取れない場合があるのでvalidationはしない
			throw new AcmsException(ABVExceptionCode.S_E_ACMS_0401, null);
		}
		boolean isNot304 = httpTaskThread.response.httpResponseCode != 304;	// 変更なし
		boolean is404 = httpTaskThread.response.httpResponseCode == 404 && !ignore404;
        if (isNot304 && is404) {
            validateHttpResponse(httpTaskThread.response, methodName);
        }

		return httpTaskThread.response;
	}
	
	private HttpResponse sendMultipart(String methodName, String url, HttpMultipart[] param) throws NetworkDisconnectedException, AcmsException {
		Logger.d(TAG, "call api : %s", methodName);
		
		if (!networkAdapter.isNetworkConnected()) {
			Logger.w(TAG, "network is disconnected.");
			throw new NetworkDisconnectedException();
		}
				
		String apiUrl = url;
		HttpMultipartTaskWorker<HttpMultipart> httpTaskThread = new HttpMultipartTaskWorker<HttpMultipart>(apiUrl, param);
		try {
			httpTaskThread.start();
			httpTaskThread.join(HttpRequestSender.DefaultConnectionTimeout);
		} catch (Exception e) {
			Logger.d(TAG, ExceptionDetailMessage.SERVER_IS_DISCONNECTED);
			//	サーバとの通信中、予期せぬ問題が発生しました。（1）
			throw new NetworkDisconnectedException(ExceptionDetailMessage.SERVER_IS_DISCONNECTED,e);
		}
		
		if (httpTaskThread.exception != null) {
			Logger.w(TAG, "call api failed.", httpTaskThread.exception);
			//throw httpTaskThread.exception;
			//	サーバとの通信中、予期せぬ問題が発生しました。（2）
			throw new NetworkDisconnectedException(ExceptionDetailMessage.SERVER_IS_DISCONNECTED, httpTaskThread.exception);
		}
		
		if (httpTaskThread.response == null) {
			Logger.d(TAG, "response from %s : %s", methodName, "null");
			//	サーバから応答がありません。しばらく時間をおいて再度操作してください。
			throw new NetworkDisconnectedException(ExceptionDetailMessage.SERVER_IS_DISCONNECTED);
		} else {
			Logger.d(TAG, "response from %s : %s", methodName, httpTaskThread.response);
		}
		
		validateHttpResponse(httpTaskThread.response, methodName);
		
		return httpTaskThread.response;
	}
	
	/**
	 * 完成してないメソッドです。
	 */
	@SuppressWarnings("unused")
	private void sendAsync(String methodName, HttpParameterObject param) throws NetworkDisconnectedException {
		if (!networkAdapter.isNetworkConnected()) {
			throw new NetworkDisconnectedException();
		}
		
		String apiUrl = AcmsApis.getApiUrl(env.acmsAddress, urlPath, methodName);
		//	TODO: later json処理すること
		
		HttpTaskWorker<HttpParameterObject> httpTaskThread = new HttpTaskWorker<HttpParameterObject>(methodName, apiUrl, param);
		httpTaskThread.setOnResponseListener(this);
		httpTaskThread.start();
	}
	
	@Override
	public void onResponse(String methodName, boolean result, HttpResponse response, Exception error) {
		if (result) {
			if (response.httpResponseCode >= 200 && response.httpResponseCode < 300) {
                //	正常
            } else if (response.httpResponseCode == 304) {
                // 変更なし
                Logger.d(TAG, "[httpResponseCode]:304");
			} else {
				//	サーバからエラーを返した場合
			}
			
		} else {
			//	通信中エラー（timeout等）
		}
	}
	
	public Date getLastPresentTime() {
		return lastPresentTime;
	}
	
	private class HttpTaskWorker <T extends HttpParameterObject> extends Thread {
		private String methodName = null;
		private boolean result = false;
		private Exception exception = null;
		private HttpResponse response = null;
		
		private String apiUrl = null;
		private T param = null;
		
		private AcmsClientResponseListener listener = null;

		private String[] postMethod = new String[]{ // postで送るメソッド
				AcmsApis.ApiUrlEnqueteReply,
				AcmsApis.ApiUrlContentReadingLog,
				AcmsApis.ApiUrlAddMemberGroup,
                AcmsApis.ApiUrlContentVersion,
                AcmsApis.ApiUrlNewAppStoreLogin,
                AcmsApis.ApiUrlAppStoreNewLogin,
				AcmsApis.ApiGetPushMessages,
				AcmsApis.ApiSendPushMessage,
				AcmsApis.ApiLockReport,
				AcmsApis.ApiUnlockReport,
				AcmsApis.ApiGetDashboardStatus
				};

		public HttpTaskWorker(String methodName, String apiUrl, T param) {
			this.methodName = methodName;
			this.apiUrl = apiUrl;
			this.param = param;
		}
		
		public void setOnResponseListener(AcmsClientResponseListener listener) {
			this.listener = listener;
		}
		
		@Override
		public void run() {
			try {
				if (StringUtil.contains(methodName, postMethod)) {
					//	日本語の文字化けなどがあるため、POSTで送信する。
					response = HttpRequestSender.post(apiUrl, param);
				}
				else {
					response = HttpRequestSender.get(apiUrl, param);
				}
				
				result = true;
				exception = null;
			} catch (Exception e) {
				exception = e;
			} finally {
				if (listener != null) {
					listener.onResponse(methodName, result, response, exception);
				}
			}
		}
	}
	
	private class HttpMultipartTaskWorker <T extends HttpMultipart> extends Thread {
		private Exception exception = null;
		private HttpResponse response = null;
		
		private String apiUrl = null;
		private T[] param = null;
		
		public HttpMultipartTaskWorker(String apiUrl, T[] param) {
			this.apiUrl = apiUrl;
			this.param = param;
		}
		
		@Override
		public void run() {
			try {
				response = HttpRequestSender.post(apiUrl, param);
				
				exception = null;
			} catch (Exception e) {
				exception = e;
			}
		}
	}

	/**
	 * ACMSから、マスタデータ情報を取得します。
	 * @param param {@link AcmsParameters} オブジェクトです。
	 * @param retryCount 失敗カウント
	 * @return コンテンツバージョン情報を格納した {@link ContentDto} のリストを返します。
	 *          情報の変更がない(HttpStatus 304)場合はnullを返す。
	 * @since 1.0.0
	 */
	public MasterDataJSON masterData(AcmsParameters param, int retryCount) throws NetworkDisconnectedException, AcmsException {
		masterDataJoinMultiplier.set(Integer.valueOf(retryCount));
		try {
			HttpResponse response = send(AcmsApis.ApiGetMasterData, param);
			return new MasterDataJSON(response.httpResponseBody);
		} finally {
			masterDataJoinMultiplier.remove();
		}
	}

	/**
	 * プッシュメッセージ情報の取得
	 * @param param 送信パラメータ情報
	 * @return 通信結果JSON
	 * @throws NetworkDisconnectedException
	 * @throws AcmsException
	 */
	public PushMessageListJSON getPushMessageList(AcmsParameters param) throws NetworkDisconnectedException, AcmsException {
		HttpResponse response = send(AcmsApis.ApiGetPushMessagesList, param);
		return new PushMessageListJSON(response.httpResponseBody);
	}
}
