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

import net.lingala.zip4j.exception.ZipException;

import org.json.adf.JSONArray;
import org.json.adf.JSONException;
import org.json.adf.JSONObject;

import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import jp.agentec.abook.abv.bl.acms.client.AcmsClient;
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.WorkerGroupJSON;
import jp.agentec.abook.abv.bl.acms.client.parameters.AcmsParameters;
import jp.agentec.abook.abv.bl.acms.client.parameters.GetTaskFileParameters;
import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.Constant;
import jp.agentec.abook.abv.bl.common.Callback;
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.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.ContentDao;
import jp.agentec.abook.abv.bl.data.dao.OperationContentDao;
import jp.agentec.abook.abv.bl.data.dao.OperationDao;
import jp.agentec.abook.abv.bl.data.dao.OperationGroupMasterOperationDao;
import jp.agentec.abook.abv.bl.data.dao.PushMessageDao;
import jp.agentec.abook.abv.bl.data.dao.TaskDao;
import jp.agentec.abook.abv.bl.data.dao.TaskReportDao;
import jp.agentec.abook.abv.bl.data.dao.TaskReportItemsDao;
import jp.agentec.abook.abv.bl.data.dao.TaskReportSendDao;
import jp.agentec.abook.abv.bl.data.dao.TaskWorkerGroupDao;
import jp.agentec.abook.abv.bl.data.dao.WorkerGroupDao;
import jp.agentec.abook.abv.bl.data.dao.WorkingReportDao;
import jp.agentec.abook.abv.bl.dto.CategoryContentDto;
import jp.agentec.abook.abv.bl.dto.ContentDto;
import jp.agentec.abook.abv.bl.dto.OperationContentDto;
import jp.agentec.abook.abv.bl.dto.OperationDto;
import jp.agentec.abook.abv.bl.dto.OperationGroupMasterRelationDto;
import jp.agentec.abook.abv.bl.dto.PushMessageDto;
import jp.agentec.abook.abv.bl.dto.TaskDto;
import jp.agentec.abook.abv.bl.dto.TaskReportDto;
import jp.agentec.abook.abv.bl.dto.TaskReportItemsDto;
import jp.agentec.abook.abv.bl.dto.TaskReportSendDto;
import jp.agentec.abook.abv.bl.dto.TaskWorkerGroupDto;
import jp.agentec.abook.abv.bl.dto.WorkerGroupDto;
import jp.agentec.adf.util.DateTimeFormat;
import jp.agentec.adf.util.DateTimeUtil;
import jp.agentec.adf.util.FileUtil;
import jp.agentec.adf.util.StringUtil;

import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.Task;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationListJSON.OperationEndDate;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationListJSON.OperationStartDate;

/**
 * Created by leej on 2018/08/17.
 */

public class OperationLogic extends AbstractLogic {
    private static final String TAG = "OperationLogic";
    private OperationDao mOperationDao = AbstractDao.getDao(OperationDao.class);
    private OperationContentDao mOperationContentDao = AbstractDao.getDao(OperationContentDao.class);
    private ContentDao mContentDao = AbstractDao.getDao(ContentDao.class);

    private WorkerGroupDao mWorkerGroupDao = AbstractDao.getDao(WorkerGroupDao.class);
    private TaskDao mTaskDao = AbstractDao.getDao(TaskDao.class);
    private TaskReportDao mTaskReportDao = AbstractDao.getDao(TaskReportDao.class);
    private TaskReportSendDao mTaskReportSendDao = AbstractDao.getDao(TaskReportSendDao.class);
    private TaskReportItemsDao mTaskReportItemsDao = AbstractDao.getDao(TaskReportItemsDao.class);
    private OperationGroupMasterOperationDao mOperationGroupMasterOperationDao = AbstractDao.getDao(OperationGroupMasterOperationDao.class);

    private ContentLogic mContentLogic = AbstractLogic.getLogic(ContentLogic.class);

    private TaskWorkerGroupDao mTaskWorkerGroupDao = AbstractDao.getDao(TaskWorkerGroupDao.class);
    private static final int FINISHED_STATUS = 999;
    private final static int INTERVAL_LAST_EDIT_TIME_MS = 1800000;
    public void initializeOperations() throws AcmsException, NetworkDisconnectedException {
        // 作業グループリスト取得
        setWorkingGroupList();
        // プロジェクト一覧取得し、登録・更新・削除する
        retrieveServerOperation();

    }

    /**
     * プロジェクト一覧取得し、登録・更新・削除する
     *
     * @throws AcmsException
     * @throws NetworkDisconnectedException
     */
    private void retrieveServerOperation() throws AcmsException, NetworkDisconnectedException {
        List<OperationDto> localOperations = mOperationDao.getAllOperation();
        Map<Long, OperationDto> localByOperationId = new HashMap<Long, OperationDto>();
        for (OperationDto local : localOperations) {
            localByOperationId.put(local.operationId, local);
        }
        AcmsParameters param = new AcmsParameters(cache.getMemberInfo().sid);
        OperationListJSON json = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).getOperationList(param);
        List<OperationDto> serverOperations = json.operationList;

        Set<Long> serverOperationIds = new HashSet<Long>();
        for (OperationDto serverOperationDto : serverOperations) {
            serverOperationIds.add(serverOperationDto.operationId);
            // 登録フラグ
            boolean insertFlg = true;
            OperationDto localProjcetDto = localByOperationId.get(serverOperationDto.operationId);
            if (localProjcetDto != null) {
                    //update
                    serverOperationDto.needSyncFlg = serverOperationDto.equalsLastEdit(localProjcetDto) ? localProjcetDto.needSyncFlg : true;
                    serverOperationDto.operationLastSyncDate = localProjcetDto.operationLastSyncDate;
                    List<Long> localOperationContentIds = mOperationContentDao.getContentIds(serverOperationDto.operationId);
                    for (OperationContentDto operationContentDto : serverOperationDto.operationContentDtoList) {
                        if (localOperationContentIds == null || localOperationContentIds.size() == 0) {
                            // insert
                            mOperationContentDao.insertOperationContent(operationContentDto);
                        } else {
                            int localOperationContentIndex = localOperationContentIds.indexOf(operationContentDto.contentId);
                            if (localOperationContentIndex >= 0) {
                                // 存在する
                                localOperationContentIds.remove(localOperationContentIndex);
                            } else {
                                // insert
                                mOperationContentDao.insertOperationContent(operationContentDto);
                            }
                        }
                    }
                    if (localOperationContentIds != null) {
                        // 関連資料の削除処理
                        for (Long deleteContentId : localOperationContentIds) {
                            mOperationContentDao.deleteOperationContent(localProjcetDto.operationId, deleteContentId);
                        }
                    }

                    if (mOperationContentDao.isExistMainOperationContent(serverOperationDto.operationId)) {
                        serverOperationDto.contentCreatingFlg = false;
                    }

                    // ローカルにある作業情報に紐づく全作業IDを取得
                    List<Integer> localOperationGroupMasterIds = mOperationGroupMasterOperationDao.getOperationGroupMasterIds(serverOperationDto.operationId);
                    for (OperationGroupMasterRelationDto operationGroupMasterRelationDto : serverOperationDto.operationGroupMasterRelationDtoList) {
                        if (localOperationGroupMasterIds == null || localOperationGroupMasterIds.size() == 0) {
                            // 作業IDが存在しなければ、登録する
                            mOperationGroupMasterOperationDao.insertOperationGroupMasterOperation(operationGroupMasterRelationDto);
                        } else {
                            // ローカル（DB）に作業IDが存在するかチェックして更新・登録を判定
                            int localOperationContentIndex = localOperationGroupMasterIds.indexOf(operationGroupMasterRelationDto.operationGroupMasterId);
                            if (localOperationContentIndex >= 0) {
                                // 作業IDが存在するので更新
                                localOperationGroupMasterIds.remove(localOperationContentIndex);
                            } else {
                                // 作業IDが存在しないので登録
                                mOperationGroupMasterOperationDao.insertOperationGroupMasterOperation(operationGroupMasterRelationDto);
                            }
                        }
                    }
                    if (localOperationGroupMasterIds != null) {
                        // ローカルとサーバーの差分は削除と見做し、削除処理を行う。
                        for (Integer deleteOperationGroupMasterId : localOperationGroupMasterIds) {
                            mOperationGroupMasterOperationDao.deleteOperationGroupMasterOperation(deleteOperationGroupMasterId, serverOperationDto.operationId);
                        }
                    }

                    mOperationDao.update(serverOperationDto);
                    // 更新することで登録フラグをfalseにセット
                    insertFlg = false;
            }

            // 更新されたら以下の処理は行わない
            if (insertFlg) {
                boolean isNotExistContent = false;
                for (OperationContentDto operationContentDto : serverOperationDto.operationContentDtoList) {
                    if (operationContentDto.operationContentFlg) {
                        if(mContentDao.getContent(operationContentDto.contentId) == null) {
                            isNotExistContent = true;
                        }
                    }
                }
                // コンテンツテーブルに存在しない場合、プロジェクトを登録しない
                if (isNotExistContent) { continue; }

                serverOperationDto.needSyncFlg = true;
                serverOperationDto.contentCreatingFlg = false;
                mOperationDao.insert(serverOperationDto);
                // 関連資料の登録
                for (OperationContentDto operationContentDto : serverOperationDto.operationContentDtoList) {
                    mOperationContentDao.insertOperationContent(operationContentDto);
                }

                // 作業種別・作業のリレーションテーブルにデータ登録
                for (OperationGroupMasterRelationDto operationGroupMasterRelationDto : serverOperationDto.operationGroupMasterRelationDtoList) {
                    mOperationGroupMasterOperationDao.insertOperationGroupMasterOperation(operationGroupMasterRelationDto);
                }
            }

            // 作業担当グループ登録
            mTaskWorkerGroupDao.deleteByOperationId(serverOperationDto.operationId);
            for (TaskWorkerGroupDto taskWorkerGroupDto : serverOperationDto.taskWorkerGroupDtoList) {
                taskWorkerGroupDto.operationId = serverOperationDto.operationId;
                for (Integer groupId : taskWorkerGroupDto.groupList) {
                    taskWorkerGroupDto.groupId = groupId;
                    mTaskWorkerGroupDao.insert(taskWorkerGroupDto);
                }
            }
        }

        List<OperationDto> deleteListDtoArray = new ArrayList<OperationDto>();

        for (OperationDto localProjcetDto : localOperations) {
            if (!serverOperationIds.contains(localProjcetDto.operationId)) {
                deleteListDtoArray.add(localProjcetDto);
            }
        }
        // サーバーから取得したプロジェクト情報がローカルに存在しないので削除する
        for (OperationDto deleteOperationDto : deleteListDtoArray) {
            List<TaskDto> taskDtoList = mTaskDao.selectAllTaskByOperationId(deleteOperationDto.operationId);
            List<Long> contentIds = mOperationContentDao.getContentIds(deleteOperationDto.operationId);
            for(TaskDto taskDto : taskDtoList) {
                mTaskDao.delete(taskDto);
            }
            mOperationDao.delete(deleteOperationDto);

            // 作業削除時、紐づいた資料を削除
            for (Long contentId : contentIds) {
                if (mOperationContentDao.isDeleteContent(contentId)) {
                    ContentDto contentDto = mContentDao.getContent(contentId);
                    mContentLogic.deleteContent(contentDto, true);
                }
            }

            String operationTaskDirPath = ABVEnvironment.getInstance().getOperationDirFile(deleteOperationDto.operationId);
            FileUtil.delete(operationTaskDirPath);
        }
    }

    /**
     * サーバーから作業者グループ取得し、m_worker_groupテーブルへ登録
     *
     * @throws NetworkDisconnectedException
     * @throws AcmsException
     */
    private void setWorkingGroupList() throws NetworkDisconnectedException, AcmsException {
        AcmsParameters param = new AcmsParameters(cache.getMemberInfo().sid);
        WorkerGroupJSON json = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).getWorkingGroupList(param);
        List<Integer> localWorkerGroupIdList = mWorkerGroupDao.getAllWorkerGroupId();
        for (WorkerGroupDto dto : json.workerGroupList) {
            if (mWorkerGroupDao.isExist(dto.groupId)) {
                localWorkerGroupIdList.remove(dto.groupId);
                mWorkerGroupDao.update(dto);
            } else {
                mWorkerGroupDao.insert(dto);
            }
        }

        // 削除
        for (Integer groupId : localWorkerGroupIdList) {
            mWorkerGroupDao.deleteById(groupId);
        }
    }

    /**
     * 報告データ登録
     * @param taskKey
     * @param operationId
     * @param contentId
     * @param reportLevel
     * @param enableReport
     * @param taskReportJson
     * @param localAttachedFileName
     * @param attachedChangeFlag
     * @param dataSendFlg
     * @param localSavedFlg
     * @throws IOException
     */
    public void insertTaskReport(
            String taskKey,
            long operationId,
            long contentId,
            int reportLevel,
            int enableReport,
            JSONObject taskReportJson,
            String localAttachedFileName,
            boolean attachedChangeFlag,
            boolean dataSendFlg,
            boolean localSavedFlg,
            TaskReportDto.SendBackInfo sendBackInfo,
            TaskReportDto.LockInfo lockInfo,
            int taskStatus
    ) throws IOException {
        TaskDto taskDto = mTaskDao.getTaskByTaskKey(taskKey);
        if (taskDto == null) {
            //TODO error
            return;
        }

        TaskReportDto taskReportDto = new TaskReportDto();
        taskReportDto.taskKey = taskKey;

        if (taskReportJson != null) {
            // suggest登録・更新処理
            registTaskReportItem(taskKey, reportLevel, taskReportJson.has("suggest") ? taskReportJson.getJSONObject("suggest") : null);
            taskReportDto.jsonData = taskReportJson.toString();
        } else {
            // taskJsonがnullの場合、jsonDataに空でセット
            taskReportDto.jsonData = "";
        }
        // 一時保存のフラグ
        taskReportDto.localSavedFlg = localSavedFlg;
        // 送信フラグ
        taskReportDto.dataSendFlg = dataSendFlg;
        taskReportDto.attachedFileSendFlg = attachedChangeFlag;
        // 作業報告
        taskReportDto.taskReportLevel = reportLevel;
        // 報告可能区分
        taskReportDto.enableReport = enableReport;
        if (localAttachedFileName != null) {
            taskReportDto.localAttachedFileName = localAttachedFileName;
        }
        if (sendBackInfo != null) {
            taskReportDto.sendBackUserId = sendBackInfo.sendBackUserId;
            taskReportDto.sendBackUserName = sendBackInfo.sendBackUserName;
            taskReportDto.sendBackComment = sendBackInfo.sendBackComment;
        }
        if (lockInfo != null) {
            taskReportDto.reportLockUserId = lockInfo.reportLockUserId;
            taskReportDto.reportLockUserName = lockInfo.reportLockUserName;
            taskReportDto.reportLockTime = lockInfo.reportLockTime;
        }
        taskReportDto.taskStatus = taskStatus;

        mTaskReportDao.insert(taskReportDto);

        if (taskReportJson != null) {
            String tempDirPath = ABVEnvironment.getInstance().getTempTaskDirPath(contentId, taskKey);

            //添付ファイル変更の場合、以下の処理を行う
            JSONObject attachedListJson = taskReportJson.getJSONObject("attached");
            List<String> attachedFileNames = JSONObject.getValues(attachedListJson);
            // コピー元のファイルで、添付ファイルとして使用しないファイル削除
            deleteDifferentialFile(tempDirPath, attachedFileNames);

            String operationDrectionOrReportDirPath = ABVEnvironment.getInstance().getOperationDirectionOrReportDirPath(operationId, taskKey, reportLevel);

            // 添付ディレクトリの移動
            boolean result = FileUtil.copy(tempDirPath, operationDrectionOrReportDirPath, true);
            if (result) {
                FileUtil.delete(tempDirPath);
            }
        }
    }

    /**
     * 作業報告を更新
     * @param taskKey
     * @param operationId
     * @param contentId
     * @param taskReportLevel
     * @param enableReport
     * @param taskReportJson
     * @param localAttachedFileName
     * @param attachedChangeFlag
     * @param dataSendFlg
     * @param localSavedFlg
     * @throws IOException
     */
    public void updateTaskReport(
            String taskKey,
            long operationId,
            long contentId,
            int taskReportLevel,
            int enableReport,
            JSONObject taskReportJson,
            String localAttachedFileName,
            boolean attachedChangeFlag,
            boolean dataSendFlg,
            boolean localSavedFlg,
            TaskReportDto.SendBackInfo sendBackInfo,
            TaskReportDto.LockInfo lockInfo,
            int taskStatus
    ) throws IOException {
        TaskReportDto taskReportDto = mTaskReportDao.getTaskReport(taskKey, taskReportLevel);
        if (taskReportDto == null) {
            //TODO error?
            return;
        }
        if (taskReportJson != null) {
            // suggest登録・更新処理
            registTaskReportItem(taskKey, taskReportLevel, taskReportJson.has("suggest") ? taskReportJson.getJSONObject("suggest") : null);

            taskReportDto.jsonData = taskReportJson.toString();
        } else {
            taskReportDto.jsonData = "";
        }
        // 一時保存のフラグ
        taskReportDto.localSavedFlg = localSavedFlg;
        // 送信フラグ
        taskReportDto.dataSendFlg = dataSendFlg;
        // 作業報告階層
        taskReportDto.taskReportLevel = taskReportLevel;
        // 報告可能区分
        taskReportDto.enableReport = enableReport;

        // attachedFileSendFlgがtrueの場合は、更新しない
        if (!taskReportDto.attachedFileSendFlg) {
            taskReportDto.attachedFileSendFlg = attachedChangeFlag;
        }
        if (localAttachedFileName != null) {
            taskReportDto.localAttachedFileName = localAttachedFileName;
        }

        if (sendBackInfo != null) {
            taskReportDto.sendBackUserId = sendBackInfo.sendBackUserId;
            taskReportDto.sendBackUserName = sendBackInfo.sendBackUserName;
            taskReportDto.sendBackComment = sendBackInfo.sendBackComment;
        }
        if (lockInfo != null) {
            taskReportDto.reportLockUserId = lockInfo.reportLockUserId;
            taskReportDto.reportLockUserName = lockInfo.reportLockUserName;
            taskReportDto.reportLockTime = lockInfo.reportLockTime;
        }
        taskReportDto.taskStatus = taskStatus;

        mTaskReportDao.update(taskReportDto);

        String tempDirPath = ABVEnvironment.getInstance().getTempTaskDirPath(contentId, taskKey);

        String operationDrectionOrReportDirPath = ABVEnvironment.getInstance().getOperationDirectionOrReportDirPath(operationId, taskKey, taskReportLevel);
        if (taskReportDto.attachedFileSendFlg) {
            //添付ファイル変更の場合、以下の処理を行う
            JSONObject attachedListJson = taskReportJson.getJSONObject("attached");
            List<String> attachedFileNames = JSONObject.getValues(attachedListJson);

            // コピー元のファイルで、添付ファイルとして使用しないファイル削除
            deleteDifferentialFile(tempDirPath, attachedFileNames);
            // 同じファイル名が存在すれば、コピー先ファイルを削除
            deleteDifferentialFile(operationDrectionOrReportDirPath, attachedFileNames);

            // 添付ディレクトリの移動
            boolean result = FileUtil.copy(tempDirPath, operationDrectionOrReportDirPath, true);
            if(result) {
                FileUtil.delete(tempDirPath);
            }
        }
    }

    /**
     * 作業報告/作業報告（回答）の削除
     *
     * @param operationId
     * @param contentId
     * @param taskKey
     */
    public void deleteTaskReport(long operationId, long contentId, String taskKey, int taskReportLevel) {
        TaskDto taskDto = mTaskDao.getTaskByTaskKey(taskKey);
        if (taskDto == null) {
            return;
        }
        TaskReportDto taskReportDto = mTaskReportDao.getTaskReport(taskKey, taskReportLevel);
        if (taskReportDto != null) {
            taskReportDto.dataSendFlg = true;
            taskReportDto.attachedFileSendFlg = false;
            if (taskReportDto.taskReportLevel == Constant.TaskReportLevel.ReportType) {
                // 報告
                taskDto.delFlg = true;
            } else {
                // 報告（回答）
                taskReportDto.jsonData = "";
                taskDto.taskStatus = 0;
            }
            mTaskReportDao.update(taskReportDto);
            mTaskDao.update(taskDto);
            // プロジェクトの作業データディレクトリ削除
            deleteTaskFileData(operationId, contentId, taskKey, taskReportLevel);
        }
    }


    /**
     * 定期点検データ新規
     * @param operationId
     * @param taskReportDto
     * @param attachedChangeFlag
     * @param dataSendFlg
     * @throws IOException
     */
    public void insertRoutineTaskReport(long operationId, long contentId, TaskReportDto taskReportDto, boolean attachedChangeFlag, boolean dataSendFlg) throws IOException {
        taskReportDto.attachedFileSendFlg = attachedChangeFlag;
        taskReportDto.dataSendFlg = dataSendFlg;
        mTaskReportDao.insert(taskReportDto);

        if (!StringUtil.isNullOrEmpty(taskReportDto.jsonData)) {
            JSONObject taskReportJson = new JSONObject(taskReportDto.jsonData);
            JSONObject taskJson = taskReportJson.getJSONObject("task");
            Iterator taskKeys = taskJson.keys();
            while (taskKeys.hasNext()) {
                TaskReportItemsDto taskReportItemsDto = new TaskReportItemsDto();
                String itemKey = (String) taskKeys.next();
                taskReportItemsDto.taskKey = taskReportDto.taskKey;
                taskReportItemsDto.itemKey = itemKey;
                try {
                    taskReportItemsDto.inputValue = taskJson.getString(itemKey);
                } catch (JSONException e) {
                    // 値がStringではない場合、無視する
                    continue;
                }
                mTaskReportItemsDao.insertTaskReportItems(taskReportItemsDto);
            }
        }

        //添付ファイル変更の場合、以下の処理を行う
        String strReportStartDate = DateTimeUtil.toString_yyyyMMddHHmmss_none(taskReportDto.reportStartDate);

        String tempDirPath = ABVEnvironment.getInstance().getTempTaskDirPath(contentId, taskReportDto.taskKey);
        String routineTaskReportDirPath = ABVEnvironment.getInstance().getRoutineTaskReportDirFilePath(operationId, taskReportDto.taskKey, taskReportDto.taskReportId, strReportStartDate);

        if (!StringUtil.isNullOrEmpty(taskReportDto.jsonData)) {
            JSONObject attachedListJson = (new JSONObject(taskReportDto.jsonData)).getJSONObject("attached");
            List<String> attachedFileNames = JSONObject.getValues(attachedListJson);
            // コピー元のファイルで、添付ファイルとして使用しないファイル削除
            deleteDifferentialFile(tempDirPath, attachedFileNames);
        }

        // 添付ディレクトリの移動
        boolean result = FileUtil.copy(tempDirPath, routineTaskReportDirPath, true);
        if(result) {
            FileUtil.delete(tempDirPath);
        }
    }

    /**
     * 定期点検データ更新
     * @param operationId
     * @param contentId
     * @param taskReportDto
     * @param attachedChangeFlag
     * @param dataSendFlg
     * @param localSavedFlg
     * @throws IOException
     */
    public void updateRoutineTaskReport(long operationId, long contentId, TaskReportDto taskReportDto, boolean attachedChangeFlag, boolean dataSendFlg, boolean localSavedFlg) throws IOException {
        taskReportDto.attachedFileSendFlg = attachedChangeFlag;
        taskReportDto.dataSendFlg = dataSendFlg;
        taskReportDto.localSavedFlg = localSavedFlg;
        mTaskReportDao.update(taskReportDto);

        if (!StringUtil.isNullOrEmpty(taskReportDto.jsonData)) {
            JSONObject taskReportJson = new JSONObject(taskReportDto.jsonData);
            JSONObject taskJson = taskReportJson.getJSONObject("task");

            List<TaskReportItemsDto> taskReportItemsDaoList = mTaskReportItemsDao.getTaskReportItemByTaskKey(taskReportDto.taskKey, Constant.TaskReportLevel.ReportType);
            if (taskReportItemsDaoList.size() > 0) {
                // 更新
                for (TaskReportItemsDto taskReportItemsDto : taskReportItemsDaoList) {
                    try {
                        String newValue = taskJson.getString(taskReportItemsDto.itemKey);
                        if (!newValue.equals(taskReportItemsDto.inputValue)) {
                            // 値が異なる場合のみ、更新する
                            taskReportItemsDto.inputValue = newValue;
                            mTaskReportItemsDao.updateTaskReportItems(taskReportItemsDto);
                        }
                    } catch (JSONException e) {
                        // 値がStringではない場合、無視する
                        continue;
                    }
                }
            } else {
                // 登録
                Iterator taskKeys = taskJson.keys();
                while (taskKeys.hasNext()) {
                    TaskReportItemsDto taskReportItemsDto = new TaskReportItemsDto();
                    String itemKey = (String) taskKeys.next();
                    taskReportItemsDto.taskKey = taskReportDto.taskKey;
                    taskReportItemsDto.itemKey = itemKey;
                    taskReportItemsDto.taskReportLevel = Constant.TaskReportLevel.ReportType;
                    try {
                        taskReportItemsDto.inputValue = taskJson.getString(itemKey);
                    } catch (JSONException e) {
                        // 値がStringではない場合、無視する
                        continue;
                    }
                    mTaskReportItemsDao.insertTaskReportItems(taskReportItemsDto);
                }
            }

            String strReportStartDate = DateTimeUtil.toString_yyyyMMddHHmmss_none(taskReportDto.reportStartDate);
            String tempDirPath = ABVEnvironment.getInstance().getTempTaskDirPath(contentId, taskReportDto.taskKey);

            String routineTaskReportDirPath = ABVEnvironment.getInstance().getRoutineTaskReportDirFilePath(operationId, taskReportDto.taskKey, taskReportDto.taskReportId, strReportStartDate);

            if (taskReportDto.attachedFileSendFlg) {
                if (!StringUtil.isNullOrEmpty(taskReportDto.jsonData)) {
                    //添付ファイル変更の場合、以下の処理を行う
                    JSONObject attachedListJson = (new JSONObject(taskReportDto.jsonData)).getJSONObject("attached");
                    List<String> attachedFileNames = JSONObject.getValues(attachedListJson);
                    // コピー元のファイルで、添付ファイルとして使用しないファイル削除
                    deleteDifferentialFile(tempDirPath, attachedFileNames);
                    // 同じファイル名が存在すれば、コピー先ファイルを削除
                    deleteDifferentialFile(routineTaskReportDirPath, attachedFileNames);
                }
            }
            // 添付ディレクトリの移動
            boolean result = FileUtil.copy(tempDirPath, routineTaskReportDirPath, true);
            if (result) {
                FileUtil.delete(tempDirPath);
            }
        }
    }

    /**
     * 作業報告送信データ、新規
     * @param taskKey
     * @param taskReportJson
     * @param deleteFlg
     * @return
     * @throws IOException
     */
    public int insertTaskReportSend(String taskKey, int taskReportId, String reportStartDate, JSONObject taskReportJson, boolean deleteFlg, boolean hotspotChangeFlg) throws IOException {
        // 登録前にホットスポット更新フラグが存在すれば、そのデータは削除する
        mTaskReportSendDao.deleteHotspotChangedData(taskKey);

        TaskReportSendDto taskReportSendDto = new TaskReportSendDto();

        taskReportSendDto.taskKey = taskKey;

        if (taskReportId > 0 && !StringUtil.isNullOrEmpty(reportStartDate)) {
            taskReportSendDto.taskReportId = taskReportId;
            taskReportSendDto.reportStartDate = reportStartDate;
        }

        if (taskReportJson == null) {
            taskReportSendDto.jsonData = "";
        } else {
            taskReportSendDto.jsonData = taskReportJson.toString();
        }
        taskReportSendDto.saveDate = DateTimeUtil.getCurrentSqlDate();
        taskReportSendDto.deleteFlg = deleteFlg;
        taskReportSendDto.hotspotChangeFlg = hotspotChangeFlg;

        return mTaskReportSendDao.insert(taskReportSendDto);
    }

    /**
     * 作業報告送信データ、更新
     * @param taskKey
     * @param taskReportId
     * @param reportStartDate
     * @param taskReportJson
     * @param deleteFlg
     * @return
     * @throws IOException
     */
    public int updateTaskReportSend(String taskKey, int taskReportId, String reportStartDate, JSONObject taskReportJson, boolean deleteFlg) throws IOException {
        // 登録前にホットスポット更新フラグが存在すれば、そのデータは削除する
        mTaskReportSendDao.deleteHotspotChangedData(taskKey);

        // 最新のtaskReportSendデータを更新（更新時、データは１つしかないのが前提）
        TaskReportSendDto taskReportSendDto = mTaskReportSendDao.getLastTaskReportSend(taskKey);

        if (taskReportSendDto == null) {
            // データがない場合、insertを行うため新しいインスタントでセット
            taskReportSendDto = new TaskReportSendDto();
        }

        taskReportSendDto.taskKey = taskKey;

        if (taskReportId > 0 && !StringUtil.isNullOrEmpty(reportStartDate)) {
            taskReportSendDto.taskReportId = taskReportId;
            taskReportSendDto.reportStartDate = reportStartDate;
        }

        if (taskReportJson == null) {
            taskReportSendDto.jsonData = "";
        } else {
            taskReportSendDto.jsonData = taskReportJson.toString();
        }
        taskReportSendDto.saveDate = DateTimeUtil.getCurrentSqlDate();
        taskReportSendDto.deleteFlg = deleteFlg;

        int taskReportSendId = mTaskReportSendDao.update(taskReportSendDto);

        if (taskReportSendId == 0) {
            taskReportSendId = insertTaskReportSend(taskKey, taskReportId, reportStartDate, taskReportJson, false, false);
        }

        return taskReportSendId;
    }

    /**
     * 作業報告送信データID取得
     * @param taskKey
     * @return
     */
    public int getLastTaskReportSendId(String taskKey) {
        return mTaskReportSendDao.getLastTaskReportSendId(taskKey);
    }

    /**
     * 定期点検用、作業削除
     * @param operationId
     * @param contentId
     * @param dto
     */
    public void deleteRoutineTaskReport(long operationId, long contentId, TaskReportDto dto) {
        mTaskReportDao.deleteRoutineTaskReport(dto);

        String reportStartDate = DateTimeUtil.toString(dto.reportStartDate, DateTimeFormat.yyyyMMddHHmmss_hyphen);
        mTaskReportSendDao.deleteByTaskKeyIdDate(dto.taskKey, dto.taskReportId, reportStartDate);

        reportStartDate = DateTimeUtil.toString_yyyyMMddHHmmss_none(dto.reportStartDate);

        // 作業報告のディレクトリ削除
        FileUtil.delete(ABVEnvironment.getInstance().getTempTaskDirPath(contentId, dto.taskKey));
        FileUtil.delete(ABVEnvironment.getInstance().getRoutineTaskReportDirFilePath(operationId, dto.taskKey, dto.taskReportId, reportStartDate));
    }

    /**
     * 定期点検用、作業内容削除
     * @param operationId
     * @param contentId
     * @param taskKey
     * @param taskReportId
     * @param reportStartDate
     */
    public void deleteRoutineTaskReport(long operationId, long contentId, String taskKey, int taskReportId, String reportStartDate) {
        TaskReportDto taskReportDto = mTaskReportDao.getRoutineTaskReportUtc(taskKey, taskReportId, reportStartDate);
        if(taskReportDto != null) {
            taskReportDto.dataSendFlg = true;
            taskReportDto.attachedFileSendFlg = false;
            taskReportDto.jsonData = "";
            mTaskReportDao.update(taskReportDto);
        }

        TaskDto taskDto = mTaskDao.getTaskByTaskKey(taskKey);
        if (taskDto != null) {
            taskDto.taskStatus = 0;
            mTaskDao.update(taskDto);
        }

        // 作業報告のディレクトリ削除

        // reportStartDateをファイルパス用の文字列に変換
        String reportFileDateStr = DateTimeUtil.toString_yyyyMMddHHmmss_none(DateTimeUtil.toDate(reportStartDate, DateTimeFormat.yyyyMMddHHmmss_hyphen));
        FileUtil.delete(ABVEnvironment.getInstance().getTempTaskDirPath(contentId, taskKey));
        FileUtil.delete(ABVEnvironment.getInstance().getRoutineTaskReportDirFilePath(operationId, taskKey, taskReportId, reportFileDateStr));
    }


    public TaskReportDto getRoutineTaskReportUtc(String taskKey, int taskReportId, String reportStartDate) {
        return mTaskReportDao.getRoutineTaskReportUtc(taskKey, taskReportId, reportStartDate);
    }

    /**
     * ホットスポットの更新
     * @param taskKey
     * @param param
     */
    public void updateTaskHotspot(String taskKey, Map<String, String> param) throws IOException {
        TaskDto taskDto = mTaskDao.getTaskByTaskKey(taskKey);
        TaskReportDto taskReportDto = mTaskReportDao.getTaskReport(taskKey, Constant.TaskReportLevel.ReportType);
        if (mTaskReportSendDao.isNotExistSendableData(taskReportDto.taskKey) && !taskReportDto.dataSendFlg && !taskReportDto.localSavedFlg) {
            // 送信データが存在しない場合のみ、ホットスポットをtrueにセット
            insertTaskReportSend(taskReportDto.taskKey, taskReportDto.taskReportId, null, new JSONObject(taskReportDto.jsonData), false, true);
        }
        taskDto.taskHotSpotInfo = param.get(ABookKeys.HOT_SPOT);
        taskReportDto.dataSendFlg = true;
        mTaskReportDao.update(taskReportDto);
        mTaskDao.update(taskDto);
    }


    /**
     * ホットスポットの位置更新
     *
     * @param taskKey
     * @param newX
     * @param newY
     */

    public void updateTaskHotspot(String taskKey, float newX, float newY, int taskReportLevel) throws IOException {
        TaskDto taskDto = mTaskDao.getTaskByTaskKey(taskKey);
        // #32782 指示者テーブル関連削除 start
        TaskReportDto taskReportDto = mTaskReportDao.getTaskReport(taskKey, taskReportLevel);
        JSONObject hotSpot = new JSONObject(taskDto.taskHotSpotInfo);
        hotSpot.put("x", newX);
        hotSpot.put("y", newY);
        taskDto.taskHotSpotInfo = hotSpot.toString();
        if (mTaskReportSendDao.isNotExistSendableData(taskReportDto.taskKey) && !taskReportDto.dataSendFlg && !taskReportDto.localSavedFlg) {
            // 送信データが存在しない場合のみ、ホットスポットをtrueにセット
            insertTaskReportSend(taskReportDto.taskKey, taskReportDto.taskReportId, null, new JSONObject(taskReportDto.jsonData), false, true);
        }
        taskReportDto.dataSendFlg = true;
        mTaskReportDao.update(taskReportDto);
        mTaskDao.update(taskDto);
    }

    /**
     * 作業指示、作業報告用のjsonファイル
     * taskReport.jsonとtaskReportSuggest.jsonとhotspot.jsonを作成
     *
     * @param operationId
     * @param contentPath
     * @param routineTaskReportFlg
     * @throws IOException
     */
    public void createJsonForOperationContent(Long operationId, String contentPath, boolean routineTaskReportFlg) throws IOException {
        try {
            if (routineTaskReportFlg) {
                // 定期点検
                createRoutineTaskReportJson(operationId, contentPath);
            } else {
                createTaskReportJson(operationId, contentPath);
            }
            createTaskReportSuggestJson(operationId, contentPath);
            createHopSpotJson(operationId, contentPath);
        } catch (IOException e) {
            Logger.e(TAG, "createJsonForOperationContent error : ", e);
            throw e;
        }
    }

    public void createJsonForRoutineTaskReport(int reportCycle, int enableReportUpdate, int taskReportId, String contentPath) throws IOException {
        JSONObject routineTaskJson = new JSONObject();

        routineTaskJson.put(ABookKeys.REPORT_CYCLE, reportCycle);
        routineTaskJson.put(ABookKeys.ENABLE_REPORT_UPDATE, enableReportUpdate);
        routineTaskJson.put(ABookKeys.TASK_REPORT_ID, taskReportId);

        Logger.d(TAG, "routineTask.json : " + routineTaskJson.toString());
        FileUtil.createFile(contentPath + "/" + ABookKeys.ROUTINE_TASK + ".json", routineTaskJson.toString());
    }

    /**
     * 作業指示、作業報告用のjsonファイル
     * content.jsonとworkerGroup.jsonを作成
     *
     * @throws IOException
     */
    public void createJsonForOpenABookCheckPano(Long operationId, Long contentId, String contentPath) throws IOException {
        try {
            createContentJson(operationId, contentId, contentPath);
            // #32926 作業報告画面改善 start
            // 作業タイプ確認用のparameterを追加
            createWorkerGroupJson(operationId, contentPath);
            // #32926 作業報告画面改善 end
        } catch (IOException e) {
            Logger.e(TAG, "createJsonForOpenABookCheckPano error : ", e);
            throw e;
        }
    }


    /**
     * content.jsonファイル作成
     *
     * @param contentId
     * @param contentPath
     * @throws IOException
     */
    private void createContentJson(Long operationId, Long contentId, String contentPath) throws IOException {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("workerCode", ABVDataCache.getInstance().getMemberInfo().workerCode);

        //　データディレクトリへの相対パス
        jsonObject.put("attachedPath", "../../../../files/ABook/operation/" + operationId);
        jsonObject.put("attachedMoviePath", ABVEnvironment.getInstance().getAttachedMoviesFilePath(contentId));

        // マスタデータのパースを参照するために「content.json」に保存する。
        jsonObject.put("masterPath", ABVEnvironment.getInstance().getMasterFilePath());

        FileUtil.createFile(contentPath + "/content.json", jsonObject.toString());
    }

    /**
     * workerGroup.jsonファイル作成
     *
     * @param operationId
     * @param contentPath
     * @throws IOException
     */
    // 現在作業タイプを確認用のparameter追加
    private void createWorkerGroupJson(Long operationId, String contentPath) throws IOException {
        int level = 0;
        int listCount = 0;
        List<JSONObject> workerGroupJsonList = new ArrayList<JSONObject>();
        JSONObject taskGroupJson = new JSONObject();
        List<TaskWorkerGroupDto> taskWorkerGroupDtoList = mTaskWorkerGroupDao.getTaskWorkerGroupByOperationId(operationId);
        // 報告と報告（回答）のデータの区分が必要なので、「taskGroup_0」と「taskGroup_1」で形式で作成

        for (TaskWorkerGroupDto dto : taskWorkerGroupDtoList) {
            listCount++;        // 最後のインデックスの判定のため
            if (level != dto.taskReportLevel) {
                taskGroupJson.put(String.format("taskReport_%s", String.valueOf(level)), workerGroupJsonList);
                workerGroupJsonList = new ArrayList<JSONObject>();
                level++;
            }

            JSONObject workerGroupJson = new JSONObject();
            workerGroupJson.put(ABookKeys.GROUP_ID, dto.groupId);
            workerGroupJson.put(ABookKeys.GROUP_NAME, dto.groupName);
            workerGroupJsonList.add(workerGroupJson);

            // 最後の時、taskReport_{level}でセット
            if (listCount == taskWorkerGroupDtoList.size()) {
                taskGroupJson.put(String.format("taskGroup_%s", String.valueOf(level)), workerGroupJsonList);
            }
        }

        Logger.d(TAG, "createWorkerGroupJson : %s  contentPath : %s ", taskGroupJson.toString(), contentPath + "/workingGroup.json");
        FileUtil.createFile(contentPath + "/workingGroup.json", taskGroupJson.toString());
    }

    /**
     * taskHotspot.jsonファイル作成
     *
     * @param operationId
     * @param contentPath
     * @throws IOException
     */
    public void createHopSpotJson(Long operationId, String contentPath) throws IOException {
        OperationDto operationDto = mOperationDao.getOperation(operationId);

        //営業日変更時間チェックにより、除外されたhotsport情報作成しない。
        boolean routineTaskFlg = operationDto.reportType == Constant.ReportType.RoutineTask ? true : false;

        List<String> taskKeyArray = new ArrayList<String>();
        if (!routineTaskFlg) {
            String jsonText = FileUtil.readTextFile(contentPath + "/" + ABookKeys.TASK_REPORT + ".json");
            JSONObject jsonObject = new JSONObject(jsonText);
            JSONArray  taskReportJsonArray = jsonObject.has(ABookKeys.KEY_TASK_REPORT_0) ? jsonObject.getJSONArray(ABookKeys.KEY_TASK_REPORT_0) : new JSONArray();
            for (int i = 0; i < taskReportJsonArray.length(); i++) {
                JSONObject taskReportJsonObject = taskReportJsonArray.getJSONObject(i);
                String taskKey = taskReportJsonObject.getString(ABookKeys.TASK_KEY);
                if (taskKey != null) {
                    taskKeyArray.add(taskKey);
                }
            }
        }

        List<JSONObject> taskJsonList = new ArrayList<JSONObject>();
        JSONObject hotspotJson = new JSONObject();
        List<TaskDto> taskDtoList = mTaskDao.selectTaskByOperationId(operationId);
        for (TaskDto dto : taskDtoList) {
            if (!routineTaskFlg && !taskKeyArray.contains(dto.taskKey)) {
                continue;
            }
            if (!StringUtil.isNullOrEmpty(dto.taskHotSpotInfo)) {
                JSONObject taskHotSpot = new JSONObject(dto.taskHotSpotInfo);
                taskHotSpot.put(OperationDataJSON.TaskCode, dto.taskCode);
                taskJsonList.add(taskHotSpot);
            }
        }
        hotspotJson.put("hotspot", taskJsonList);
        Logger.d(TAG, "createHopSpotJson : " + hotspotJson.toString());
        FileUtil.createFile(contentPath + "/taskHotspot.json", hotspotJson.toString());
    }

    /**
     * 作業報告用のjsonファイル
     * 報告と報告（回答）のデータの区分が必要なので、「taskReport_0」と「taskReport_1」で形式で作成
     * 報告（回答）の場合はtaskReport_1を作成
     *
     * @param operationId
     * @param contentPath
     * @throws IOException
     */
    public void createTaskReportJson(Long operationId, String contentPath) throws IOException {
        List<JSONObject> taskReportJsonList = new ArrayList<JSONObject>();
        JSONObject taskReportJson = new JSONObject();
        OperationDto operationDto = mOperationDao.getOperation(operationId);
        List<TaskReportDto> taskReportDtoList = mTaskReportDao.getTaskReportByOperationId(operationId);

        //報告ステータスが完了のタスクキーを保存し、営業日変更時間チェック時に除外する。
        List<TaskDto> taskDtoList = mTaskDao.selectAllTaskByOperationId(operationId);
        List<String> finishStatusTaskKeyList = new ArrayList<String>();
        for (TaskDto taskDto : taskDtoList) {
            if (taskDto.taskStatus == FINISHED_STATUS) {
                if (!finishStatusTaskKeyList.contains(taskDto.taskKey)) {
                    finishStatusTaskKeyList.add(taskDto.taskKey);
                }
            }
        }

        int level = 0;
        int listCount = 0;
        for (TaskReportDto dto : taskReportDtoList) {
            listCount++;        // 最後のインデックスの判定のため
            if (level != dto.taskReportLevel) {
                taskReportJson.put(String.format("taskReport_%s", String.valueOf(level)), taskReportJsonList);
                taskReportJsonList = new ArrayList<JSONObject>();
                level++;
            }

            if(dto.jsonData != null && dto.jsonData.length() > 0) {
                JSONObject editJson = new JSONObject(dto.jsonData);
                //報告ステータスが完了の場合、営業日変更時間チェックを行う
                if (finishStatusTaskKeyList.contains(dto.taskKey)) {
                    if (checkWorkdayChangeTime(editJson)) {
                        continue;
                    }
                }
                if (operationDto.reportType == Constant.ReportType.ReportReply) {
                    editJson.put(ABookKeys.HAS_AUTHORITY, dto.enableReport);
                    editJson.put(ABookKeys.LOCAL_SAVE_FLG, dto.localSavedFlg);
                    // ロックと差戻情報の追加
                    addLockSendBackInfo(editJson, dto);
                    taskReportJsonList.add(editJson);
                } else {
                    editJson = new JSONObject(dto.jsonData);
                    // ロックと差戻情報の追加
                    addLockSendBackInfo(editJson, dto);
                    taskReportJsonList.add(editJson);
                }
            } else {
                JSONObject emptyTaskReportJson = new JSONObject();
                emptyTaskReportJson.put(ABookKeys.TASK_KEY, dto.taskKey);
                if (operationDto.reportType == Constant.ReportType.ReportReply) {
                    emptyTaskReportJson.put(ABookKeys.HAS_AUTHORITY, dto.enableReport);
                    emptyTaskReportJson.put(ABookKeys.LOCAL_SAVE_FLG, dto.localSavedFlg);
                }
                // ロックと差戻情報の追加
                addLockSendBackInfo(emptyTaskReportJson, dto);
                taskReportJsonList.add(emptyTaskReportJson);
            }
            // 最後の時、taskReport_{level}でセット
            if (listCount == taskReportDtoList.size()) {
                taskReportJson.put(String.format("taskReport_%s", String.valueOf(level)), taskReportJsonList);
            }
        }
        taskReportJson.put(OperationStartDate, DateTimeUtil.toString(operationDto.operationStartDate, DateTimeFormat.yyyyMMddHHmmss_hyphen));
        taskReportJson.put(OperationEndDate, DateTimeUtil.toString(operationDto.operationEndDate, DateTimeFormat.yyyyMMddHHmmss_hyphen));

        // データが存在しない場合、
        if (taskReportDtoList.size() <= 0 || taskReportJson.length() == 0) {
            taskReportJson.put("taskReport_0", taskReportJsonList);
        }

        Logger.d(TAG, "createTaskReportJson : " + taskReportJson.toString());
        Logger.d(TAG,contentPath + "/" + ABookKeys.TASK_REPORT + ".json");

        FileUtil.createFile(contentPath + "/" + ABookKeys.TASK_REPORT + ".json", taskReportJson.toString());
    }

    /**
     * ロックと差戻情報の追加
     *
     * @param json 格納するJSON
     * @param dto もとになる報告書
     * @return
     */
    private void addLockSendBackInfo(JSONObject json, TaskReportDto dto) {
        json.put(OperationDataJSON.TaskStatus, dto.taskStatus);
        json.put(OperationDataJSON.ReportLockUserId, dto.reportLockUserId);
        json.put(OperationDataJSON.ReportLockUserName, dto.reportLockUserName);
        json.put(OperationDataJSON.ReportLockTime, dto.reportLockTime);
        json.put(OperationDataJSON.SendBackUserId, dto.sendBackUserId);
        json.put(OperationDataJSON.SendBackUserName, dto.sendBackUserName);
        json.put(OperationDataJSON.SendBackComment, dto.sendBackComment);
    }


    /**
     * 営業日変更時間と報告日時をチェックし、営業日変更時間が過ぎた場合、報告表示しないように処理
     * @param jsonObject 報告回答のJson文字列
     * @return true: 報告非表示、false：報告表示
     */
    private boolean checkWorkdayChangeTime(JSONObject jsonObject) {
        JSONArray taskReplyArray = jsonObject.getJSONArray(ABookKeys.KEY_TASK_REPLY);
        boolean workdayChangeTimeflg = true;
        for (int i = 0; i < taskReplyArray.length(); i++) {
            JSONObject taskReplyJSONObject = taskReplyArray.getJSONObject(i);
            String pageNo = taskReplyJSONObject.getString(ABookKeys.KEY_QUESTION_NO);
            if (pageNo.equals("6")) {
                //報告日付を取得し、Date型に変換
                String createReportDateStr = taskReplyJSONObject.getString(ABookKeys.KEY_VALUE);
                Date createReportDate = DateTimeUtil.toDate(createReportDateStr, "Asia/Tokyo", DateTimeFormat.yyyyMMddHHmm_hyphen);
                //チェック用の営業日変更開始日取得
                Date workdayStartChangeDate = workdayChangeDateStartDate();
                if (createReportDate == null || workdayStartChangeDate == null) {
                    Logger.e(TAG, "checkWorkdayChangeTime createReportDate or workdayStartChangeDate null");
                    return false;
                }
                //チェック用の営業日変更終了日計算
                Calendar calendar = Calendar.getInstance(); // creates calendar
                calendar.setTime(workdayStartChangeDate); // sets calendar time/date
                int day = calendar.get(Calendar.DATE);
                calendar.set(Calendar.DATE, day + 1);
                Date workdayEndChangeDate = calendar.getTime();

                //作業報告作成日が営業日変更開始日と終了日の間にあるかチェック
                if (workdayStartChangeDate.compareTo(createReportDate) == 0 ||
                        (workdayStartChangeDate.before(createReportDate) && workdayEndChangeDate.after(createReportDate))) {
                    workdayChangeTimeflg = false;
                }
            }
        }
        return workdayChangeTimeflg;
    }

    /**
     * 営業日変更時間を元にして営業日変更開始日を計算
     * @return 営業日変更開始日
     */
    private Date workdayChangeDateStartDate() {
        String operationChangeTime = ABVDataCache.getInstance().serviceOption.operationChangeTime();
        String[] timeArray = operationChangeTime.split(":", 0);
        if (timeArray.length != 2) { //データ形式が異常
            return null;
        }
        // 営業日変更時間
        int hour = Integer.valueOf(timeArray[0]); //時間
        int minute = Integer.valueOf(timeArray[1]); //分
        Calendar calendar = Calendar.getInstance();

        calendar.set(Calendar.HOUR_OF_DAY, hour);
        calendar.set(Calendar.MINUTE, minute);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date workdayStartChangeDate = calendar.getTime();

        if (hour != 0) { //「00:00」以外設定時
            Date nowDate = new Date();
            if (nowDate.before(workdayStartChangeDate)) {
                int day = calendar.get(Calendar.DATE);
                calendar.set(Calendar.DATE, day - 1);
                workdayStartChangeDate = calendar.getTime();
            }
        }
        return workdayStartChangeDate;
    }

    /**
     * 定期点検用のJSON作成
     * @param operationId
     * @param contentPath
     * @throws IOException
     */
    public void createRoutineTaskReportJson(Long operationId, String contentPath) throws IOException {
        List<JSONObject> taskReportJsonList = new ArrayList<JSONObject>();
        JSONObject taskReportJsonRoot = new JSONObject();
        JSONObject taskReportJsonRow;
        List<TaskReportDto> routineTaskReportDtoList = mTaskReportDao.getTaskReportByOperationId(operationId);
        // 現在の作業タイプは定期点検の場合
        for (TaskReportDto dto : routineTaskReportDtoList) {
            taskReportJsonRow = new JSONObject();
            taskReportJsonRow.put(ABookKeys.TASK_REPORT_INFO_ID, dto.taskReportInfoId);
            taskReportJsonRow.put(ABookKeys.REPORT_START_DATE, DateTimeUtil.toStringInTimeZone(dto.reportStartDate, DateTimeFormat.yyyyMMddHHmmss_hyphen, DateTimeUtil.getLocalTimeZone()));
            taskReportJsonRow.put(ABookKeys.REPORT_END_DATE, DateTimeUtil.toStringInTimeZone(dto.reportEndDate, DateTimeFormat.yyyyMMddHHmmss_hyphen, DateTimeUtil.getLocalTimeZone()));
            // 一時保存フラグ設定
            taskReportJsonRow.put(ABookKeys.LOCAL_SAVE_FLG, dto.localSavedFlg);
            List<JSONObject> taskReportInfoList = new ArrayList<JSONObject>();
            if (!StringUtil.isNullOrEmpty(dto.jsonData)) {
                taskReportInfoList.add(new JSONObject(dto.jsonData));
            }
            taskReportJsonRow.put(ABookKeys.TASK_REPORT_INFO, taskReportInfoList);
            // ロックと差戻の情報を追加
            addLockSendBackInfo(taskReportJsonRow, dto);
            taskReportJsonList.add(taskReportJsonRow);
        }
        OperationDto operationDto = mOperationDao.getOperation(operationId);
        taskReportJsonRoot.put(ABookKeys.ENABLE_REPORT_UPDATE, operationDto.enableReportUpdate);

        taskReportJsonRoot.put(OperationStartDate, DateTimeUtil.toString(operationDto.operationStartDate, DateTimeFormat.yyyyMMddHHmmss_hyphen));
        taskReportJsonRoot.put(OperationEndDate, DateTimeUtil.toString(operationDto.operationEndDate, DateTimeFormat.yyyyMMddHHmmss_hyphen));
        // 最後のtaskReportId
        TaskReportDto lastRoutineTaskReportDto = mTaskReportDao.getLastRoutineTaskReportData(operationId);
        taskReportJsonRoot.put(ABookKeys.TASK_REPORT_ID, lastRoutineTaskReportDto.taskReportId);
        taskReportJsonRoot.put("taskReport_0", taskReportJsonList);
        Logger.d(TAG, "createRoutineTaskReportJson : " + taskReportJsonRoot.toString());
        Logger.d(TAG,contentPath + "/" + ABookKeys.TASK_REPORT + ".json");
        FileUtil.createFile(contentPath + "/" + ABookKeys.TASK_REPORT + ".json", taskReportJsonRoot.toString());
    }

    private void createTaskReportSuggestJson(Long operationId, String contentPath) throws IOException {
        JSONObject itemJson = new JSONObject();
        JSONObject taskReportSuggestJson = new JSONObject();
        List<TaskReportItemsDto> taskReportItemsDtoList = mTaskReportItemsDao.getTaskReportItemByOperationId(operationId);
        for (TaskReportItemsDto dto : taskReportItemsDtoList) {
            JSONArray jsonArray = new JSONArray();
            if (itemJson.has(dto.itemKey)) {
                boolean result = false;
                jsonArray = itemJson.getJSONArray(dto.itemKey);
                for (int i = 0; i < jsonArray.length(); i++) {
                    if (jsonArray.get(i).equals(dto.inputValue)) {
                        result = true;
                        break;
                    }
                }
                if (!result) {
                    jsonArray.put(dto.inputValue);
                    itemJson.put(dto.itemKey, jsonArray);
                }
            } else {
                jsonArray.put(dto.inputValue);
                itemJson.put(dto.itemKey, jsonArray);
            }
        }
        // #32926 作業報告画面改善 start
        List<TaskReportDto> taskReportList = mTaskReportDao.getTaskReportByOperationId(operationId);
        for (TaskReportDto  dto : taskReportList) {
            taskReportSuggestJson.put(String.format("%s_%s", ABookKeys.TASK_REPORT_SUGGEST, String.valueOf(dto.taskReportLevel)), itemJson);
        }
        Logger.d(TAG, "createTaskReportSuggestJson : " + taskReportSuggestJson.toString());
        Logger.d(TAG,contentPath + "/" + ABookKeys.TASK_REPORT_SUGGEST + ".json");
        // #32926 作業報告画面改善 end
        FileUtil.createFile(contentPath + "/" + ABookKeys.TASK_REPORT_SUGGEST + ".json", taskReportSuggestJson.toString());
    }

    /**
     * プロジェクト情報取得
     *
     * @param operationId
     * @return
     */
    public OperationDto getOperation(Long operationId) {
        return mOperationDao.getOperationJoinContent(operationId);
    }

    /**
     * プロジェクトの作業情報取得
     *
     * @param operationId
     * @return
     */
    public List<TaskDto> getOperationTask(Long operationId) {
        return mTaskDao.selectTaskByOperationId(operationId);
    }

    /**
     * 作業キーで報告リスト取得
     *
     * @param taskKey
     * @return
     */
    public List<TaskReportDto> getTaskReportByTaskKey(String taskKey) {
        return mTaskReportDao.getTaskReportListByTaskKey(taskKey);
    }

    /**
     * 作業一覧に表示する作業情報取得
     *
     * @param searchWord 検索キーワード
     * @param searchStartDateStr 開始期間
     * @param searchEndDateStr 終了期間
     * @return 作業情報配列
     */
    public List<OperationDto> getRefreshOperation(String searchWord, String searchStartDateStr, String searchEndDateStr, String reportTypeStr) {
        List<OperationDto> list = mOperationDao.getOperations(searchWord, searchStartDateStr, searchEndDateStr, reportTypeStr);
        WorkingReportDao dao = AbstractDao.getDao(WorkingReportDao.class);

        // ローカル保存している分を補正する
        Map<Long, Integer> working = dao.getWorkingTaskReportCounts();
        for (OperationDto dto : list) {
            Integer workingCount = working.get(dto.operationId);
            if (workingCount != null) {
                dto.statusNotStartedCount += workingCount;
            }
        }

        return list;
    }

    /**
     * 作業毎に情報更新ボタンを表示するため、NeedSyncFlgをセットする。
     * @param operationDto 作業情報
     * @param syncedDate 最後に情報更新した日付
     */
    public void updateOperationNeedSyncFlg(OperationDto operationDto, Date syncedDate) {
        //情報更新が既に表示状態は何もしない
        if (operationDto.needSyncFlg) {
            return;
        }

        boolean updateFlg = false;
        // 作業送信フラグが存在する場合またはホットスポット更新フラグが存在する場合、needSyncFlgをtrueにセット
        if (mTaskReportDao.isExistSendTaskData(operationDto.operationId) ||
                mTaskReportDao.isExistUpdateTargetHotSpotTaskData(operationDto.operationId)) {
            updateFlg = true;
        }

        // 定期点検の場合
        if (operationDto.reportType == Constant.ReportType.RoutineTask) {
            if (StringUtil.isNullOrEmpty(operationDto.reportPeriod)) {
                updateFlg = true;
            }
        }

        if (updateFlg) {
            operationDto.needSyncFlg = true;
            mOperationDao.updateNeedSyncFlg(operationDto.operationId, operationDto.needSyncFlg);
        }
    }

    /**
     * プロジェクト関連資料または共通資料一覧取得
     *
     * @return
     */
    public List<CategoryContentDto> getOperationRelatedContent() {
        List<ContentDto> contentDtoList;
        contentDtoList = mContentDao.getCommonContent();


        List<CategoryContentDto> categoryContentDtoArray = new ArrayList<CategoryContentDto>();
        List<ContentDto> saveContentDtoArray = new ArrayList<ContentDto>();
        CategoryContentDto categoryContentDto = new CategoryContentDto();

        for (ContentDto contentDto : contentDtoList) {
            if (contentDto.categoryId == categoryContentDto.categoryId) {
                    saveContentDtoArray.add(contentDto);
            } else {
                if (saveContentDtoArray.size() != 0) {
                    categoryContentDto.contentDtoArray = saveContentDtoArray;
                    categoryContentDtoArray.add(categoryContentDto);
                }
                categoryContentDto = new CategoryContentDto();
                saveContentDtoArray = new ArrayList<ContentDto>();
                categoryContentDto.categoryId = contentDto.categoryId;
                categoryContentDto.categoryName = contentDto.categoryNames;
                saveContentDtoArray.add(contentDto);
            }
            if (contentDtoList.get(contentDtoList.size() - 1).equals(contentDto)) {
                categoryContentDto.contentDtoArray = saveContentDtoArray;
                categoryContentDtoArray.add(categoryContentDto);
            }
        }
        return categoryContentDtoArray;
    }

    /**
     * プロジェクト関連資料コンテンツ存在チェック
     *
     * @param operationId
     * @return
     */
    public boolean isExistOperationRelatedContent(Long operationId) {
        boolean rtnVal = false;
        List<ContentDto> contentDtoList = mContentDao.getOperationRelatedContent(operationId);
        if (contentDtoList != null && contentDtoList.size() > 0) {
            rtnVal = true;
        }
        return rtnVal;
    }

    /**
     * 共通資料コンテンツ存在チェック
     *
     * @return
     */
    public boolean isExistCommonContent() {
        boolean rtnVal = false;
        List<ContentDto> contentDtoList = mContentDao.getCommonContent();
        if (contentDtoList != null && contentDtoList.size() > 0) {
            rtnVal = true;
        }
        return rtnVal;
    }

    /**
     * プロジェクト関連資料かチェック
     *
     * @param contentId
     * @param operationId
     * @return
     */
    public boolean isOperationRelatedContent(long contentId, long operationId) {
        return mContentDao.isOperationRelatedContent(contentId, operationId);
    }

    /**
     * 共通資料コンテンツかチェック
     *
     * @param contentId
     * @return
     */
    public boolean isCommonContent(long contentId) {
        return mContentDao.isCommonContent(contentId);
    }

    /**
     * 作業報告履歴データ送信(全体)
     * @param operationId
     * @param progressCallback
     * @return
     * @throws Exception
     */
    public void sendTaskReportSendData(long operationId, Callback progressCallback) throws Exception {
        sendTaskReportSendData(operationId, null, null, progressCallback);
    }

    /**
     * 作業報告履歴データ送信
     * @param operationId
     * @param taskKey
     * @throws Exception
     */
    public void sendTaskReportSendData(long operationId, String taskKey, Integer taskReportLevel, Callback progressCallback) throws Exception {
        OperationDto operationDto = mOperationDao.getOperation(operationId);
        List<TaskReportDto> taskReportDtoList = null;
        int maxProgress = 0;

        if (taskKey == null) {
            taskReportDtoList = mTaskReportDao.getSendableTaskData(operationId);
            maxProgress = 40;
        } else {
            taskReportDtoList = mTaskReportDao.getSendableTaskData(operationId, taskKey, taskReportLevel);
            maxProgress = 100;
        }

        if (taskReportDtoList != null && taskReportDtoList.size() > 0) {

            int allSendCount = mTaskReportSendDao.getSendableTaskReportSendDataCount(operationId, taskKey);
            allSendCount = allSendCount == 0 ? 1 : allSendCount;
            int progress = maxProgress / allSendCount;

            // 送信済みsendIdをまとめて削除するため、リストに追加して最後に削除
            List<Integer> removeTaskReportSendIds = new ArrayList<Integer>();

            for (TaskReportDto taskReportDto : taskReportDtoList) {

                List<TaskReportSendDto> taskReportSendDtoList;
                int lastTaskReportSendId;
                if (operationDto.reportType == Constant.ReportType.RoutineTask) {
                    // 定期点検
                    String reportStartDateStr = DateTimeUtil.toString(taskReportDto.reportStartDate, DateTimeFormat.yyyyMMddHHmmss_hyphen);
                    taskReportSendDtoList = mTaskReportSendDao.getSendableTaskReportSendData(taskReportDto.taskKey, taskReportDto.taskReportId, reportStartDateStr);
                    lastTaskReportSendId = mTaskReportSendDao.getLastTaskReportSendId(taskReportDto.taskKey, taskReportDto.taskReportId, reportStartDateStr);
                } else {
                    taskReportSendDtoList = mTaskReportSendDao.getSendableTaskReportSendData(taskReportDto.taskKey);
                    lastTaskReportSendId = mTaskReportSendDao.getLastTaskReportSendId(taskReportDto.taskKey);
                }

                for (TaskReportSendDto taskReportSendDto : taskReportSendDtoList) {
                    // 履歴なし：元のattachedFileSendFlg
                    // 履歴あり：true、ファイルに変更が無くてもzipファイル作成
                    // 履歴ありで、ローカルの添付ファイルが存在する場合、attachedFileSendFlgをtrueにセットする
                    if (taskReportSendDto.taskReportSendId == lastTaskReportSendId) {
                        if (!StringUtil.isNullOrEmpty(taskReportDto.localAttachedFileName) && operationDto.enableReportHistory == Constant.EnableReportHistory.Enable) {
                            taskReportDto.attachedFileSendFlg = true;
                        }
                    } else {
                        taskReportDto.attachedFileSendFlg = true;
                    }

                    File zipFile = null;
                    String strReportStartDate = DateTimeUtil.toString_yyyyMMddHHmmss_none(taskReportDto.reportStartDate);

                    if (taskReportDto.attachedFileSendFlg) {
                        String fileName = null;
                        zipFile = createAttachedFileForSend(operationId, taskReportDto.taskKey, taskReportSendDto.taskReportSendId, operationDto.reportType == Constant.ReportType.RoutineTask, taskReportDto.taskReportId, strReportStartDate, taskReportDto.taskReportLevel);
                        if (zipFile != null) {
                            fileName = FileUtil.getFilenameWithoutExt(zipFile.getName());
                        }
                        taskReportDto.localAttachedFileName = fileName != null ? SecurityUtil.getMd5Hash(fileName) : taskReportDto.localAttachedFileName;
                    }

                    try {
                        AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).sendTaskData(
                                cache.getMemberInfo().sid,
                                operationId,
                                taskReportDto,
                                zipFile,
                                taskReportSendDto.saveDate,
                                operationDto.reportType,
                                taskReportSendDto.hotspotChangeFlg
                        );
                        progressCallback.callback(new Integer(progress));
                        removeTaskReportSendIds.add(taskReportSendDto.taskReportSendId);

                        FileUtil.delete(ABVEnvironment.getInstance().getOperationTaskReportSendDirFilePath(operationId, taskReportSendDto.taskKey, taskReportSendDto.taskReportSendId));
                    } catch (AcmsException ex) {
                        if (ex.getCode() == ABVExceptionCode.P_E_ACMS_P005 || ex.getCode() == ABVExceptionCode.P_E_ACMS_P006) {
                            mTaskReportSendDao.deleteBySendId(taskReportSendDto.taskReportSendId);
                            FileUtil.delete(ABVEnvironment.getInstance().getOperationTaskReportSendDirFilePath(operationId, taskReportSendDto.taskKey, taskReportSendDto.taskReportSendId));
                            taskReportDto.dataSendFlg = false;
                            mTaskReportDao.update(taskReportDto);
                        }
                        throw ex;
                    } catch (Exception ex) {
                        Logger.e(TAG, ex);
                        if (ex instanceof NetworkDisconnectedException) {
                            // オフラインの場合、エラー扱いにしないで、設問の状況から、taskStatusを推測する
                            taskReportDto.taskStatus = recoveryTaskStatus(taskReportDto.jsonData);
                            taskReportDto.dataSendFlg = true;
                            mTaskReportDao.update(taskReportDto);
                            // CMSへの送信は打ち切り
                            return;
                        } else {
                            throw ex;
                        }
                    } finally {
                        if (zipFile != null) {
                            zipFile.delete();
                        }
                    }
                }

                if (taskReportDto.delFlg) {
                    // 物理削除
                    mTaskDao.deleteTaskData(taskReportDto.taskKey);
                } else {
                    taskReportDto.dataSendFlg = false;
                    taskReportDto.attachedFileSendFlg = false;
                    mTaskReportDao.update(taskReportDto);
                    // 削除
                    for (Integer removeTaskReportSendId : removeTaskReportSendIds) {
                        mTaskReportSendDao.deleteBySendId(removeTaskReportSendId);
                    }
                }
            }
        }
    }

    private int recoveryTaskStatus(String taskReportInfo) {
        JSONObject jsonObject = new JSONObject(taskReportInfo);
        JSONObject taskObject = jsonObject.getJSONObject(Task);
        Iterator<String> keys = taskObject.keys();
        while (keys.hasNext()) {
            String key = keys.next();
            if (key.startsWith("q_3_")) {   // 状況入力欄
                try {
                    int taskStatus = Integer.parseInt(taskObject.getString(key));
                    if (taskStatus != 0 && taskStatus != 999) {
                        taskStatus = 1;
                    }
                    return taskStatus;
                } catch (Throwable ignored) {
                    return 0;
                }
            }
        }
        return 0;
    }

    /**
     * 作業関連ディレクトリ削除
     *
     * @param operationId
     * @param contentId
     * @param taskKey
     */
    public void deleteTaskFileData(long operationId, long contentId, String taskKey, int taskReportLevel) {
        FileUtil.delete(ABVEnvironment.getInstance().getTempTaskDirPath(contentId, taskKey));
        if (taskReportLevel == Constant.TaskReportLevel.ReportType) {
            // 報告
            FileUtil.delete(ABVEnvironment.getInstance().getOperationTaskDirFilePath(operationId, taskKey));
        } else {
            // 報告（回答）
            FileUtil.delete(ABVEnvironment.getInstance().getOperationTaskReportLevelDirPath(operationId, taskKey, taskReportLevel));
        }
    }

    /**
     * プロジェクトの最終更新日と同期フラグの更新
     *
     * @param operationId
     * @param lastEditDate
     */
    public void finishedSyncOperation(Long operationId, Date lastEditDate, Date lastSyncDate) {
        OperationDto operationDto = mOperationDao.getOperationJoinContent(operationId);
        operationDto.lastEditDate = lastEditDate;
        operationDto.operationLastSyncDate = lastSyncDate;
        operationDto.needSyncFlg = false;
        mOperationDao.update(operationDto);
    }

    /**
     * プロジェクトの同期フラグの更新
     *
     * @param operationId
     */
    public void updateSyncOperation(Long operationId, boolean syncFlg) {
        mOperationDao.updateNeedSyncFlg(operationId, syncFlg);
    }

    /**
     * タスク関連zipファイル取得
     * @param operationId
     * @param taskKey
     * @param taskId
     * @param attachedFileName
     * @param type
     * @return
     * @throws ABVException
     * @throws InterruptedException
     */
    public String getTaskFile(long operationId, String taskKey, long taskId, String attachedFileName, Integer type) throws ABVException, InterruptedException {
        GetTaskFileParameters parameters = new GetTaskFileParameters(cache.getMemberInfo().sid, taskId, type);
        String outputFilePath = ABVEnvironment.getInstance().getOperationTaskDirFilePath(operationId, taskKey) + "/" + attachedFileName + ".zip";
        AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).getTaskFile(parameters, operationId, outputFilePath);
        return outputFilePath;
    }

    /**
     * 定期点検関連zipファイル取得
     * @param operationId
     * @param taskKey
     * @param taskId
     * @param taskReportInfoId
     * @param reportStartDate
     * @param attachedFileName
     * @return
     * @throws ABVException
     * @throws InterruptedException
     */
    public String getRoutineTaskReportFile(long operationId, String taskKey, long taskId, int taskReportId, int taskReportInfoId, String reportStartDate, String attachedFileName) throws ABVException, InterruptedException {
        GetTaskFileParameters parameters = new GetTaskFileParameters(cache.getMemberInfo().sid, taskId, Constant.ReportType.Report, taskReportInfoId);
        String outputFilePath = ABVEnvironment.getInstance().getRoutineTaskReportDirFilePath(operationId, taskKey, taskReportId, reportStartDate) + "/" + attachedFileName + ".zip";
        AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).getTaskFile(parameters, operationId, outputFilePath);
        return outputFilePath;
    }

    /**
     * シーンの登録
     * @param file
     * @throws IOException
     * @throws AcmsException
     */
    public Integer sendScene(File file) throws IOException, AcmsException, NetworkDisconnectedException {
        SceneEntryJSON json = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).sceneEntry(cache.getMemberInfo().sid, file);
        return json.resourceId;
    }

    /**
     * 360コンテンツの登録
     * @param operationId
     * @param contentName
     * @param file
     * @throws IOException
     * @throws AcmsException
     */
    public void sendPanoContent(long operationId, String contentName, File file) throws IOException, ABVException {
        AcmsClient.getInstance(cache.getUrlPath(), networkAdapter).taskContentEntry(cache.getMemberInfo().sid, operationId, contentName, file);
    }

    public void deleteAllOperation() {
        List<OperationDto> operationDtoList = mOperationDao.getAllOperation();
        for(OperationDto operationDto : operationDtoList) {
            FileUtil.delete(ABVEnvironment.getInstance().getOperationDirFile(operationDto.operationId));
        }
        mOperationDao.deleteAll();
    }

   public void setContentCreatingFlg(long operationId) {
        mOperationDao.updateContentCreatingFlg(operationId, true);
   }

    /**
     * 作業の添付ファイルのzip生成
     * @param operationId
     * @param taskKey
     * @param directionFlg
     * @return
     * @throws ZipException
     * @throws NoSuchAlgorithmException
     * @throws IOException
     */
    private File createAttachedFile(Long operationId, String taskKey, boolean directionFlg, int taskReportLevel) throws ZipException, NoSuchAlgorithmException, IOException {
        File zipFile;
        // #32926 作業報告画面改善 start
        String filePath = ABVEnvironment.getInstance().getOperationTaskReportLevelDirPath(operationId, taskKey, taskReportLevel);
        // #32926 作業報告画面改善 end

        File fileDir = new File(filePath);
        if (!fileDir.exists()) {
            return null;
        }
        String[] files = fileDir.list();
        if (files == null || files.length == 0) {
            return null;
        }
        ArrayList<String> fileList = new ArrayList<String>();
        for (String dataFile : files) {
            fileList.add(fileDir.getPath() + StringUtil.Slash + dataFile);
        }
        String fileName = UUID.randomUUID().toString();
        zipFile = new File(filePath + StringUtil.Slash + fileName + ".zip");
        if (zipFile.exists()) {
            // zipファイルが存在する時は削除して、新たに作成
            boolean result = zipFile.delete();
            if (result) {
                FileUtil.saveZip(zipFile.getPath(), null, fileList.toArray(new String[fileList.size()]));
            } else {
                // 削除失敗
                Logger.e(TAG, "delete zip file failed");
                return null;
            }
        } else {
            FileUtil.saveZip(zipFile.getPath(), null, fileList.toArray(new String[fileList.size()]));
        }
        return zipFile;
    }

    /**
     * 作業報告送信の添付ファイルのzip生成
     * @param operationId
     * @param taskKey
     * @param taskReportSendId
     * @param routineFlag
     * @param reportId
     * @param reportStartDate
     * @return
     * @throws ZipException
     * @throws NoSuchAlgorithmException
     * @throws IOException
     */
    private File createAttachedFileForSend(Long operationId, String taskKey, int taskReportSendId, boolean routineFlag, int reportId, String reportStartDate, int taskReportLevel) throws ZipException, NoSuchAlgorithmException, IOException {
        File zipFile;
        String filePath = ABVEnvironment.getInstance().getOperationTaskReportSendDirFilePath(operationId, taskKey, taskReportSendId);

        File fileDir = new File(filePath);
        if (!fileDir.exists()) {
            return null;
        }
        String[] files = fileDir.list();
        if (files == null || files.length == 0) {
            return null;
        }
        ArrayList<String> fileList = new ArrayList<String>();
        for (String dataFile : files) {
            fileList.add(fileDir.getPath() + StringUtil.Slash + dataFile);
        }
        String fileName = UUID.randomUUID().toString();
        zipFile = new File(filePath + StringUtil.Slash + fileName + ".zip");
        if (zipFile.exists()) {
            // zipファイルが存在する時は削除して、新たに作成
            boolean result = zipFile.delete();
            if (result) {
                FileUtil.saveZip(zipFile.getPath(), null, fileList.toArray(new String[fileList.size()]));
            } else {
                // 削除失敗
                Logger.e(TAG, "delete zip file failed");
                return null;
            }
        } else {
            FileUtil.saveZip(zipFile.getPath(), null, fileList.toArray(new String[fileList.size()]));
        }
        return zipFile;
    }

    /**
     * ディレクトリのファイルがattachedFileNamesに含まれないファイル削除
     * @param dirPath
     * @param attachedFileNames
     */
    private void deleteDifferentialFile (String dirPath, List<String> attachedFileNames) {
        File file = new File(dirPath);
        if (file.exists()) {
            String[] fileList = file.list();
            if (fileList != null) {
                for (String localFileName : fileList) {
                    if (!attachedFileNames.contains(localFileName)) {
                        FileUtil.delete(dirPath + "/" + localFileName);
                    }
                }
            }
        }
    }

    /**
     * 作業の開始日のチェック
     * @param operationId
     * @return
     */
    public String getRoutineTaskOperationAvailableDateStr(Long operationId) {
        OperationDto operationDto = getOperation(operationId);
        Date currDate = DateTimeUtil.getCurrentSqlDate();
        if (DateTimeUtil.isDateBefore(currDate, operationDto.operationStartDate)) {
            return DateTimeUtil.toString(operationDto.operationStartDate, DateTimeFormat.yyyyMMdd_slash);
        }
        return null;
    }

    /**
     * 作業報告の取得
     * @param taskKey
     * @param taskReportlevel
     * @return
     */
    public TaskReportDto getTaskReport(String taskKey, int taskReportlevel) {
        return mTaskReportDao.getTaskReport(taskKey, taskReportlevel);
    }

    /**
     * suggestJsonデータをtaskReportItemテーブルに登録
     * @param taskKey
     * @param reportLevel
     * @param suggestJson
     */
    public void registTaskReportItem(String taskKey, int reportLevel, JSONObject suggestJson) {
        if (suggestJson == null) {
            // suggestJsonがnullの場合、return
            return;
        }
        Iterator itemKeys = suggestJson.keys();
        while (itemKeys.hasNext()) {
            String itemKey = (String) itemKeys.next();
            TaskReportItemsDto taskReportItemsDto = mTaskReportItemsDao.getTaskReportItem(taskKey, reportLevel, itemKey);
            if (taskReportItemsDto != null) {
                // 更新
                String newValue = suggestJson.getString(taskReportItemsDto.itemKey);
                if (!newValue.equals(taskReportItemsDto.inputValue)) {
                    // 値が異なる場合のみ、更新する
                    taskReportItemsDto.inputValue = newValue;
                    mTaskReportItemsDao.updateTaskReportItems(taskReportItemsDto);
                }
            } else {
                // 登録
                taskReportItemsDto = new TaskReportItemsDto();
                taskReportItemsDto.taskKey = taskKey;
                taskReportItemsDto.itemKey = itemKey;
                taskReportItemsDto.taskReportLevel = reportLevel;
                try {
                    taskReportItemsDto.inputValue = suggestJson.getString(itemKey);
                } catch (JSONException e) {
                    // 値がStringではない場合、無視する
                    continue;
                }
                mTaskReportItemsDao.insertTaskReportItems(taskReportItemsDto);
            }
        }
    }

    /**
     * t_taskテーブルに登録（報告データ）
     * データが存在しないと登録する
     * @param taskKey
     * @param operationId
     * @param hotSpotInfo
     */
    public void registTaskData(String taskKey, Long operationId, String hotSpotInfo, JSONObject taskJson) {
        TaskDto taskDto = mTaskDao.getTaskByTaskKey(taskKey);
        String taskCode = null;
        Integer taskStatus = null;
        if (taskJson != null) {

            Iterator taskKeys = taskJson.keys();
            while (taskKeys.hasNext()) {
                String itemKey = (String) taskKeys.next();
                // 作業コード取得
                if (itemKey.startsWith("q_1_")) {
                    taskCode = taskJson.getString(itemKey);
                }
                // 報告状況取得
                if (itemKey.startsWith("q_3_")) {
                    taskStatus = taskJson.getInt(itemKey);
                }
            }
        }

        if (taskDto != null) {
            // 更新
            if (taskDto.taskCode != null) {
                taskDto.taskCode = taskCode;
            }

            if (hotSpotInfo != null) {
                taskDto.taskHotSpotInfo = hotSpotInfo;
            }
            if (taskStatus != null) {
                taskDto.taskStatus = taskStatus;
            }
            mTaskDao.update(taskDto);
        } else {
            // 登録
            taskDto = new TaskDto();
            taskDto.taskCode = taskCode;
            taskDto.taskKey = taskKey;
            taskDto.operationId = operationId;
            if (hotSpotInfo != null) {
                taskDto.taskHotSpotInfo = hotSpotInfo;
            }
            if (taskStatus != null) {
                taskDto.taskStatus = taskStatus;
            }
            mTaskDao.insert(taskDto);
        }
    }

    /**
     * サービスオプション「営業日変更時間」をチェックして、情報更新可否を判断
     * @param syncedDate 最終同期日付
     * @return true : 情報更新可能、false：情報更新不可
     */
    public boolean checkServiceOptionOperaionChangeTime(Date syncedDate) {
        boolean result = false;
        String operationChangeTime = ABVDataCache.getInstance().serviceOption.operationChangeTime();
        String[] timeArray = operationChangeTime.split(":", 0);

        if (timeArray.length != 2) { //データ形式が異常
            return false;
        }
        // 営業日変更時間
        int businessHour = Integer.valueOf(timeArray[0]); //時間
        int businessMinute = Integer.valueOf(timeArray[1]); //分

        Calendar syncCalender = Calendar.getInstance();
        syncCalender.setTime(syncedDate);

        // 現在日付
        Date nowDate = Calendar.getInstance().getTime();

        // 同期処理を行った時間から現在の時間まで１時間単位でループ処理
        while (syncCalender.before(nowDate)) {
            // 1時間毎追加
            syncCalender.add(Calendar.HOUR_OF_DAY, 1);

            // 変更された時間取得
            int targetHour = syncCalender.get(Calendar.HOUR_OF_DAY);
            // 1時間追加された時間と営業時間と比較
            if (targetHour == businessHour) {
                result = true;
                break;
            }
        }
        return result;
    }

    private Boolean isEnableTimeForAutoSync(Date lastEditDateTime) {
        Date currentDateTime = DateTimeUtil.getCurrentDate();
        if (lastEditDateTime == null) {
            return true;
        } else {
            long diffInMillis = currentDateTime.getTime() - lastEditDateTime.getTime();
            if (diffInMillis >= INTERVAL_LAST_EDIT_TIME_MS) {
                return true;
            }
        }
        return false;
    }

    public List<OperationDto> needSyncCheckArray(List<OperationDto> needSyncOperationInfoArray) {
        List<OperationDto> needSyncCheckArray = new ArrayList<>();
        for (OperationDto operationDto : needSyncOperationInfoArray) {
            if (isEnableTimeForAutoSync(operationDto.operationLastSyncDate)) {
                needSyncCheckArray.add(operationDto);
            }
        }
        return needSyncCheckArray;
    }

    /**
     * 重複作業情報を配列から除外
     * @param operationDtoList
     * @return
     */
    public List<OperationDto> deduplicateOperationDto(List<OperationDto> operationDtoList) {
        Map<String, OperationDto> map = new HashMap<>();
        for (OperationDto operationDto : operationDtoList) {
            // IDと名前だけをキーにする
            String key = operationDto.operationId +"_" + operationDto.operationName;
            // 上記のキーで要素を格納
            map.put(key, operationDto);
        }
        return new ArrayList<>(map.values());
    }
}
