/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * http://aipostyle.com/
 *
 * Copyright(C) 2011 avanza Co.,Ltd. All rights reserved.
 * http://www.avnz.co.jp/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package com.aimluck.eip.webmail;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.cayenne.CayenneException;
import org.apache.cayenne.access.DataContext;
import org.apache.cayenne.access.ResultIterator;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;

import com.aimluck.eip.cayenne.om.portlet.AvzTMailBatch;
import com.aimluck.eip.cayenne.om.portlet.AvzTMailSend;
import com.aimluck.eip.cayenne.om.portlet.EipTMail;
import com.aimluck.eip.mail.ALAdminMailContext;
import com.aimluck.eip.mail.ALAdminMailMessage;
import com.aimluck.eip.mail.ALMailService;
import com.aimluck.eip.mail.util.ALMailUtils;
import com.aimluck.eip.orm.Database;
import com.aimluck.eip.util.ALEipUtils;
import com.aimluck.eip.webmail.util.FileZip;
import com.aimluck.eip.webmail.util.WebMailUtils;

/**
 * 非同期にてメール一括退避処理を行うクラスです
 * 
 * @see java.lang.Runnable
 */
public class WebMailPackageProcessFolderSaveThread implements Runnable {

  /** logger */
  private static final JetspeedLogger logger =
    JetspeedLogFactoryService
      .getLogger(WebMailPackageProcessFolderSaveThread.class.getName());

  /** ログインユーザー名 * */
  private final String login_user_name;

  /** ログインユーザーID * */
  private final int user_id;

  /** ログインユーザーのアカウントID * */
  private int account_id;

  /** ログ出力用メッセージ(一括退避処理パラメータ取得) * */
  private final String ERROR_MESSAGE_GET_STATUS_PARAMETER =
    "メール一括退避処理パラメータ取得に失敗しました";

  /** ログ出力用メッセージ(退避処理対象メール取得) * */
  private final String ERROR_MESSAGE_GET_TARGET_MAIL = "一括退避処理対象メール取得に失敗しました";

  /** ログ出力用メッセージ(メール一括退避用ファイル作成) * */
  private final String ERROR_MESSAGE_CREATE_SAVE_FOLDER =
    "メール一括退避用ファイル作成に失敗しました";

  /** ログ出力用メッセージ(一括退避処理パラメータ更新) * */
  private final String ERROR_MESSAGE_UPDATE_MAIL_BATCH_STATUS =
    "メール一括退避処理パラメータ更新に失敗しました";

  /** データベース ID */
  private DataContext dataContext = null;

  /** ファイル拡張子名 */
  private final String ZIP_EXTENSION = ".zip";

  /** 圧縮ファイル名 */
  private String zip_file_name = null;

  /**
   * コンストラクタ
   * 
   * @param orgId
   * @param userId
   * @param mailAccountId
   */
  public WebMailPackageProcessFolderSaveThread(DataContext dataContext,
      int user_id) {
    this.dataContext = dataContext;
    this.user_id = user_id;
    this.login_user_name = ALEipUtils.getUserFullName(user_id);

  }

  @Override
  public void run() {
    // 処理パラメータオブジェクト
    AvzTMailBatch mailBatch = null;
    // 処理パラメータから取得するフォルダ種別
    String folder_kind = null;
    // 処理パラメータから取得するフォルダID
    int folder_id = -1;
    // 非同期にて処理を行う場合はdataContextオブジェクトを再生成？
    DataContext.bindThreadDataContext(dataContext);
    // 最終的に圧縮するzipファイル
    File zip_file = null;
    // 処理結果フラグ
    boolean success = true;
    // キャンセルフラグ
    boolean cancel = false;
    // add start
    // 内部レビュー反映No.7
    // 一括処理キャンセル確認カウント用変数
    int status_check_count = 0;
    // add end
    // 処理パラメータ取得し、アカウントID、フォルダID、フォルダ種別を取得する。
    Runtime runtime0 = Runtime.getRuntime();
    long max0 = runtime0.maxMemory();
    long free0 = runtime0.freeMemory();
    long used0 = max0 - free0;

    logger.info("メール退避処理モニター 処理開始直前状態["
      + login_user_name
      + "　メモリMAX:"
      + max0
      / 1024
      / 1024
      + "M メモリFREE:"
      + free0
      / 1024
      / 1024
      + "M メモリUSED:"
      + used0
      / 1024
      / 1024);

    try {

      mailBatch = WebMailUtils.getAvzTMailBatchResultList(user_id);
      account_id = mailBatch.getAccountId();
      folder_id = mailBatch.getFolderId();
      folder_kind =
        WebMailUtils.getEipTMailFolder(account_id, folder_id).getFolderKind();
    } catch (Exception ex) {
      logger.error(ERROR_MESSAGE_GET_STATUS_PARAMETER + login_user_name, ex);
      return;
    }

    // change start カーソル処理へ変更
    ResultIterator it = null;
    FileOutputStream fos = null;
    OutputStreamWriter osw = null;
    PrintWriter pw = null;
    // change end カーソル処理へ変更

    // 退避メール出力先ディレクトリ(退避用フォルダ生成)
    // ダウンロード先ディレクトリ取得
    try {
      // final String tmpFilePath =
      // JetspeedResources.getString("aipo.ziptmp.directory", "");
      // String downloadDirectoryPath =
      // tmpFilePath + File.separator + "download" + File.separator;
      String downloadDirectoryPath = WebMailUtils.getDownloadFileDir(user_id);

      File download_dir =
        WebMailUtils.makeFileDownloadDirectory(
          account_id,
          folder_id,
          downloadDirectoryPath);

      // add start カーソル処理へ変更
      fos =
        new FileOutputStream(new File(downloadDirectoryPath
          + File.separator
          + "file.list"));
      osw = new OutputStreamWriter(fos, "UTF-8");
      pw = new PrintWriter(osw);
      // end start カーソル処理へ変更

      // try {
      // 退避対象となるメールのレコードを取得する。（フォルダ種別で参照テーブルが異なる）
      if (WebMailConsts.RECEIVE_FOLDER.equals(folder_kind)) {
        // フォルダ種別 = R:受信トレイの場合
        // List<EipTMail> eipTMailList = new ArrayList<EipTMail>();

        try {
          // change start カーソル処理へ変更
          // eipTMailList =
          // WebMailUtils.getEipTMail(
          // account_id,
          // user_id,
          // folder_id,
          // mailBatch,
          // false);
          it =
            WebMailUtils.getEipTMailResultIterator(
              account_id,
              user_id,
              folder_id,
              mailBatch,
              false);
          // change end
        } catch (Exception ex) {
          logger.error(ERROR_MESSAGE_GET_TARGET_MAIL + login_user_name, ex);
          return;
        }

        // change start カーソル処理へ変更
        // if (null == eipTMailList || eipTMailList.size() < 1) {
        if (null == it) {
          // change end
          // <結果レコード>が0件の場合、処理ステータスを「一括処理キャンセル」に更新
          WebMailUtils.updateMailBatchData(
            mailBatch,
            WebMailConsts.BATCH_PROCESSING_CANCEL);
        } else {
          // <結果レコード>の件数分対象となるメールファイルを、ダウンロード用にコピーする。

          // change start カーソル処理へ変更
          // int num = eipTMailList.size();
          // for (int i = 0; i < num; i++) {
          int i = 0;
          long memoryMax = 0;
          while (it.hasNextRow()) {
            // change end
            try {
              // add start
              // 内部レビュー反映No.7
              status_check_count = i % 500;
              // add end
              // パフォーマンス見直しによるソース修正
              // if文の中に下記2行を移動
              // String status =
              // WebMailUtils.getAvzTMailBatchResultList(user_id).getStatus();
              // change start
              // 一括処理キャンセル指示を見るためのＤＢアクセスは、
              // ５００レコードに１回ぐらいに変更（ＤＢ負荷を考慮）。
              if (status_check_count == 0) {

                Runtime runtime = Runtime.getRuntime();
                long max = runtime.maxMemory();
                long free = runtime.freeMemory();
                long used = max - free;

                if (memoryMax < used) {
                  memoryMax = used;
                }

                logger.info("メール退避処理モニター["
                  + login_user_name
                  + " ここまでの最大メモリ使用量:"
                  + memoryMax
                  / 1024
                  / 1024
                  + "　初期使用メモリ:"
                  + used0
                  / 1024
                  / 1024
                  + "　メモリMAX:"
                  + max
                  / 1024
                  / 1024
                  + "M メモリFREE:"
                  + free
                  / 1024
                  / 1024
                  + "M メモリUSED:"
                  + used
                  / 1024
                  / 1024
                  + "M 現在処理件数:"
                  + i
                  // + "件 全処理件数:"
                  // + num
                  + "件");

                String status =
                  WebMailUtils
                    .getAvzTMailBatchResultListOnOtherTran(user_id)
                    .getStatus();
                logger.debug(login_user_name + " 一括処理状態：" + status);
                if (WebMailConsts.BATCH_PROCESSING_CANCEL.equals(status)) {
                  logger.info(login_user_name + " 一括退避処理がキャンセルされました");
                  // finally節で後処理を行わせるためのフラグをON
                  cancel = true;
                  // 一括処理キャンセル指示がされている場合退避先フォルダを削除し処理を終了する。
                  deleteFolder(download_dir);
                  return;
                }
              }
              // if (WebMailConsts.BATCH_PROCESSING_CANCEL.equals(status)) {
              // // 一括処理キャンセル指示がされている場合退避先フォルダを削除し処理を終了する。
              // deleteFolder(download_dir);
              // return;
              // } else {
              Map<String, Object> row = it.nextDataRow();

              // change start カーソル処理へ変更
              // WebMailUtils.copyMailFileToDownloadDirectory(
              // user_id,
              // account_id,
              // folder_kind,
              // eipTMailList.get(i).getFilePath(),
              // eipTMailList.get(i).getSubject(),
              // eipTMailList.get(i).getEventDate(),
              // download_dir,
              // i);
              File outFile =
                WebMailUtils.copyMailFileToDownloadDirectory(
                  user_id,
                  account_id,
                  folder_kind,
                  (String) row.get(EipTMail.FILE_PATH_COLUMN),
                  (String) row.get(EipTMail.SUBJECT_COLUMN),
                  (Date) row.get(EipTMail.EVENT_DATE_COLUMN),
                  download_dir,
                  i);
              i++;

              if (outFile != null) {
                pw.println(outFile.getName());
              }
              // change end カーソル処理へ変更

              // remove start カーソル処理へ変更
              // eipTMailList.set(i, null); // GC対象にする
              // remove end カーソル処理へ変更

              // }
              // change end
            } catch (OutOfMemoryError e) {

              logger.error("メール退避処理モニター["
                + login_user_name
                + "] "
                + i
                + "回目の処理でOutOfMemoryErrorが発生しました。", e);
              throw new Exception();
            }
          }
        }
      } else if (WebMailConsts.SEND_FOLDER.equals(folder_kind)) {
        // フォルダ種別 = S:送信トレイの場合
        // List<AvzTMailSend> avzTMailSend = new ArrayList<AvzTMailSend>();
        try {
          // avzTMailSend =
          // WebMailUtils.getAvzTMailSend(
          // account_id,
          // user_id,
          // folder_id,
          // mailBatch,
          // false);
          it =
            WebMailUtils.getAvzTMailSendResultIterator(
              account_id,
              user_id,
              folder_id,
              mailBatch,
              false);
        } catch (Exception ex) {
          logger.error(ERROR_MESSAGE_GET_TARGET_MAIL + login_user_name, ex);
          return;
        }
        // if (null == avzTMailSend || avzTMailSend.size() < 1) {
        if (null == it) {
          // <結果レコード>が0件の場合、処理ステータスを「一括処理キャンセル」に更新
          WebMailUtils.updateMailBatchData(
            mailBatch,
            WebMailConsts.BATCH_PROCESSING_CANCEL);
        } else {
          // <結果レコード>の件数分対象となるメールファイルを、ダウンロード用にコピーする。

          // change start カーソル処理へ変更
          // int num = avzTMailSend.size();
          // for (int i = 0; i < avzTMailSend.size(); i++) {
          int i = 0;
          while (it.hasNextRow()) {
            // change end
            try {
              // add start
              // 内部レビュー反映No.7
              status_check_count = i % 500;
              // add end
              // パフォーマンス見直しによるソース修正
              // if文の中に下記2行を移動
              // String status =
              // WebMailUtils.getAvzTMailBatchResultList(user_id).getStatus();
              // change start
              // 一括処理キャンセル指示を見るためのＤＢアクセスは、
              // ５００レコードに１回ぐらいに変更（ＤＢ負荷を考慮）。
              if (status_check_count == 0) {

                Runtime runtime = Runtime.getRuntime();
                long max = runtime.maxMemory();
                long free = runtime.freeMemory();
                long used = max - free;
                logger.info("メール退避処理モニター["
                  + login_user_name
                  + "]　メモリMAX:"
                  + max
                  / 1024
                  / 1024
                  + "M メモリFREE:"
                  + free
                  / 1024
                  / 1024
                  + "M メモリUSED:"
                  + used
                  / 1024
                  / 1024
                  + "M 現在処理件数:"
                  + i
                  + "件 ");

                String status =
                  WebMailUtils
                    .getAvzTMailBatchResultListOnOtherTran(user_id)
                    .getStatus();
                logger.debug(login_user_name + " 一括処理状態：" + status);
                if (WebMailConsts.BATCH_PROCESSING_CANCEL.equals(status)) {
                  logger.info(login_user_name + " 一括退避処理がキャンセルされました");
                  // finally節で後処理を行わせるためのフラグをON
                  cancel = true;
                  // 一括処理キャンセル指示がされている場合退避先フォルダを削除し処理を終了する。
                  deleteFolder(download_dir);
                  return;
                }
              }
              // if (WebMailConsts.BATCH_PROCESSING_CANCEL.equals(status)) {
              // // 一括処理キャンセル指示がされている場合退避先フォルダを削除し処理を終了する。
              // deleteFolder(download_dir);
              // return;
              // } else {

              Map row = it.nextDataRow();

              // change start カーソル処理へ変更
              // WebMailUtils.copyMailFileToDownloadDirectory(
              // user_id,
              // account_id,
              // folder_kind,
              // avzTMailSend.get(i).getFilePath(),
              // avzTMailSend.get(i).getSubject(),
              // avzTMailSend.get(i).getEventDate(),
              // download_dir,
              // i);
              File outFile =
                WebMailUtils.copyMailFileToDownloadDirectory(
                  user_id,
                  account_id,
                  folder_kind,
                  (String) row.get(AvzTMailSend.FILE_PATH_COLUMN),
                  (String) row.get(AvzTMailSend.SUBJECT_COLUMN),
                  (Date) row.get(AvzTMailSend.EVENT_DATE_COLUMN),
                  download_dir,
                  i);
              i++;

              if (outFile != null) {
                pw.println(outFile.getName());
              }
              // change end

              // remove start カーソル処理へ変更
              // avzTMailSend.set(i, null); // GC対象にする
              // remove end

              // }
            } catch (OutOfMemoryError e) {

              logger.error("メール退避処理モニター["
                + login_user_name
                + "] "
                + i
                + "回目の処理でOutOfMemoryErrorが発生しました。", e);
              throw new Exception();
            }
          }
        }
      }

      // add start カーソル処理へ変更
      if (pw != null) {
        // 運用課題No.43
        // 対象ファイル一覧を書き込み
        pw.flush();
        pw.close();
        pw = null;
      }
      // add end カーソル処理へ変更

      // 出力したディレクトリをZIP形式で圧縮する。
      zip_file =
        FileZip.zipDir(download_dir + ZIP_EXTENSION, download_dir
          .getAbsolutePath());
      zip_file_name = zip_file.getName();
      // add start
      // ZIP形式で圧縮後、退避先フォルダを削除する。
      deleteFolder(download_dir);
      // add end
    } catch (IOException ioex) {
      Database.rollback();
      logger.error(ERROR_MESSAGE_CREATE_SAVE_FOLDER + login_user_name, ioex);
      success = false;
      return;
    } catch (Exception ex) {
      Database.rollback();
      logger.error(ERROR_MESSAGE_CREATE_SAVE_FOLDER + login_user_name, ex);
      success = false;
      return;
    } finally {
      if (!success) {

        // このルートは例外（IOException、Exception）が発生した場合のみ実行されます。
        // ⇒各catch節でのreturn前に実行されます。

        if (it != null) {
          try {
            it.close();
            it = null;
          } catch (CayenneException e) {
            logger.error("カーソルの解放で予期せぬ例外が発生しました。", e);
          }
        }

        mailBatch = WebMailUtils.getAvzTMailBatchResultList(user_id);
        WebMailUtils.updateMailBatchData(
          mailBatch,
          WebMailConsts.BATCH_PROCESSING_CANCEL);

        Database.commit();

        // 失敗通知メールを送信します
        sendBatchResultMail(account_id, success);

        try {
          Database.tearDown();
        } catch (Exception e) {
          logger.error("DB切断で予期せぬ例外が発生しました。", e);
        }
      }

      if (cancel) {
        if (it != null) {
          try {
            it.close();
            it = null;
          } catch (CayenneException e) {
            e.printStackTrace();
          }
        }
        try {
          Database.tearDown();
        } catch (Exception e) {
          logger.error("DB切断で予期せぬ例外が発生しました。", e);
        }
      }

      // add start カーソル処理へ変更
      if (pw != null) {
        pw.close();
        pw = null;
      }
      // add end カーソル処理へ変更
    }
    try {
      if (it != null) {
        try {
          it.close();
          it = null;
        } catch (CayenneException e) {
          logger.error("カーソルの解放で予期せぬ例外が発生しました。", e);
        }
      }

      // 処理ステータスを「一括処理完了」に更新。
      updateCompleteMailBatchData(mailBatch, zip_file_name);
      // レコードを更新
      Database.commit();

      // 完了メールを送信します
      sendBatchResultMail(account_id, success);
    } catch (Exception ex) {
      Database.rollback();
      logger
        .error(ERROR_MESSAGE_UPDATE_MAIL_BATCH_STATUS + login_user_name, ex);
      return;
    } finally {
      try {
        Database.tearDown();
      } catch (Exception e) {
        logger.error("DB切断で予期せぬ例外が発生しました。", e);
      }
    }
  }

  /**
   * 処理ステータスを「一括処理完了」に更新します(ファイル名も更新)
   * 
   * @param mailBatch
   *          メール処理オブジェクト
   * @param zip_file_name
   *          圧縮ファイル名
   * @return boolean 成功:true 失敗:false
   */
  // change start
  // 内部レビュー反映no.7
  // public boolean updateCompleteMailBatchData(AvzTMailBatch mailBatch,
  // String zip_file_name) {
  private boolean updateCompleteMailBatchData(AvzTMailBatch mailBatch,
      String zip_file_name) {
    // change end
    Date now = Calendar.getInstance().getTime();
    // 処理ステータス
    mailBatch.setStatus(WebMailConsts.BATCH_PROCESSING_COMPLETE);
    // 圧縮されたZIPファイル名
    mailBatch.setFileName(zip_file_name);
    // 更新日は処理開始日時の格納用に使用する
    // mailBatch.setUpdateDate(now);
    return true;
  }

  /**
   * 完了メールを送信します
   * 
   * @see com.aimluck.eip.mail.ALMailService.sendAdminMail
   * @param int account_id アカウントID
   */
  // change start
  // 内部レビュー反映no.7
  // public void sendBatchCompleteMail(int account_id) {
  private void sendBatchResultMail(int account_id, boolean success) {
    // change end
    String subject = "メール一括退避ファイル作成完了のお知らせ";
    if (!success) {
      subject = "メール一括退避ファイル作成失敗のお知らせ";
    }
    String orgId = Database.getDomainName();
    List<ALAdminMailMessage> messageList = new ArrayList<ALAdminMailMessage>();
    ALAdminMailMessage message = new ALAdminMailMessage();
    try {
      message.setPcSubject(subject);
      message.setPcBody(createMsgForPc(success));
      message.setUserId(user_id);
      message.setPcMailAddr(WebMailUtils.getEipMMailAddress(account_id));
      messageList.add(message);
      ALMailService.sendAdminMail(new ALAdminMailContext(
        orgId,
        user_id,
        messageList,
        ALMailUtils.getSendDestType(ALMailUtils.VALUE_MSGTYPE_DEST_PC)));
    } catch (Exception ex) {
      logger.error("メール一括退避処理完了メール送信に失敗しました", ex);
      return;
    }
    return;
  }

  /**
   * メール本文作成
   * 
   * @return String メール本文
   */
  // change start
  // 内部レビュー反映no.7
  // public static String createMsgForPc() {
  private static String createMsgForPc(boolean success) {
    // change end
    String CR = System.getProperty("line.separator");
    StringBuffer body = new StringBuffer("");
    if (success) {
      body.append("メールの一括ダウンロードの準備ができました。").append(CR).append(CR);
      body
        .append("Webメール上部の「メール退避・削除・移動」をクリックし、ダウンロードを行ってください。")
        .append(CR)
        .append(CR);
    } else {
      body.append("メールの一括ダウンロードの準備中にエラーが発生しました。").append(CR).append(CR);
      body.append(CR).append(CR);
    }
    return body.toString();
  }

  /**
   * フォルダ削除
   * 
   * @param File
   *          削除フォルダ
   */
  static private void deleteFolder(File f) {
    WebMailUtils.deleteFolder(f);
  }

}
