/*
 * 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.file;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.jar.Attributes;

import javax.mail.Header;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;

import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;

import com.aimluck.eip.cayenne.om.portlet.EipTMail;
import com.aimluck.eip.common.ALPageNotFoundException;
import com.aimluck.eip.mail.ALAbstractFolder;
import com.aimluck.eip.mail.ALLocalMailMessage;
import com.aimluck.eip.mail.ALMailMessage;
import com.aimluck.eip.orm.Database;
import com.aimluck.eip.orm.query.SelectQuery;
import com.aimluck.eip.services.storage.ALStorageService;

/**
 * ローカルのファイルシステムを利用し、送受信したメールを保持するローカルフォルダのクラスです。 <br />
 * 
 */
public class ALFileLocalFolder extends ALAbstractFolder {

  private static final JetspeedLogger logger = JetspeedLogFactoryService
    .getLogger(ALFileLocalFolder.class.getName());

  /** メールのファイル名規則 */
  public static final String DEFAULT_MAIL_FILENAME_DATE_FORMAT =
    "yyyyMMddHHmmssSSS";

  /**
   * コンストラクタ
   * 
   * @param parentFolder
   *          親フォルダ
   * @param folderName
   *          自身のフォルダ名
   */
  public ALFileLocalFolder(int type_mail, String org_id, int user_id,
      int account_id) {
    super(type_mail, org_id, user_id, account_id);
  }

  /**
   * メールを取得します。
   * 
   * @param index
   * @return
   */
  @Override
  public ALLocalMailMessage getMail(int mailid) {
    try {
      SelectQuery<EipTMail> query = Database.query(EipTMail.class);

      Expression exp1 =
        ExpressionFactory.matchDbExp(EipTMail.MAIL_ID_PK_COLUMN, Integer
          .valueOf(mailid));
      Expression exp2 =
        ExpressionFactory.matchExp(EipTMail.USER_ID_PROPERTY, user_id);

      EipTMail email = query.setQualifier(exp1.andExp(exp2)).fetchSingle();
      if (email == null) {
        logger.debug("[Mail] Not found ID...");
        return null;
      }

      ALLocalMailMessage msg =
        readMail(getFullName()
          + ALStorageService.separator()
          + email.getFilePath());

      // 未読→既読に変更
      email.setReadFlg("T");
      Database.commit();

      return msg;
    } catch (Throwable t) {
      Database.rollback();
      logger.error(t);
      return null;
    }
  }

  /**
   * 指定されたファイルを読み込み，mail メッセージを取得する．
   * 
   * @param fileName
   * @return
   */
  private ALLocalMailMessage readMail(String filepath) {
    System.setProperty("mail.mime.charset", "ISO-2022-JP");
    System.setProperty("mail.mime.decodetext.strict", "false");
    Properties prop = new Properties();
    prop.setProperty("mail.mime.address.strict", "false");
    ALLocalMailMessage localmsg = null;
    BufferedInputStream input = null;
    try {
      input = new BufferedInputStream(ALStorageService.getFile(filepath));
      localmsg =
        new ALLocalMailMessage(Session.getDefaultInstance(prop), input);
      input.close();
    } catch (Exception ex) {
      logger.error("Exception", ex);
    }
    return localmsg;
  }

  /**
   * メールを保存する。
   * 
   * @param messages
   * @return
   */
  @Override
  public boolean saveMail(ALMailMessage mail, String orgId) {
    boolean res = false;

    try {
      String tmpFileName = getNewFileName();
      res = saveMailToFile(mail, tmpFileName, true);

      if (res) {

        res = insertMailToDB((MimeMessage) mail, tmpFileName, false, false);
      }
    } catch (Exception ex) {
      logger.error("Exception", ex);
      res = false;
    }
    return res;
  }

  /**
   * 受信サーバから受信した受信可能サイズを超えたメールを保存する。<br />
   * このメールはヘッダ情報のみ、受信サーバから取得し、他の情報は取得しない。
   * 
   * @param localMailMessage
   * @return
   */
  @Override
  public boolean saveDefectiveMail(ALMailMessage mail, String orgId) {
    boolean res = false;
    try {
      String tmpFileName = getNewFileName();
      res = saveMailToFile(mail, tmpFileName, false);

      if (res) {
        res = insertMailToDB((MimeMessage) mail, tmpFileName, false, false);
      }
    } catch (Exception ex) {
      logger.error("Exception", ex);
      res = false;
    }
    return res;
  }

  /**
   * メールをファイルに保存します。
   * 
   * @param mail
   * @return
   */
  private boolean saveMailToFile(ALMailMessage mail, String fileName,
      boolean savecontents) {
    boolean res = false;
    ByteArrayOutputStream output = null;
    try {
      // String pop3MailPath = getFullName() + File.separator + fileName;

      // メールの保存
      output = new ByteArrayOutputStream();
      if (savecontents) {
        mail.writeTo(output);
      } else {
        MimeMessage mimeMessage = (MimeMessage) mail;
        Session session = Session.getDefaultInstance(new Properties());
        Message newMsg = new MimeMessage(session);
        Enumeration<?> headers = mimeMessage.getAllHeaders();
        while (headers.hasMoreElements()) {
          Header h = (Header) headers.nextElement();
          newMsg.addHeader(h.getName(), h.getValue());
        }
        newMsg
          .setText("メールのサイズが7MBを超えていたため、このメールを受信できませんでした。\r\n 誠に恐れ入りますが、別のメーラーで受信してください。");
        newMsg.writeTo(output);
      }
      ALStorageService.createNewFile(new ByteArrayInputStream(output
        .toByteArray()), getFullName(), fileName);

      output.flush();
      output.close();

      mail.clearContents();

      res = true;
    } catch (Exception ex) {
      logger.error("Exception", ex);
      try {
        if (output != null) {
          output.close();
        }
      } catch (IOException ie) {
        logger.error("Exception", ie);
      }
      res = false;
    } finally {
      try {
        if (output != null) {
          output.close();
        }
      } catch (IOException ie) {
        logger.error("Exception", ie);
        res = false;
      }
    }
    return res;
  }

  /**
   * 指定されたインデックスのメールを削除する．
   * 
   * @return
   */
  @Override
  public boolean deleteMail(int mailid) {
    try {
      SelectQuery<EipTMail> query = Database.query(EipTMail.class);
      Expression exp1 =
        ExpressionFactory.matchDbExp(EipTMail.MAIL_ID_PK_COLUMN, Integer
          .valueOf(mailid));
      Expression exp2 =
        ExpressionFactory.matchExp(EipTMail.USER_ID_PROPERTY, user_id);
      Expression exp3 =
        ExpressionFactory.matchExp(EipTMail.ACCOUNT_ID_PROPERTY, account_id);

      List<EipTMail> mail_list =
        query
          .andQualifier(exp1)
          .andQualifier(exp2)
          .andQualifier(exp3)
          .fetchList();
      if (mail_list == null || mail_list.size() == 0) {
        logger.debug("[ALDbLocalFolder] Not found ID...");
        throw new ALPageNotFoundException();
      }

      EipTMail record = mail_list.get(0);
      String filePath = record.getFilePath();

      // ファイル削除
      ALStorageService.deleteFile(getFullName()
        + ALStorageService.separator()
        + filePath);

      // メールを削除する
      Database.delete(record);
      Database.commit();
    } catch (Throwable t) {
      Database.rollback();
      logger.error(t);
      return false;
    }
    return true;
  }

  /**
   * 指定されたインデックスのメールを削除する．
   * 
   * @param msgIndexes
   * @return
   */
  @Override
  public boolean deleteMails(List<String> msgIndexes) {
    try {
      SelectQuery<EipTMail> query = Database.query(EipTMail.class);
      Expression exp1 =
        ExpressionFactory.inDbExp(EipTMail.MAIL_ID_PK_COLUMN, msgIndexes);
      Expression exp2 =
        ExpressionFactory.matchExp(EipTMail.USER_ID_PROPERTY, Integer
          .valueOf(user_id));

      List<EipTMail> mail_list =
        query.andQualifier(exp1).andQualifier(exp2).fetchList();
      if (mail_list == null || mail_list.size() == 0) {
        logger.debug("[ALFileLocalFolder] Not found ID...");
        throw new ALPageNotFoundException();
      }

      for (EipTMail record : mail_list) {
        String filePath = record.getFilePath();
        ALStorageService.deleteFile(getFullName()
          + ALStorageService.separator()
          + filePath);
      }

      Database.deleteAll(mail_list);
      Database.commit();
    } catch (Throwable t) {
      Database.rollback();
      logger.error(t);
      return false;
    }
    return true;
  }

  /**
   * 
   * @return
   */
  @Override
  protected Attributes getColumnMap() {
    Attributes map = new Attributes();
    map.putValue("read_flg", EipTMail.READ_FLG_PROPERTY);
    map.putValue("subject", EipTMail.SUBJECT_PROPERTY);
    map.putValue("person", EipTMail.PERSON_PROPERTY);
    map.putValue("date", EipTMail.EVENT_DATE_PROPERTY);
    map.putValue("volume", EipTMail.FILE_VOLUME_PROPERTY);
    return map;
  }

  /**
   * 新着メール数を取得する。
   * 
   * @return
   */
  @Override
  public int getNewMailNum() {
    return 0;
  }

  /**
   * 新着メール数を更新する．
   * 
   * @param num
   */
  @Override
  public void setNewMailNum(int num) {

  }

  /**
   * 指定したフォルダ内のメールの総数を取得する。
   * 
   * @param type
   *          送受信フラグ
   * @return
   */
  public int getMailSum() {
    return 0;
  }

  /**
   * 指定したフォルダ内の未読メール数を取得する．
   * 
   * @return
   */
  @Override
  public int getUnreadMailNum() {
    return 0;
  }

  /**
   * ローカルフォルダを閉じる．
   */
  @Override
  public void close() {
  }

  /**
   * 新しいファイル名を生成する．
   * 
   * @return
   */
  public String getNewFileName() {
    return String.valueOf(System.nanoTime());
  }
}
