package org.maachang.comet.httpd.engine.script.service.mail;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Properties;

import javax.mail.Address;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeUtility;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * メール受信サービス.
 *
 * @version 2007/10/22.
 * @author masahito suzuki.
 * @since MaachangComet 1.00
 */
public class ReceiveMailService {
    
    /**
     * LOG.
     */
    private static final Log LOG = LogFactory.getLog( ReceiveMailService.class ) ;
    
    /**
     * 添付ファイル名なしの場合の代用ファイル名.
     */
    private static final String UNKNOWN_ATTACHMENT = "attachment-" ;
    
    /**
     * メールを受信.
     * <BR><BR>
     * メールを受信します.
     * <BR>
     * @param mode [true]の場合、1件以上メールが存在する場合、１件だけを受信します.
     * @param receiveMail 受信メール情報を設定します.
     * @return ArrayList<ResultMail> 受信結果内容が返されます.
     */
    public static ArrayList<ResultMail> receiveMailTo( boolean mode,ReceiveMail receiveMail ) {
        ArrayList<ResultMail> ret = null ;
        try {
            ret = receiveMail( mode,receiveMail ) ;
        } catch( Exception e ) {
            LOG.error( "receiveMailError",e ) ;
            ret = null ;
        }
        return ret ;
    }
    
    /**
     * メールを受信.
     * <BR><BR>
     * メールを受信します.
     * <BR>
     * @param mode [true]の場合、1件以上メールが存在する場合、１件だけを受信します.
     * @param receiveMail 受信メール情報を設定します.
     * @return ArrayList<ResultMail> 受信結果内容が返されます.
     * @exception Exception 例外.
     */
    public static ArrayList<ResultMail> receiveMail( boolean mode,ReceiveMail receiveMail )
        throws Exception {
        if( receiveMail == null || receiveMail.check() == false ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        
        final Properties props = new Properties();
        
        props.setProperty("mail.pop3.host", receiveMail.getHost() ) ;
        if( receiveMail.getPort() > 0 ) {
            props.setProperty("mail.pop3.port", String.valueOf( receiveMail.getPort() ) ) ;
        }
        
        if( receiveMail.getTimeout() > 0L ) {
            props.setProperty("mail.pop3.connectiontimeout", String.valueOf( receiveMail.getTimeout() ) ) ;
            props.setProperty("mail.pop3.timeout", String.valueOf( receiveMail.getTimeout() ) ) ;
        }
        
        if( receiveMail.isSsl() == true ) {
            props.setProperty("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory" ) ;
            props.setProperty("mail.pop3.socketFactory.fallback", "false" ) ;
            if( receiveMail.getPort() > 0 ) {
                props.setProperty("mail.pop3.socketFactory.port", String.valueOf( receiveMail.getPort() ) ) ;
            }
        }
        
        final Session session = Session.getInstance( props,
            new MailAuth( receiveMail.getUser(), receiveMail.getPasswd() ) ) ;
        
        //session.setDebug(true);
        
        Store store = null ;
        Folder folder = null ;
        try {
            store = session.getStore( "pop3" ) ;
            store.connect() ;
            
            folder = store.getFolder( "INBOX" ) ;
            folder.open( Folder.READ_ONLY ) ;
            
            Message[] messages = folder.getMessages() ;
            if( messages != null && messages.length > 0 ) {
                int len = messages.length ;
                ArrayList<ResultMail> ret = new ArrayList<ResultMail>() ;
                int cnt = 0 ;
                for( int i = 0 ; i < len ; i ++ ) {
                    if( cnt > 0 && mode == true ) {
                        break ;
                    }
                    ResultMail res = new ResultMail() ;
                    oneResult( res,1,messages[ i ] ) ;
                    if( res != null ) {
                        ret.add( res ) ;
                        cnt ++ ;
                    }
                    messages[ i ].setFlag( Flags.Flag.DELETED,receiveMail.isDelete() ) ;
                }
                return ret ;
            }
            else {
                return new ArrayList<ResultMail>() ;
            }
        } finally {
            try {
                folder.close( false ) ;
            } catch( Exception e ) {
            }
            try {
                store.close() ;
            } catch( Exception e ) {
            }
        }
    }
    
    /**
     * １つのメッセージ受信処理.
     */
    private static int oneResult( ResultMail out,int attCnt,Part part )
        throws Exception {
        String plus = "" ;
        boolean attachment = false ;
        if( part instanceof Message ) {
            setMessage( out,( Message )part ) ;
        }
        if( part.isMimeType( "text/plain" ) ) {
            if( out.getBody() != null ) {
                out.setBody( out.getBody() + ( String )part.getContent() ) ;
            }
            else {
                out.setBody( ( String )part.getContent() ) ;
            }
        }
        else if( part.isMimeType( "multipart/*" ) ) {
            Multipart multi = ( Multipart )part.getContent() ;
            int len = multi.getCount() ;
            for (int i = 0; i < len; i++) {
                attCnt = oneResult( out,attCnt,multi.getBodyPart( i ) ) ;
            }
        }
        else if( part.isMimeType( "message/rfc822" ) ) {
            attCnt = oneResult( out,attCnt,( Part )part.getContent() ) ;
        }
        else if( part.isMimeType( "text/html" ) ) {
            plus = ".html" ;
            attachment = true ;
        }
        else {
            attachment = true ;
        }
        
        if( attachment == true ) {
            String disp = part.getDisposition();
            if (disp == null || disp.equalsIgnoreCase(Part.ATTACHMENT)) {
                String fileName = part.getFileName();
                if (fileName != null || ( fileName = fileName.trim() ).length() <= 0 ) {
                    fileName =  MimeUtility.decodeText(fileName);
                } else {
                    attCnt ++ ;
                    fileName = UNKNOWN_ATTACHMENT + attCnt + plus ;
                }
                try {
                    ByteArrayOutputStream buf = new ByteArrayOutputStream() ;
                    InputStream in = part.getInputStream() ;
                    byte[] bin = new byte[ 8192 ] ;
                    for( ;; ) {
                        int len = in.read( bin ) ;
                        if( len <= 0 ) {
                            if( len <= -1 ) {
                                break ;
                            }
                        }
                        else {
                            buf.write( bin,0,len ) ;
                        }
                    }
                    bin = null ;
                    buf.flush() ;
                    byte[] att = buf.toByteArray() ;
                    buf.close() ;
                    in.close() ;
                    ArrayList<ResultAttached> lst = null ;
                    if( ( lst = out.getAttached() ) == null ) {
                        lst = new ArrayList<ResultAttached>() ;
                        out.setAttached( lst ) ;
                    }
                    ResultAttached ra = new ResultAttached() ;
                    ra.setAttached( att ) ;
                    ra.setFileName( fileName ) ;
                    lst.add( ra ) ;
                } catch( Exception e ) {
                    throw e ;
                }
            }
        }
        return attCnt ;
    }
    
    /**
     * メッセージ設定.
     */
    private static void setMessage( ResultMail res,Message message )
        throws Exception {
        res.setDate( message.getSentDate() ) ;
        res.setSubject( message.getSubject() ) ;
        Address[] addr = null ;
        if( ( addr = message.getFrom() ) != null ) {
            int len = addr.length ;
            ArrayList<String> froms = new ArrayList<String>() ;
            for( int i = 0 ; i < len ; i ++ ) {
                InternetAddress internetAddr = ( InternetAddress )addr[ i ] ;
                froms.add( internetAddr.getAddress() ) ;
            }
            res.setFrom( froms ) ;
        }
        if( ( addr = message.getRecipients( Message.RecipientType.TO ) ) != null ) {
            int len = addr.length ;
            ArrayList<String> tos = new ArrayList<String>() ;
            for( int i = 0 ; i < len ; i ++ ) {
                InternetAddress internetAddr = ( InternetAddress )addr[ i ] ;
                tos.add( internetAddr.getAddress() ) ;
            }
            res.setTo( tos ) ;
        }
    }
    
}

