/*
 * blanco Framework
 * Copyright (C) 2004-2007 IGA Tosiki
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */
package blanco.mail.core;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.Properties;

import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeUtility;

import blanco.commons.util.BlancoStringUtil;
import blanco.mail.core.resourcebundle.BlancoMailCoreMessageResourceBundle;
import blanco.mail.core.valueobject.BlancoMailAddress;
import blanco.mail.core.valueobject.BlancoMailSetting;

/**
 * メール送信、メール受信で共通的に利用する機能。
 * 
 * 内部的な共通クラスのため、非publicクラスとします。
 * 
 * @author IGA Tosiki
 */
class BlancoMailCoreUtil {
    /**
     * コンストラクタは隠します。
     */
    private BlancoMailCoreUtil() {
    }

    /**
     * メールサーバ接続のためのセッションオブジェクト・インスタンスを取得します。
     * 
     * 必要なユーザ名・パスワードの設定についても、このメソッド内で実施されます。
     * 
     * @param argSetting
     *            blancoMailの各種設定情報をあらわすバリューオブジェクト。サーバ接続設定や送受信時のデフォルト値などが格納されます。(注意)このクラスのインスタンスは
     *            BlancoMailSettingFactory クラスから取得してください。
     * @param argProps
     *            接続のために利用するプロパティ設定値。JavaMail APIの仕様によるもの。
     * @return セッションオブジェクト。
     */
    static Session getSendSession(final BlancoMailSetting argSetting,
            final Properties argProps) {
        return Session.getInstance(argProps, new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(argSetting.getSendUserName(),
                        argSetting.getSendPassword());
            }
        });
    }

    /**
     * メールサーバ接続のためのセッションオブジェクト・インスタンスを取得します。
     * 
     * 必要なユーザ名・パスワードの設定についても、このメソッド内で実施されます。
     * 
     * @param argSetting
     *            blancoMailの各種設定情報をあらわすバリューオブジェクト。サーバ接続設定や送受信時のデフォルト値などが格納されます。(注意)このクラスのインスタンスは
     *            BlancoMailSettingFactory クラスから取得してください。
     * @param argProps
     *            接続のために利用するプロパティ設定値。JavaMail APIの仕様によるもの。
     * @return セッションオブジェクト。
     */
    static Session getReceiveSession(final BlancoMailSetting argSetting,
            final Properties argProps) {
        return Session.getInstance(argProps, new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(argSetting
                        .getReceiveUserName(), argSetting.getReceivePassword());
            }
        });
    }

    /**
     * セッションログを開始して、その出力ストリームを取得します。
     * 
     * このメソッドが呼び出された場合には、常にセッションデバッグをONに設定します。[Session.setDebug(true)]
     * しかし、そのセッションデバッグは 常に 内部的に保持される (そして戻り値として戻される)
     * ByteArrayOutputStreamにリダイレクトされます。標準出力にはセッションデバッグ情報は出力されません。
     * ByteArrayOutputStreamにリダイレクトされたセッションデバッグ内容をセッションログファイルに出力するかどうかは、呼び出し元で決定することです。
     * 
     * なお、メソッドの戻り値として戻るのは Sessionオブジェクトの セッションデバッグである PrintStreamではなく、内部的な
     * ByteArrayOutputStream オブジェクトです。
     * 
     * @param argSetting
     *            blancoMailの各種設定情報をあらわすバリューオブジェクト。サーバ接続設定や送受信時のデフォルト値などが格納されます。(注意)このクラスのインスタンスは
     *            BlancoMailSettingFactory クラスから取得してください。
     * @param argSession
     *            JavaMail APIのセッションオブジェクト。
     * @return 内部的に結び付けに利用したPrintStreamに与えたByteArrayOutputStreamが戻ります。null以外が戻ってきた場合には、最後にログファイルへの書き込み処理を実施してください。
     * @throws IOException
     *             入出力例外が発生した場合。
     */
    static ByteArrayOutputStream beginSessionLogOutputStream(
            final BlancoMailSetting argSetting, final Session argSession)
            throws IOException {
        // 常にセッションデバッグをバイトストリームにリダイレクトします。
        final ByteArrayOutputStream resultOutStream = new ByteArrayOutputStream(
                8192);

        final PrintStream logStream = new PrintStream(resultOutStream, false,
                argSetting.getSendEncoding());
        argSession.setDebugOut(logStream);

        // 標準出力へのデバッグ出力ON設定は ここで実施します。
        // ※このタイミングでデバッグ出力ON設定実行するのには理由があります。
        // ログストリームをセットしてからデバッグをONにしないと、標準出力に１行バージョン情報が出てしまうのです。
        // そのため、この場所にデバッグ出力ON設定の記述が実装されているのは必要なことです。
        argSession.setDebug(true);

        return resultOutStream;
    }

    /**
     * BlancoMailAddress から InternetAddress を作成します。
     * 
     * @param argSetting
     *            blancoMailの各種設定情報をあらわすバリューオブジェクト。サーバ接続設定や送受信時のデフォルト値などが格納されます。(注意)このクラスのインスタンスは
     *            BlancoMailSettingFactory クラスから取得してください。
     * @param argAddress
     *            blancoMailCore API のアドレスオブジェクト。
     * @return JavaMail API のインターネットアドレスオブジェクト。
     * @throws UnsupportedEncodingException
     *             サポートされない文字エンコーディングが与えられた場合。具体的にはリソースバンドルの文字エンコーディングの指定ミスなどの場合に発生することが考えられます。
     * @throws AddressException
     *             アドレスに関する例外が発生した場合。
     */
    static InternetAddress toInternetAddress(
            final BlancoMailSetting argSetting,
            final BlancoMailAddress argAddress)
            throws UnsupportedEncodingException, AddressException {
        if (argAddress.getPersonal() != null) {
            // ユーザ名がある場合。
            return new InternetAddress(argAddress.getAddress(), argAddress
                    .getPersonal(), argSetting.getSendEncoding());
        } else {
            // ユーザ名が無い場合。
            return new InternetAddress(argAddress.getAddress());
        }
    }

    /**
     * InternetAddress から BlancoMailAddress を作成します。
     * 
     * @param argAddress
     *            JavaMail API のインターネットアドレスオブジェクト。
     * @return blancoMailCore API のアドレスオブジェクト。
     */
    static BlancoMailAddress toBlancoMailAddress(
            final InternetAddress argAddress) {
        final BlancoMailAddress address = new BlancoMailAddress();
        address.setAddress(argAddress.getAddress());
        if (argAddress.getPersonal() != null
                && argAddress.getPersonal().length() > 0) {
            try {
                // ユーザ名をセット。
                address.setPersonal(MimeUtility.decodeText(argAddress
                        .getPersonal()));
            } catch (UnsupportedEncodingException e) {
                // ユーザ名のデコードに失敗した場合、その例外は無視することとします。
                // これは、ユーザ名のデコードが失敗したとしても、本質的な処理には影響ないものと考えているからです。

                // TODO ログのシステムが整備されたら、この例外内容はログに出力するように変更してください。それまでは無視します。
            }
        }

        return address;
    }

    /**
     * メール送受信で共通の BlancoMailSettingの設定内容チェック。
     * 
     * @param argSetting
     *            blancoMailの各種設定情報をあらわすバリューオブジェクト。サーバ接続設定や送受信時のデフォルト値などが格納されます。(注意)このクラスのインスタンスは
     *            BlancoMailSettingFactory クラスから取得してください。
     */
    static void checkSetting(final BlancoMailSetting argSetting) {
        if (argSetting == null) {
            throw new IllegalArgumentException(
                    "BlancoMailCoreUtil#checkSetting: メソッドのパラメータ(argSetting)に nullが渡されました。このオブジェクトは nullであってはなりません。");
        }

        final BlancoMailCoreMessageResourceBundle msg = new BlancoMailCoreMessageResourceBundle();

        // ログディレクトリ関連のチェック
        // SESSION_LOG_DIRECTORY_ERROR
        if (BlancoStringUtil.null2Blank(
                argSetting.getSessionLogDirectoryError()).length() > 0) {
            final File fileCheck = new File(argSetting
                    .getSessionLogDirectoryError());
            if (fileCheck.exists() == false) {
                if (fileCheck.mkdirs() == false) {
                    throw new IllegalArgumentException(msg
                            .getMbmcz101(argSetting
                                    .getSessionLogDirectoryError()));
                }
            } else {
                if (fileCheck.isDirectory() == false) {
                    throw new IllegalArgumentException(msg
                            .getMbmcz102(argSetting
                                    .getSessionLogDirectoryError()));
                }
            }
        }

        // SESSION_LOG_DIRECTORY_SUCCESS
        if (BlancoStringUtil.null2Blank(
                argSetting.getSessionLogDirectorySuccess()).length() > 0) {
            final File fileCheck = new File(argSetting
                    .getSessionLogDirectorySuccess());
            if (fileCheck.exists() == false) {
                if (fileCheck.mkdirs() == false) {
                    throw new IllegalArgumentException(msg
                            .getMbmcz103(argSetting
                                    .getSessionLogDirectorySuccess()));
                }
            } else {
                if (fileCheck.isDirectory() == false) {
                    throw new IllegalArgumentException(msg
                            .getMbmcz104(argSetting
                                    .getSessionLogDirectorySuccess()));
                }
            }
        }

        if (argSetting.getTestMode()) {
            // テストモードの場合にのみディレクトリ設定値をチェックします。
            if (BlancoStringUtil.null2Blank(
                    argSetting.getTestModeLogDirectorySend()).length() > 0) {
                final File fileCheck = new File(argSetting
                        .getTestModeLogDirectorySend());
                if (fileCheck.exists() == false) {
                    if (fileCheck.mkdirs() == false) {
                        throw new IllegalArgumentException(msg
                                .getMbmcz105(argSetting
                                        .getTestModeLogDirectorySend()));
                    }
                } else {
                    if (fileCheck.isDirectory() == false) {
                        throw new IllegalArgumentException(msg
                                .getMbmcz106(argSetting
                                        .getTestModeLogDirectorySend()));
                    }
                }
            }

            if (BlancoStringUtil.null2Blank(
                    argSetting.getTestModeLogDirectoryReceive()).length() > 0) {
                final File fileCheck = new File(argSetting
                        .getTestModeLogDirectoryReceive());
                if (fileCheck.exists() == false) {
                    if (fileCheck.mkdirs() == false) {
                        throw new IllegalArgumentException(msg
                                .getMbmcz107(argSetting
                                        .getTestModeLogDirectoryReceive()));
                    }
                } else {
                    if (fileCheck.isDirectory() == false) {
                        throw new IllegalArgumentException(msg
                                .getMbmcz108(argSetting
                                        .getTestModeLogDirectoryReceive()));
                    }
                }
            }
        }
    }
}
