/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * Copyright (C) 2004-2011 Aimluck,Inc.
 * http://www.aipo.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.aimluck.eip.mail;

import java.util.Calendar;

import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.access.DataContext;
import org.apache.jetspeed.om.security.JetspeedUser;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.services.resources.JetspeedResources;
import org.apache.turbine.om.security.User;

import com.aimluck.eip.cayenne.om.portlet.EipMMailAccount;
import com.aimluck.eip.mail.util.ALMailUtils;
import com.aimluck.eip.mail.util.ALNewMailCounterStaticObject;
import com.aimluck.eip.mail.util.ALStaticObject;
import com.aimluck.eip.orm.Database;

/**
 * メール受信（POP3）を操作するクラスです。 <br />
 * 
 */
public class ALPop3MailReceiveThread implements Runnable {

  /** スレッド生成時の排他制御用フラグ */
  public static final Integer KEY_SYNCHRONIZED_LOCK = Integer.valueOf(1);

  /** 受信結果のキー（新着メール数） */
  public static final String KEY_NEW_MAIL_NUM = "nmn";

  /** 受信結果のキー（受信メール数） */
  public static final String KEY_RECEIVE_MAIL_NUM = "rmn";

  /** 受信結果のキー（受信メール総数） */
  public static final String KEY_RECEIVE_MAIL_ALL_NUM = "rman";

  /** 受信中のキー */
  public static final String KEY_RECEIVE_STAT = "rs";

  /** 受信中のキー */
  public static final String KEY_THREAD = "thread";

  /** 処理タイプ（メール受信） */
  public static final int PROCESS_TYPE_RECEIVEMAIL = 1;

  /** 処理タイプ（新着メール確認） */
  public static final int PROCESS_TYPE_GET_NEWMAILNUM = 2;

  /** 処理タイプ（メール受信中断） */
  public static final int PROCESS_TYPE_STOP_RECEIVEMAIL = 3;

  /** 処理状態（終了） */
  public static final int PROCESS_STAT_FINISHED = 0;

  /** 処理状態（実行中） */
  public static final int PROCESS_STAT_PROCESSING = -100;

  /** 処理状態（未実行） */
  public static final int PROCESS_STAT_NONPROCESSING = -101;

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

  /** ユーザー ID */
  private String userId = null;

  /** メールアカウント ID */
  private int mailAccountId = 0;

  /** 処理タイプ */
  private int processType = PROCESS_TYPE_RECEIVEMAIL;

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

  // add start
  private static boolean do_performance_test =
    JetspeedResources.getBoolean("aipo.do_performance_test", false);

  // add end

  /**
   * コンストラクタ
   * 
   * @param orgId
   * @param userId
   * @param mailAccountId
   */
  public ALPop3MailReceiveThread(DataContext dataContext, JetspeedUser user,
      int mailAccountId, int processType) {
    this.dataContext = dataContext;
    this.mailAccountId = mailAccountId;
    this.processType = processType;

    userId = user.getUserId();
  }

  /**
   * メール受信処理
   * 
   */
  @Override
  public void run() {
    ALStaticObject ob = ALStaticObject.getInstance();
    ob.addAccountId(mailAccountId);

    // add start by motegi
    ALNewMailCounterStaticObject obmc =
      ALNewMailCounterStaticObject.getInstance();
    obmc.addAccountId(mailAccountId);
    // add end

    try {

      DataContext.bindThreadDataContext(dataContext);
      String orgId = Database.getDomainName();

      EipMMailAccount account =
        ALMailUtils.getMailAccount(Integer.parseInt(userId), mailAccountId);
      // add start
      // アカウントが取得できない場合
      if (null == account) {
        // アカウントがユーザーの代理受信元アカウントであることが確認できた場合は、そのアカウントで続行
        account = ALMailUtils.getMailAccount(mailAccountId);
        if (!ALMailUtils.isRepresentReceiveAccount(account, Integer
          .parseInt(userId))) {
          throw new Exception();
        }
      }
      // add end

      if (processType == PROCESS_TYPE_RECEIVEMAIL) {
        // logger
        // .info("[ALFilePop3MailReceiveThread] start receivemail (orgId,
        // userId, mailAccountId)=("
        // + Database.getDomainName()
        // + ","
        // + userId
        // + ","
        // + mailAccountId
        // + ")");

        // change start by motegi
        // ob.updateAccountStat(mailAccountId, KEY_RECEIVE_MAIL_NUM, Integer
        // .valueOf(0));
        obmc.updateAccountStat(mailAccountId, KEY_RECEIVE_MAIL_NUM, Integer
          .valueOf(0));
        // change end

        // add start
        if (do_performance_test) {
          logger.info("Webメール受信処理開始【"
            + account.getPop3userName()
            + "】UserId("
            + userId
            + ")");
        }
        // add end

        // メール受信
        int res = receiveMail(orgId, account);

        // add start
        if (do_performance_test) {
          Integer mailNum =
            (Integer) ALNewMailCounterStaticObject
              .getInstance()
              .getAccountStat(mailAccountId, KEY_RECEIVE_MAIL_NUM);
          logger.info("Webメール受信処理終了【"
            + account.getPop3userName()
            + "】UserId("
            + userId
            + ") 受信数【"
            + mailNum
            + "】　結果【"
            + res
            + "】");
        }
        // add end

        // change start by motegi
        // ob.updateAccountStat(mailAccountId, KEY_RECEIVE_MAIL_NUM, Integer
        // .valueOf(res));
        if (res < 0) {
          obmc.updateAccountStat(mailAccountId, KEY_RECEIVE_MAIL_NUM, Integer
            .valueOf(res));
        }
        // change end
      } else if (processType == PROCESS_TYPE_GET_NEWMAILNUM) {
        logger
          .info("[ALFilePop3MailReceiveThread] start newmailnum (orgId, userId, mailAccountId)=("
            + orgId
            + ","
            + userId
            + ","
            + mailAccountId
            + ")");

        // 新着メール数確認
        int res = checkNewMailNum(orgId, account);
        if (res >= 0) {
          // change start by motegi
          // ob.updateAccountStat(mailAccountId, KEY_NEW_MAIL_NUM, Integer
          // .valueOf(res));
          obmc.updateAccountStat(mailAccountId, KEY_NEW_MAIL_NUM, Integer
            .valueOf(res));
          // change end
        }
      }
    } catch (Exception e) {
      logger.error("[ALFilePop3MailReceiveThread]", e);
    } finally {
      Database.tearDown();
      // change start by motegi
      // ob.updateAccountStat(
      // mailAccountId,
      // KEY_RECEIVE_STAT,
      // PROCESS_STAT_FINISHED);
      ob.removeAccountId(mailAccountId);
      // change end
    }
  }

  /**
   * 指定されたアカウントのメールを受信する。
   * 
   * @param account
   */
  private int receiveMail(String orgId, EipMMailAccount account) {
    ALStaticObject.getInstance().updateAccountStat(
      mailAccountId,
      KEY_RECEIVE_STAT,
      PROCESS_STAT_PROCESSING);
    int result = ALPop3MailReceiver.RECEIVE_MSG_FAIL;
    if (account == null) {
      return result;
    }

    try {
      ALMailHandler handler =
        ALMailFactoryService.getInstance().getMailHandler();
      ALMailReceiverContext rcontext =
        ALMailUtils.getALPop3MailReceiverContext(orgId, account);

      result = handler.receive(rcontext, orgId);

      if (result <= ALPop3MailReceiver.RECEIVE_MSG_FAIL
        && result != ALPop3MailReceiver.RECEIVE_MSG_FAIL_OVER_MAIL_MAX_SIZE) {
        // 受信に失敗した場合の処理
        return result;
      }

      // 最終受信日を保存する．
      DataContext dataContext = account.getDataContext();
      account.setLastReceivedDate(Calendar.getInstance().getTime());
      // change start 0728 余計な例外を出力させない
      // Database.commit(dataContext);
      try {
        Database.commit(dataContext);
      } catch (CayenneRuntimeException e) {
        // logger.error("最終受信日時の更新でエラーが発生しました。", e);
      }
      // change end
    } catch (Exception ex) {
      Database.rollback();
      logger.error("[ALFilePop3MailReceiveThread]", ex);
      result = ALPop3MailReceiver.RECEIVE_MSG_FAIL;
      return result;
    }
    return result;
  }

  /**
   * 指定されたアカウントの新着メール数を取得する。
   * 
   * @param account
   */
  private int checkNewMailNum(String orgId, EipMMailAccount account) {
    int res = -1;
    if (account == null) {
      return res;
    }

    try {
      ALMailHandler handler =
        ALMailFactoryService.getInstance().getMailHandler();
      ALMailReceiverContext rcontext =
        ALMailUtils.getALPop3MailReceiverContext(orgId, account);

      res = handler.getNewMailSum(rcontext);

    } catch (Exception ex) {
      logger.error("[ALFilePop3MailReceiveThread]", ex);
      res = -1;
    }
    return res;
  }

  /**
   * 新着メール数を取得する。
   * 
   * @param user
   * @return
   */
  public static int getNewMailNum(User user, int mailAccountId) {
    if (user == null) {
      return PROCESS_STAT_NONPROCESSING;
    }

    // change start by motegi
    // ALStaticObject ob = ALStaticObject.getInstance();
    ALNewMailCounterStaticObject ob =
      ALNewMailCounterStaticObject.getInstance();
    // change end
    Object obj = ob.getAccountStat(mailAccountId, KEY_NEW_MAIL_NUM);

    if (obj == null) {
      return PROCESS_STAT_NONPROCESSING;
    }

    return ((Integer) obj).intValue();
  }

  /**
   * 指定したユーザーのaccountIdが存在するかどうかチェック
   * 
   * @param user
   * @return
   */
  public static boolean isProcessing(User user, int mailAccountId) {
    return !(ALStaticObject.getInstance().receivable(mailAccountId));
  }

  /**
   * 指定したユーザーがメールを受信中かどうかチェック
   * 
   * @param user
   * @return
   */
  public static boolean isReceiving(User user, int mailAccountId) {
    Object objRS =
      ALStaticObject.getInstance().getAccountStat(
        mailAccountId,
        KEY_RECEIVE_STAT);
    if (objRS == null || (Integer) objRS != PROCESS_STAT_PROCESSING) {
      return false;
    }
    return true;
  }

  /**
   * 受信結果を取得する。
   * 
   * @param user
   * @return
   */
  public static int getReceiveMailResult(User user, int mailAccountId) {

    // change start by motegi
    // ALStaticObject ob = ALStaticObject.getInstance();
    ALNewMailCounterStaticObject ob =
      ALNewMailCounterStaticObject.getInstance();
    // change end

    // change start
    Object obj = ob.getAccountStat(mailAccountId, KEY_RECEIVE_MAIL_NUM);
    // Object obj = ob.getAccountStat(mailAccountId, KEY_RECEIVE_MAIL_ALL_NUM);
    // change end

    if (obj == null) {
      return PROCESS_STAT_NONPROCESSING;
    }

    return ((Integer) obj).intValue();
  }

  public static String getReceiveMailResultStr(User user, int mailAccountId) {
    String msg = null;

    if (isReceiving(user, mailAccountId)) {
      StringBuffer sb = new StringBuffer();

      // change start by motegi
      // Object objRMAN =
      // ALStaticObject.getInstance().getAccountStat(
      // mailAccountId,
      // KEY_RECEIVE_MAIL_ALL_NUM);
      // Object objRMN =
      // ALStaticObject.getInstance().getAccountStat(
      // mailAccountId,
      // KEY_RECEIVE_MAIL_NUM);
      Object objRMAN =
        ALNewMailCounterStaticObject.getInstance().getAccountStat(
          mailAccountId,
          KEY_RECEIVE_MAIL_ALL_NUM);
      Object objRMN =
        ALNewMailCounterStaticObject.getInstance().getAccountStat(
          mailAccountId,
          KEY_RECEIVE_MAIL_NUM);
      // change end

      // change start
      // if (objRMAN != null) {
      if (objRMAN != null && (Integer) objRMAN > 0) {
        // change end
        sb
          .append("メールを受信中です（ ")
          .append(objRMAN)
          .append(" 件中 ")
          .append(objRMN)
          .append(" 件を受信 ）。");
      } else {
        sb.append("メール受信サーバに問い合わせ中です。");
      }
      return sb.toString();
    }

    // 処理が終了している場合
    int res = getReceiveMailResult(user, mailAccountId);

    if (res == PROCESS_STAT_NONPROCESSING) {
      msg = "";
    } else if (res == ALPop3MailReceiver.RECEIVE_MSG_FAIL) {
      msg = "メールを受信できませんでした。メールアカウントの設定をご確認ください。";
    } else if (res == ALPop3MailReceiver.RECEIVE_MSG_FAIL_LOCKED) {
      msg = "メールの受信中、もしくは、メンテナンス中です。しばらくしてから、『メール一覧』ボタンを押してください。";
    } else if (res == ALPop3MailReceiver.RECEIVE_MSG_FAIL_OVER_MAIL_MAX_SIZE) {
      msg = ("7MB よりも大きいサイズのメールがありました。7MBを超えたメールの場合は、送信者などの情報のみ受信し、本文は受信しません。");
    } else if (res == ALPop3MailReceiver.RECEIVE_MSG_FAIL_CONNECT) {
      msg = "設定されている受信サーバ（POP3）と接続できませんでした。";
    } else if (res == ALPop3MailReceiver.RECEIVE_MSG_FAIL_AUTH) {
      msg = "設定されている受信サーバ（POP3）へのログインに失敗しました。";
    } else if (res == ALPop3MailReceiver.RECEIVE_MSG_FAIL_EXCEPTION) {
      msg = "システム上の問題により、メールを受信できませんでした（Exception エラー）。";
    } else if (res == ALPop3MailReceiver.RECEIVE_MSG_FAIL_OUTOFMEMORY) {
      msg = "システム上の問題により、メールを受信できませんでした（OutOfMemory エラー）。";
      // change start
      // } else {
    } else if (res > 0) { // 受信メールがある場合
      // change end
      msg = "メールを受信しました。";
    }

    // change start by motegi
    // ALStaticObject.getInstance().removeAccountId(mailAccountId);
    ALNewMailCounterStaticObject.getInstance().removeAccountId(mailAccountId);
    // change end

    return msg;
  }

}
