package jp.sourceforge.mailagent;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.AuthenticationFailedException;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;


import org.apache.log4j.Logger;

/**
 * メール送信スレッド.
 */
class SendmailThread implements Runnable{
   private MailSource source;       // メール送信ソース
   private String smtpserver;       // SMTPメールサーバ
   private boolean smtpauth=false; // SMTP認証 true=行う
   private String smtpuser;         // SMTP認証ユーザ
   private String smtppasswd;       // SMTP認証パスワード
   private String smtptimeout;      // SMTPタイムアウト(milli seconds)
   private String encode;           // 文字エンコード
  
   /**
    * コンストラクタ.
    * @param msource メール送信ソース
    */
   protected SendmailThread(MailSource msource){
      this.precheck(msource);
      this.source = msource;
   }
   /**
    * ＳＭＴＰサーバセット
    * @param smtp ＳＭＴＰサーバ
    * @param auth SMTP認証
    * @param user SMTP認証ユーザ名（不要ならnull）
    * @param passwd SMTP認証パスワード（不要ならnull）
    * @param smtpconntimeout SMTPタイムアウト
    */
   protected void setSMTP(String smtp,boolean auth,String smtpuser,String smtppasswd, int smtpconntimeout){
      this.smtpserver = smtp;
      this.smtpauth = auth;
      this.smtpuser = smtpuser;
      this.smtppasswd = smtppasswd;
      this.smtptimeout = Integer.toString(smtpconntimeout * 1000);
   }
   /**
    * 文字エンコードセット
    * @param encode
    */
   protected void setEncode(String encode){
      this.encode = encode;
   }
   
   /* メール送信処理スレッド実行
    * @see java.lang.Runnable#run()
    */
   public void run(){
      Logger logger = Logger.getLogger("mailagent"); // ロギング
      ResultMail rtn = this.send();
      logger.debug("mail send status = "+rtn.isStatus()+" subject:"+this.source.getSubject());
      if (rtn.isStatus()==false){
         logger.warn("send failed : subject:"+this.source.getSubject(),rtn.getThrowable());
      }
   }
   
   /**
    * メール送信処理.
    * @return 結果
    */
   protected ResultMail send(){
      ResultMail rtn = new ResultMail();
      // SMTPの設定
      Properties props = System.getProperties();
      props.put("mail.smtp.host",this.smtpserver);
      props.put("mail.smtp.timeout",this.smtptimeout);
      if (this.smtpauth){ 
         // SMTP認証が必要な場合
         props.put("mail.smtp.auth","true");
      }
      Session session=Session.getDefaultInstance(props,null);
      try{
      MimeMessage mimeMessage = new MimeMessage(session);
      // メール作成
      Map<String,String> map;
      // FROM:
      map = this.source.getFrommap();
      Iterator<String> itr=map.keySet().iterator();
      String fromaddres = itr.next();
      String fromalias = map.get(fromaddres);
      if (fromalias != null && fromalias.length() > 0){
         mimeMessage.setFrom(new InternetAddress(fromaddres,fromalias,this.encode));
         // 返信先あれば設定
         if (this.source.getReplyto() != null){
            mimeMessage.setReplyTo(new Address[]{new InternetAddress(this.source.getReplyto())});  
         }
      }else{
         mimeMessage.setFrom(new InternetAddress(fromaddres));
         // 返信先あれば設定
         if (this.source.getReplyto() != null){
            mimeMessage.setReplyTo(new Address[]{new InternetAddress(this.source.getReplyto())});  
         }
      }
      // TO:
      mimeMessage = setMimeAddres(mimeMessage,this.source.getTomap(),Message.RecipientType.TO);
      // CC:
      mimeMessage = setMimeAddres(mimeMessage,this.source.getCcmap(),Message.RecipientType.CC);
      // BCC:
      mimeMessage = setMimeAddres(mimeMessage,this.source.getBccmap(),Message.RecipientType.BCC);
      // Subject:
      mimeMessage.setSubject(this.source.getSubject(),this.encode);
      // メール内容（添付あり／なしで本文セットが異なる）
      List<File> templist = this.source.getTemplist();
      if (templist.size() > 0){
         // 添付あり
         MimeBodyPart txtmbp = new MimeBodyPart();  
         txtmbp.setText(this.source.getMessage(),this.encode);
         txtmbp.setHeader("Content-Type","text/plain; charset="+this.encode);
         MimeMultipart multipart = new MimeMultipart(); // マルチパートオブジェクト
         multipart.addBodyPart(txtmbp);
         for(Iterator<File> it=templist.iterator();it.hasNext();){
            MimeBodyPart mbp2 = new MimeBodyPart();
            // 添付するファイル名を指定
            FileDataSource fds = new FileDataSource(it.next());
            mbp2.setDataHandler(new DataHandler(fds));
            //mbp2.setFileName(MimeUtility.encodeWord(fds.getName()));
            mbp2.setFileName(MimeUtility.encodeWord(fds.getName(),"Windows-31J","B"));
            // ボディパートを追加
            multipart.addBodyPart(mbp2);
         }
         // マルチパートオブジェクトをメッセージに設定
         mimeMessage.setContent(multipart);

      }else{
         // 添付なし
         mimeMessage.setText(this.source.getMessage(),this.encode);
         mimeMessage.setHeader("Content-Type","text/plain; charset="+this.encode);
         mimeMessage.setHeader("Content-Transfer-Encoding","quoted-printable");
      }
      
      mimeMessage.setSentDate(new Date());   // 送信日付
      // 送信
      if (this.smtpauth){ 
         // SMTP認証が必要な場合
         Transport transport = session.getTransport("smtp");
         transport.connect(this.smtpserver,this.smtpuser,this.smtppasswd);
         transport.sendMessage(mimeMessage,mimeMessage.getAllRecipients());
      }else{
         Transport.send(mimeMessage);
      }
      }catch(AuthenticationFailedException e){
         rtn.setStatus(false);
         rtn.setErrormsg("AuthenticationFailedException : "+this.smtpserver
                        +":"+this.smtpuser+":"+this.smtppasswd);
      }catch(NoSuchProviderException e){
         rtn.setStatus(false);
         rtn.setErrormsg("NoSuchProviderException : "+this.smtpserver
                        +":"+this.smtpuser+":"+this.smtppasswd);
      }catch(MessagingException e){
         rtn.setStatus(false);
         rtn.setErrormsg("MessagingException : "+e.getMessage(),e);
      }catch(UnsupportedEncodingException e){
         rtn.setStatus(false);
         rtn.setErrormsg("UnsupportedEncodingException : "+this.encode);
      }catch(Exception e){
         rtn.setStatus(false);
         rtn.setErrormsg("other Exception : "+e.getMessage());
      }
      return rtn;
   }
   private MimeMessage setMimeAddres(MimeMessage mime,Map<String,String> map,Message.RecipientType type) 
   throws MessagingException,UnsupportedEncodingException{
      int mapsize = map.size();
      if (mapsize==0){ return mime; }
      MimeMessage rtn = mime;
      String[] addres = new String[mapsize];
      String[] alias  = new String[mapsize];
      int i=0;
      for(Iterator<String> it=map.keySet().iterator();it.hasNext();i++){
         addres[i] = it.next();
         alias[i] = map.get(addres);
      }
      rtn.setRecipients(type,addres[0]);
      if (alias[0] != null && alias[0].length() > 0){
         rtn.setRecipient(type,new InternetAddress(addres[0],alias[0],this.encode));
      }
      for(int k=1;k < addres.length;k++){
         rtn.addRecipients(type,addres[k]);
         if (alias[k] != null && alias[k].length() > 0){
            rtn.addRecipient(type,new InternetAddress(addres[k],alias[k],this.encode));
         }
      }
      return rtn;
   }
   /**
    * 事前チェック
    * @param msource
    */
   private void precheck(MailSource msource){
      // 送信元メールアドレス
      if (msource.getFrommap().isEmpty()){
         throw new UnsupportedOperationException("◆ mail from Not found");
      }
      // TO:の存在チェック
      if (msource.getTomap().isEmpty()){
         throw new UnsupportedOperationException("◆ mail to Not found");
      }
      // タイトル無きものは、エラー
      String chk;
      chk = msource.getSubject();
      if (chk==null || chk.length()==0){
         throw new UnsupportedOperationException("◆ mail subject Not found");
      }
      // メッセージ無きものは、エラー
      chk = msource.getMessage();
      if (chk==null || chk.length()==0){
         throw new UnsupportedOperationException("◆ mail mail Body Not found");
      }
   }
}
