/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * Copyright (C) 2004-2008 Aimluck,Inc.
 * http://aipostyle.com/
 * 
 * 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.mail;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

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.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.ALStaticObject;
import com.aimluck.eip.orm.DatabaseOrmService;

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

  /** スレッド名のキー */
  public static final String KEY_THREAD_NAME = "tr_pop3mail_recieve";

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

  /** 処理状態のキー */
  public static final String KEY_THREAD_STAT = "tr_pop3mail_recieve_stat";

  /** 受信結果のキー */
  public static final String KEY_THREAD_RESULT = "tr_pop3mail_recieve_res";

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

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

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

  /** 処理状態（終了） */
  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 String orgId = null;

  /** ユーザー */
  private JetspeedUser user;

  /** ユーザー 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());

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

    userId = user.getUserId();
  }

  /**
   * メール受信処理
   * 
   */
  public void run() {
    int res = 0;

    /**
     * [mailAccountId, [[KEY_THREAD_STAT, PROCESS_STAT_PROCESSING],
     * [KEY_THREAD_NEWMAILNUM, KEY_THREAD_NEWMAILNUM]]]
     * 
     */
    Map result = null;
    Map statMap = null;
    try {
      // メール受信中フラグ
      result = (Map) user.getTemp(KEY_THREAD_NAME);
      if (result != null) {
        statMap = (Map) result.get(Integer.valueOf(mailAccountId));
        if (statMap == null) {
          statMap = new HashMap();
          result.put(Integer.valueOf(mailAccountId), statMap);
        }
        statMap.put(KEY_THREAD_STAT, Integer.valueOf(PROCESS_STAT_PROCESSING));
        statMap.put(KEY_THREAD_NEWMAILNUM, Integer
            .valueOf(PROCESS_STAT_PROCESSING));
      } else {
        statMap = new HashMap();
        statMap.put(KEY_THREAD_STAT, Integer.valueOf(PROCESS_STAT_PROCESSING));
        statMap.put(KEY_THREAD_NEWMAILNUM, Integer
            .valueOf(PROCESS_STAT_PROCESSING));

        result = new HashMap();
        result.put(Integer.valueOf(mailAccountId), statMap);
        user.setTemp(KEY_THREAD_NAME, result);
      }

      EipMMailAccount account = ALMailUtils.getMailAccount(orgId, Integer
          .parseInt(userId), mailAccountId);
      if (processType == PROCESS_TYPE_RECEIVEMAIL) {
        System.out
            .println("[ALFilePop3MailReceiveThread] start receivemail (orgId, userId, mailAccountId)=("
                + orgId + "," + userId + "," + mailAccountId + ")");

        // メール受信
        res = receiveMail(orgId, account);
      } else if (processType == PROCESS_TYPE_GET_NEWMAILNUM) {
        System.out
            .println("[ALFilePop3MailReceiveThread] start newmailnum (orgId, userId, mailAccountId)=("
                + orgId + "," + userId + "," + mailAccountId + ")");

        // 新着メール数確認
        int num = checkNewMailNum(orgId, account);

        if (num > -1) {
          statMap.put(KEY_THREAD_NEWMAILNUM, Integer.valueOf(num));
          res = ALPop3MailReceiver.RECEIVE_MSG_SUCCESS;
        } else {
          res = ALPop3MailReceiver.RECEIVE_MSG_FAIL;
        }
      }

      statMap.put(KEY_THREAD_RESULT, Integer.valueOf(res));
      statMap.put(KEY_THREAD_STAT, Integer.valueOf(PROCESS_STAT_FINISHED));

      ALStaticObject ob = ALStaticObject.getInstance();
      ob.removeId(mailAccountId);
    } catch (Exception e) {
      logger.error("[ALFilePop3MailReceiveThread]", e);
    } finally {
      // try {
      // statMap.remove(KEY_THREAD_STAT);
      // } catch (Exception e) {
      // logger.error("[ALFilePop3MailReceiveThread]", e);
      // }
    }
  }

  /**
   * 指定されたアカウントのメールを受信する。
   * 
   * @param account
   */
  private int receiveMail(String orgId, EipMMailAccount account) {
    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_SUCCESS
          && result != ALPop3MailReceiver.RECEIVE_MSG_FAIL_OVER_MAIL_MAX_SIZE) {
        // 受信に失敗した場合の処理
        return result;
      }

      // 最終受信日を保存する．
      DataContext dataContext = DatabaseOrmService.getInstance()
          .getDataContext();
      account.setLastReceivedDate(new Date());
      dataContext.commitChanges();
    } catch (Exception ex) {
      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;

    Map result = (Map) user.getTemp(KEY_THREAD_NAME);
    if (result == null)
      return PROCESS_STAT_NONPROCESSING;

    Map statMap = (Map) result.get(Integer.valueOf(mailAccountId));
    if (statMap == null) {
      return PROCESS_STAT_NONPROCESSING;
    }

    Integer res = (Integer) statMap.get(KEY_THREAD_RESULT);
    if (res == null) {
      return PROCESS_STAT_NONPROCESSING;
    }

    if (res.intValue() != ALPop3MailReceiver.RECEIVE_MSG_SUCCESS) {
      return res.intValue();
    }

    Integer mailNum = (Integer) statMap.get(KEY_THREAD_NEWMAILNUM);
    if (mailNum == null)
      return PROCESS_STAT_PROCESSING;

    return mailNum.intValue();
  }

  /**
   * 指定したユーザーがメールを受信中かどうかチェック
   * 
   * @param user
   * @return
   */
  public static boolean isProcessing(User user, int mailAccountId) {
    int stat = getStat(user, mailAccountId);
    return (stat == PROCESS_STAT_PROCESSING);
  }

  /**
   * 現在の状態（未実行、実行中、終了）を取得する。
   * 
   * @param user
   * @return
   */
  public static int getStat(User user, int mailAccountId) {
    Map result = (Map) user.getTemp(KEY_THREAD_NAME);
    if (result == null) {
      return PROCESS_STAT_NONPROCESSING;
    }

    Map statMap = (Map) result.get(Integer.valueOf(mailAccountId));
    if (statMap == null) {
      return PROCESS_STAT_NONPROCESSING;
    }

    Integer stat = (Integer) statMap.get(KEY_THREAD_STAT);
    if (stat == null)
      return PROCESS_STAT_NONPROCESSING;

    return stat.intValue();
  }

  /**
   * 受信結果を取得する。
   * 
   * @param user
   * @return
   */
  public static int getReceiveMailResult(User user, int mailAccountId) {
    Map result = (Map) user.getTemp(KEY_THREAD_NAME);
    if (result == null) {
      return PROCESS_STAT_NONPROCESSING;
    }

    Map statMap = (Map) result.get(Integer.valueOf(mailAccountId));
    if (statMap == null) {
      return PROCESS_STAT_NONPROCESSING;
    }

    Integer res = (Integer) statMap.get(KEY_THREAD_RESULT);
    if (res == null) {
      return PROCESS_STAT_NONPROCESSING;
    }

    // セッションから削除する。
    statMap.remove(KEY_THREAD_RESULT);

    return res.intValue();
  }

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

    int stat = getStat(user, mailAccountId);
    if (stat == PROCESS_STAT_PROCESSING) {
      msg = "メールを受信中です。";
      return msg;
    } else if (stat == PROCESS_STAT_NONPROCESSING) {
      msg = "";
      return msg;
    }

    // 処理が終了している場合
    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 エラー）。";
    } else {
      msg = "メールを受信しました。";
    }

    return msg;
  }
}
