package jp.co.headwaters.webappos.fuction;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.internet.AddressException;

import jp.co.headwaters.webappos.cache.SystemConstantCache;
import jp.co.headwaters.webappos.common.AppConstants;
import jp.co.headwaters.webappos.common.JavaMailer;
import jp.co.headwaters.webappos.common.SystemConstantKeys;
import jp.co.headwaters.webappos.dao.bean.SystemConstant;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

/**
 * メール送信クラス
 */
public class SendMailFunction {
	protected final Pattern SUBJECT_PATTERN = Pattern.compile("^Subject:(.+)?(\\r\\n|\\r)");
	protected final Pattern TO_PATTERN = Pattern.compile("^To:(.+)?(\\r\\n|\\r)");
	protected final Pattern CC_PATTERN = Pattern.compile("^Cc:(.+)?(\\r\\n|\\r)");
	protected final Pattern BCC_PATTERN = Pattern.compile("^Bcc:(.+)?(\\r\\n|\\r)");

	/** Mailer本体 */
	protected JavaMailer _mailer;

	/** FROMアドレス初期値 */
	protected String _mailFromAddress;
	/** FROM名初期値 */
	protected String _mailFromName;

	// 共通テンプレートプロパティ
	/** サイト名 */
	protected String _siteName;

	/** ハッシュマップ */
	protected Map<String, Object> _params;

	/** freeMarkerコンフィグ */
	protected Configuration _configuration;

	/** 定数情報 */
	private SystemConstantCache _systemConstantCache;

	public SendMailFunction() throws IOException {
		if (_mailer == null) {
			_systemConstantCache = SystemConstantCache.getInstance();
			initialize();
		}
	}

	/**
	 * 初期化
	 * @throws IOException
	 */
	public void initialize() throws IOException {

		// system_constantからメール関連の定義情報を取得する
		Map<String, SystemConstant> mailConstants = _systemConstantCache.getSystemConstantMap(SystemConstantKeys.CATEGORY_MAIL);
		String charset = null;
		String host = null;
		String port = null;
		String userName = null;
		String password = null;
		String connectionTimeout = null;
		String timeout = null;
		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_CHARSET)){
			charset = mailConstants.get(SystemConstantKeys.KEY_MAIL_CHARSET).getValue();
		} else {
			// TODO:エラー
		}
		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_HOST)){
			host = mailConstants.get(SystemConstantKeys.KEY_MAIL_HOST).getValue();
		} else {
		}
		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_PORT)){
			port = mailConstants.get(SystemConstantKeys.KEY_MAIL_PORT).getValue();
		} else {
		}
		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_USERNAME)){
			userName = mailConstants.get(SystemConstantKeys.KEY_MAIL_USERNAME).getValue();
		} else {
		}
		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_PASSWORD)){
			password = mailConstants.get(SystemConstantKeys.KEY_MAIL_PASSWORD).getValue();
		} else {
		}
		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_CONNECTION_TIMEOUT)){
			connectionTimeout = mailConstants.get(SystemConstantKeys.KEY_MAIL_CONNECTION_TIMEOUT).getValue();
		} else {
		}
		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_TIMEOUT)){
			timeout = mailConstants.get(SystemConstantKeys.KEY_MAIL_TIMEOUT).getValue();
		} else {
		}
		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_FROM_ADDRESS)){
			_mailFromAddress = mailConstants.get(SystemConstantKeys.KEY_MAIL_FROM_ADDRESS).getValue();
		} else {
		}
		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_FROM_NAME)){
			_mailFromName = mailConstants.get(SystemConstantKeys.KEY_MAIL_FROM_NAME).getValue();
		} else {
		}
		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_SITE_NAME)){
			_siteName = mailConstants.get(SystemConstantKeys.KEY_MAIL_SITE_NAME).getValue();
		} else {
		}

		// メーラを設定する
		_mailer = new JavaMailer();
		_mailer.setCharset(charset);
		_mailer.setServerInfo(host, port, userName, password);
		_mailer.setTimeout(Integer.valueOf(connectionTimeout), Integer.valueOf(timeout));

		// freeMarkerコンフィグを設定する
		_configuration = new Configuration();
		_configuration.setDirectoryForTemplateLoading(new File(AppConstants.MAIL_TEMPLATE_PATH));
		_configuration.setDefaultEncoding("utf-8");

		// freeMarkerで使うハッシュマップを初期化する
		_params = new HashMap<String, Object>();
	}

	/**
	 * DTOをハッシュマップに展開しメールを送信します
	 *
	 * @param templateName テンプレート名
	 * @param dto テンプレートへassignするDTO
	 * @throws Exception 例外
	 */
	public void sendMail(String templateName, Map<String, Object> obj) throws Exception  {

		// デフォルト送信者情報をセットします
		_mailer.setFrom(_mailFromAddress, _mailFromName);

		// プロパティの情報をハッシュマップにセットします
		assign("siteName", _siteName);

		// DTOをハッシュマップへ展開します
		// ※assignされているマップと衝突した際は上書きされるので注意
		_params.putAll(obj);

		// テンプレートを解析します
		fetch(templateName, _params);

		// 送信します
		_mailer.send();
		_mailer = null;
	}

	/**
	 * Fromを設定します
	 * @param address 送信者アドレス
	 * @param name 送信者名
	 * @throws AddressException
	 */
	public void setFrom(String address, String name) throws UnsupportedEncodingException, AddressException {
		_mailer.setFrom(address, name);
	}

	/**
	 * Toを追加します
	 * @param addresses 送信先アドレス<br>
	 * 表示名を指定する際は<>で囲む<br>
	 * 複数を一度に登録する際はカンマ(,)区切り
	 * @throws UnsupportedEncodingException
	 * @throws AddressException
	 */
	public void addTo(String addresses) throws AddressException, UnsupportedEncodingException {
		_mailer.addTo(addresses);
	}

	/**
	 * Ccを追加します
	 * @param addresses ccアドレス<br>
	 * 表示名を指定する際は<>で囲む<br>
	 * 複数を一度に登録する際はカンマ(,)区切り
	 * @throws UnsupportedEncodingException
	 * @throws AddressException
	 */
	public void addCc(String addresses) throws AddressException, UnsupportedEncodingException {
		_mailer.addCc(addresses);
	}

	/**
	 * BCcを追加します
	 * @param addresses bccアドレス<br>
	 * 表示名を指定する際は<>で囲む<br>
	 * 複数を一度に登録する際はカンマ(,)区切り
	 * @throws UnsupportedEncodingException
	 * @throws AddressException
	 */
	public void addBcc(String addresses) throws AddressException, UnsupportedEncodingException {
		_mailer.addBcc(addresses);
	}

	// freeMarker用メソッド
	///////////////////////////////////////////////////////
	/**
	 * ハッシュマップに値を設定します
	 * @param key
	 * @param value
	 */
	public void assign(String key, Object value) {
		_params.put(key, value);
	}

	/**
	 * freeMarkerテンプレートファイルを取得する
	 *
	 * @param templateName テンプレート名
	 * @return テンプレートファイル
	 * @throws IOException
	 */
	protected Template getTemplate(String templateName) throws IOException {
		// テンプレートファイルを取得
		StringBuilder sb = new StringBuilder(templateName);
		sb.append(".ftl");
		return _configuration.getTemplate(sb.toString());
	}

	/**
	 * freeMarkerからテンプレート情報を取得してフィールドに設定する
	 *
	 * @param templateName テンプレート名
	 * @param map freeMakerへ送るハッシュマップ
	 * @throws IOException
	 * @throws TemplateException
	 * @throws AddressException
	 * @throws Exception
	 */
	protected void fetch(String templateName, Map<String, Object> map) throws IOException, TemplateException, AddressException {
		Template template = getTemplate(templateName);
		Matcher m;

		StringWriter out = new StringWriter();
		template.process(map, out);
		String message = out.toString();

		// Subjectを取得
		m = SUBJECT_PATTERN.matcher(message);
		if (m.find()) {
			_mailer.setSubject(m.group(1).trim());
			message = m.replaceFirst("");
		}
		// Toを取得
		m = TO_PATTERN.matcher(message);
		if (m.find()) {
			addTo(m.group(1));
			message = m.replaceFirst("");
		}
		// Ccを取得
		m = CC_PATTERN.matcher(message);
		if (m.find()) {
			addCc(m.group(1));
			message = m.replaceFirst("");
		}
		// Bccを取得
		m = BCC_PATTERN.matcher(message);
		if (m.find()) {
			addBcc(m.group(1));
			message = m.replaceFirst("");
		}

		// 先頭と末尾の改行をトリムする
		m = Pattern.compile("^(\\r\\n|\\r)+|(\\r\\n|\\r)+$").matcher(message);
		if (m.find()) {
			message = m.replaceAll("");
		}
		_mailer.setBody(message);
    }

}
