/*
 * 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.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.cayenne.access.Transaction;
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.cayenne.om.portlet.EipTMailFolder;
import com.aimluck.eip.cayenne.om.security.TurbineUser;
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.orm.query.SQLTemplate;
import com.aimluck.eip.util.ALEipUtils;
import com.aimluck.eip.webmail.util.WebMailUtils;

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

  /** logger */
  private static final JetspeedLogger logger =
    JetspeedLogFactoryService
      .getLogger(WebMailPackageProcessFolderMoveThread.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_COMPLETE_MAIL = "メール一括移動処理完了メール送信に失敗しました";

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

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

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

  /** システム日付 */
  private final Date now;

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

  @Override
  public void run() {

    // add by motegi start 2012.1.12
    DataContext.bindThreadDataContext(dataContext);

    // 自己管理トランザクション
    Transaction tx =
      Transaction.internalTransaction(DataContext
        .getThreadDataContext()
        .getParentDataDomain()
        .getTransactionDelegate());

    // 標準のトランザクションを自己管理トランザクションに置き換えます。
    Transaction.bindThreadTransaction(tx);

    // add end

    // change start カーソル処理へ変更
    ResultIterator it = null;
    // キャンセルフラグ
    boolean cancel = false;
    // change end カーソル処理へ変更

    try {
      // トランザクションモードを切替するため、一旦コミット
      // Database.commit();
      // Database
      // .sql(
      // TurbineUser.class,
      // "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ
      // COMMITTED;")
      // .execute();
      Database.sql(
        TurbineUser.class,
        "SET TRANSACTION ISOLATION LEVEL READ COMMITTED;").execute();
      logger.info("トランザクションモードを切替しました。");

      // 処理パラメータオブジェクト
      AvzTMailBatch mailBatch = null;

      // 非同期にて処理を行う場合はdataContextオブジェクトを再生成？
      // DataContext.bindThreadDataContext(dataContext);

      try {
        // 処理パラメータ取得し、アカウントID、フォルダID、フォルダ種別を取得する。
        mailBatch = WebMailUtils.getAvzTMailBatchResultList(user_id);
      } catch (Exception ex) {
        logger.error(ERROR_MESSAGE_GET_STATUS_PARAMETER + login_user_name, ex);
        return;
      }

      // 対象アカウントID
      account_id = mailBatch.getAccountId();

      // 対象フォルダID
      int from_folder_id = mailBatch.getFolderId();
      // 移動先フォルダID
      int to_folder_id = mailBatch.getToFolderId();

      // 対象フォルダ取得
      EipTMailFolder from_folder =
        WebMailUtils.getEipTMailFolder(account_id, from_folder_id);
      // 移動先フォルダ取得
      EipTMailFolder to_folder =
        WebMailUtils.getEipTMailFolder(account_id, to_folder_id);

      // フォルダ種別
      String folder_kind = to_folder.getFolderKind();

      // 対象フォルダ容量取得
      long from_folder_volume =
        WebMailUtils.getFolderVolume(folder_kind, from_folder_id);
      // 移動先フォルダ容量取得
      long to_folder_volume =
        WebMailUtils.getFolderVolume(folder_kind, to_folder_id);

      long mail_volume = 0;
      // 対象となるメールのフォルダを更新する（フォルダ種別で参照テーブルが異なる）
      if (WebMailConsts.RECEIVE_FOLDER.equals(folder_kind)) {
        // フォルダ種別 = R:受信トレイの場合
        // List<EipTMail> eipTMailList = new ArrayList<EipTMail>();
        // eipTMailList =
        // WebMailUtils.getEipTMail(
        // account_id,
        // user_id,
        // from_folder_id,
        // mailBatch,
        // false);
        it =
          WebMailUtils.getEipTMailResultIterator(
            account_id,
            user_id,
            from_folder_id,
            mailBatch,
            false);
        // if (null == eipTMailList || eipTMailList.size() < 1) {
        if (null == it) {
        } else {
          // 対象となる受信メールのフォルダを更新する。
          // for (int i = 0; i < eipTMailList.size(); i++) {
          int i = 0;
          while (it.hasNextRow()) {

            // EipTMail eipTMail = eipTMailList.get(i);
            // change start 2012.2.9 受入障害対応No.211
            // eipTMail.setFolderId(to_folder_id);
            // eipTMail.setUpdateDate(now);
            // updateEipTMail(eipTMail, to_folder_id, now);
            Map<String, Object> row = it.nextDataRow();
            int mail_id = (Integer) row.get(EipTMail.MAIL_ID_PK_COLUMN);
            updateEipTMail(mail_id, to_folder_id, now);
            // change end 2012.2.9
            // メール容量加算
            // mail_volume += eipTMail.getFileVolume().intValue() * 1024;
            int volume = (Integer) row.get(EipTMail.FILE_VOLUME_COLUMN);
            mail_volume += volume * 1024;

            int status_check_count = i % 500;
            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)) {
                // finally節で後処理を行わせるためのフラグをON
                logger.info(login_user_name + " 一括移動処理がキャンセルされました");
                cancel = true;
                return;
              }

              dataContext.commitChanges();
            }
            i++;
          }
          dataContext.commitChanges();
        }
      } else if (WebMailConsts.SEND_FOLDER.equals(folder_kind)) {
        // フォルダ種別 = S:送信トレイの場合
        // List<AvzTMailSend> avzTMailSend = new ArrayList<AvzTMailSend>();
        // avzTMailSend =
        // WebMailUtils.getAvzTMailSend(
        // account_id,
        // user_id,
        // from_folder_id,
        // mailBatch,
        // false);
        it =
          WebMailUtils.getAvzTMailSendResultIterator(
            account_id,
            user_id,
            from_folder_id,
            mailBatch,
            false);
        // if (null == avzTMailSend || avzTMailSend.size() < 1) {
        if (null == it) {
        } else {
          // 対象となる送信メールのフォルダを更新する。
          // for (int i = 0; i < avzTMailSend.size(); i++) {
          int i = 0;
          while (it.hasNextRow()) {
            // AvzTMailSend avzTMail = avzTMailSend.get(i);
            // change start 2012.2.9 受入障害対応No.211
            // avzTMail.setFolderId(to_folder_id);
            // avzTMail.setUpdateDate(now);
            // updateAvzTMailSend(avzTMail, to_folder_id, now);
            Map<String, Object> row = it.nextDataRow();
            int mail_id = (Integer) row.get(AvzTMailSend.MAIL_ID_PK_COLUMN);
            updateAvzTMailSend(mail_id, to_folder_id, now);
            // change end 2012.2.9
            // メール容量加算
            // mail_volume += avzTMail.getFileVolume().intValue() * 1024;
            int volume = (Integer) row.get(EipTMail.FILE_VOLUME_COLUMN);
            mail_volume += volume * 1024;

            int status_check_count = i % 500;
            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)) {
                // finally節で後処理を行わせるためのフラグをON
                logger.info(login_user_name + " 一括移動処理がキャンセルされました");
                cancel = true;
                return;
              }

              dataContext.commitChanges();
            }
            i++;
          }
          dataContext.commitChanges();
        }
      }

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

      // 対象フォルダ容量更新
      if (!WebMailUtils.isRootFolder(from_folder.getFolderId())) {
        from_folder.setFolderVolume(from_folder_volume - mail_volume);
        from_folder.setUpdateDate(now);

        // 移動先フォルダ容量更新
        to_folder.setFolderVolume(to_folder_volume + mail_volume);
        to_folder.setUpdateDate(now);

      } else {
        // 処理元のフォルダがルートフォルダ（全フォルダ対象）の場合
        List<EipTMailFolder> listMyFolders =
          WebMailUtils.getEipTMailFolderAll(account_id, folder_kind);
        for (EipTMailFolder e : listMyFolders) {
          long newVolume =
            WebMailUtils.getFolderVolume(folder_kind, e.getFolderId());
          e.setFolderVolume(newVolume);
        }
      }

      // 処理ステータスを「一括処理完了」に更新する。
      WebMailUtils.updateMailBatchData(
        mailBatch,
        WebMailConsts.BATCH_PROCESSING_COMPLETE);

      // コミットする。
      Database.commit();

      // 処理完了メールを送信する。
      sendBatchCompleteMail(account_id);

    } catch (Exception ex) {

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

      Database.rollback();
      logger
        .error(ERROR_MESSAGE_UPDATE_MAIL_BATCH_STATUS + login_user_name, ex);
    } finally {
      try {
        // add start カーソル処理へ変更
        if (cancel) {
          if (it != null) {
            try {
              it.close();
              it = null;
            } catch (CayenneException e) {
              logger.error("カーソルの解放で予期せぬ例外が発生しました。", e);
            }
          }
          Database.rollback();
        }
        // end start カーソル処理へ変更
        Database.tearDown();
      } catch (Exception e) {
        logger.error("DB切断で予期せぬ例外が発生しました。", e);
      }
    }
  }

  /**
   * 完了メールを送信します
   * 
   * @see com.aimluck.eip.mail.ALMailService.sendAdminMail
   * @param int
   *            account_id アカウントID
   */
  public void sendBatchCompleteMail(int account_id) {
    String subject = "メール一括フォルダ移動完了のお知らせ";
    String orgId = Database.getDomainName();
    List<ALAdminMailMessage> messageList = new ArrayList<ALAdminMailMessage>();
    ALAdminMailMessage message = new ALAdminMailMessage();
    try {
      message.setPcSubject(subject);
      message.setPcBody(createMsgForPc());
      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(ERROR_MESSAGE_COMPLETE_MAIL + login_user_name, ex);
      return;
    }
    return;
  }

  /**
   * メール本文作成
   * 
   * @return String メール本文
   */
  public static String createMsgForPc() {
    String CR = System.getProperty("line.separator");
    StringBuffer body = new StringBuffer("");
    body.append("メールの一括フォルダ移動が完了しました。").append(CR).append(CR);
    return body.toString();
  }

  // add start 2012.2.9 受入障害対応No.211
  // private static void updateEipTMail(EipTMail eipTMail, int to_folder_id,
  // Date now) {
  // final String updateSql =
  // "UPDATE "
  // + " EIP_T_MAIL "
  // + " SET FOLDER_ID = #bind($folder_id 'INTEGER'), "
  // + " UPDATE_DATE = #bind($update_date 'DATE') "
  // + " WHERE MAIL_ID = #bind($mail_id 'INTEGER') ";
  //
  // SQLTemplate<EipTMail> query =
  // new SQLTemplate<EipTMail>(EipTMail.class, updateSql);
  // query.param("folder_id", to_folder_id).param("update_date", now).param(
  // "mail_id",
  // eipTMail.getMailId()).execute();
  // }
  private static void updateEipTMail(int mail_id, int to_folder_id, Date now) {
    final String updateSql =
      "UPDATE "
        + " EIP_T_MAIL "
        + " SET FOLDER_ID = #bind($folder_id 'INTEGER'), "
        + " UPDATE_DATE = #bind($update_date 'DATE') "
        + " WHERE MAIL_ID = #bind($mail_id 'INTEGER') ";

    SQLTemplate<EipTMail> query =
      new SQLTemplate<EipTMail>(EipTMail.class, updateSql);
    query.param("folder_id", to_folder_id).param("update_date", now).param(
      "mail_id",
      mail_id).execute();
  }

  // private static void updateAvzTMailSend(AvzTMailSend avzTMailSend,
  // int to_folder_id, Date now) {
  // final String updateSql =
  // "UPDATE "
  // + " Avz_T_Mail_Send "
  // + " SET FOLDER_ID = #bind($folder_id 'INTEGER'), "
  // + " UPDATE_DATE = #bind($update_date 'DATE') "
  // + " WHERE MAIL_ID = #bind($mail_id 'INTEGER') ";
  //
  // SQLTemplate<AvzTMailSend> query =
  // new SQLTemplate<AvzTMailSend>(AvzTMailSend.class, updateSql);
  // query.param("folder_id", to_folder_id).param("update_date", now).param(
  // "mail_id",
  // avzTMailSend.getMailId()).execute();
  // }
  private static void updateAvzTMailSend(int mail_id, int to_folder_id, Date now) {
    final String updateSql =
      "UPDATE "
        + " Avz_T_Mail_Send "
        + " SET FOLDER_ID = #bind($folder_id 'INTEGER'), "
        + " UPDATE_DATE = #bind($update_date 'DATE') "
        + " WHERE MAIL_ID = #bind($mail_id 'INTEGER') ";

    SQLTemplate<AvzTMailSend> query =
      new SQLTemplate<AvzTMailSend>(AvzTMailSend.class, updateSql);
    query.param("folder_id", to_folder_id).param("update_date", now).param(
      "mail_id",
      mail_id).execute();
  }
  // add end
}
