package jp.co.headwaters.webappos.controller.fuction;

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

import javax.mail.internet.AddressException;

import jp.co.headwaters.webappos.controller.cache.bean.AbstractFunctionBean;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

/**
 * メール送信クラス
 */
public class SendMailFunction extends AbstractFunction {

	protected final Pattern SUBJECT_PATTERN = Pattern.compile("^Subject:(.+)?(\\r\\n|\\r)"); //$NON-NLS-1$
	protected final Pattern TO_PATTERN = Pattern.compile("^To:(.+)?(\\r\\n|\\r)"); //$NON-NLS-1$
	protected final Pattern CC_PATTERN = Pattern.compile("^Cc:(.+)?(\\r\\n|\\r)"); //$NON-NLS-1$
	protected final Pattern BCC_PATTERN = Pattern.compile("^Bcc:(.+)?(\\r\\n|\\r)"); //$NON-NLS-1$

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

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

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

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

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

	@Override
	protected void initialize() {
		super.initialize();

		// TODO;別メソッド
//		// system_constantからメール関連の定義情報を取得する
//		Map<String, SystemConstant> mailConstants = this._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();
//		}
//		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_PORT)) {
//			port = mailConstants.get(SystemConstantKeys.KEY_MAIL_PORT).getValue();
//		}
//		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_USERNAME)) {
//			userName = mailConstants.get(SystemConstantKeys.KEY_MAIL_USERNAME).getValue();
//		}
//		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_PASSWORD)) {
//			password = mailConstants.get(SystemConstantKeys.KEY_MAIL_PASSWORD).getValue();
//		}
//		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_CONNECTION_TIMEOUT)) {
//			connectionTimeout = mailConstants.get(SystemConstantKeys.KEY_MAIL_CONNECTION_TIMEOUT).getValue();
//		}
//		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_TIMEOUT)) {
//			timeout = mailConstants.get(SystemConstantKeys.KEY_MAIL_TIMEOUT).getValue();
//		}
//		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_FROM_ADDRESS)) {
//			this._mailFromAddress = mailConstants.get(SystemConstantKeys.KEY_MAIL_FROM_ADDRESS).getValue();
//		}
//		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_FROM_NAME)) {
//			this._mailFromName = mailConstants.get(SystemConstantKeys.KEY_MAIL_FROM_NAME).getValue();
//		}
//		if (mailConstants.containsKey(SystemConstantKeys.KEY_MAIL_SITE_NAME)) {
//			this._siteName = mailConstants.get(SystemConstantKeys.KEY_MAIL_SITE_NAME).getValue();
//		}
//
//		// メーラを設定する
//		this._mailer = new JavaMailer();
//		this._mailer.setCharset(charset);
//		this._mailer.setServerInfo(host, port, userName, password);
//		this._mailer.setTimeout(Integer.valueOf(connectionTimeout), Integer.valueOf(timeout));
//
//		// freeMarkerコンフィグを設定する
//		this._configuration = new Configuration();
//		// TODO:メールテンプレートPATH
////		this._configuration.setDirectoryForTemplateLoading(new File(PropertyUtils.getProperty(ControllerConstants.PROPERTY_KEY_MAIL_TEMPLATE_PATH)));
//		this._configuration.setDefaultEncoding("utf-8"); //$NON-NLS-1$
//
//		// freeMarkerで使うハッシュマップを初期化する
//		this._params = new HashMap<String, Object>();
	}


	@Override
	protected void execute(AbstractFunctionBean function) throws Exception {
		// TODO 自動生成されたメソッド・スタブ
	}

//	private boolean executeSendMailFunction(AbstractFunctionBean function) throws Exception {
//	try {
//		SendMailFunction sendMailFunction = null;
//		Map<String, Object> mailParams = new HashMap<String, Object>();
//
//		// テンプレート名を取得する
//		String templateName = function.getMethod();
//
//		if (StringUtils.isEmpty(function.getToIdColumnName())
//				|| StringUtils.isEmpty(function.getToAddressColumnName())) {
//			// system_constantに定義されたアドレスに送信する
//			sendMailFunction = new SendMailFunction();
//			addAddress(function, sendMailFunction);
//
//			createMailParamMap(function.getResult(), mailParams);
//			sendMailFunction.sendMail(templateName, mailParams);
//		} else {
//			String[] ids = _requestParams.get(function.getResult() + ControllerConstants.REQUEST_PARAM_NAME_DELIMITER + "col" + ControllerConstants.REQUEST_PARAM_NAME_DELIMITER + function.getToIdColumnName());
//			if (ids == null || ids.length == 0) {
//				// エラー
//				return false;
//			}
//
//			// address_colの内容
//			String[] addressColumnInfo = function.getToAddressColumnName().split("\\.");
//			String addressTalbeName = addressColumnInfo[0];
//			String addressColumnName = addressColumnInfo[1];
//
//			List<Object> idList = new ArrayList<Object>();
//			for (String val1 : ids) {
//				for (String val2 : Arrays.asList(val1.split(","))) {
//					if (StringUtils.isEmpty(val2)) {
//						continue;
//					}
//					idList.add(convertDataType(val2, _schemaColumnCache.getSchemaColumn("id")));
//				}
//			}
//
//			String mapperName = getMapperName(CrudEnum.CRUD_SELECT_BY_EXAMPLE.getMethod(), addressTalbeName);
//			CommonExample example = new CommonExample();
//			Criteria criteria = example.createCriteria();
//			// TODO:PKのカラム名を固定にしたいな
//			// TODO:テーブルコメントに関連書いて取得かな。。
//			criteria.andIn("id", idList);
//
//			// 送信アドレスを取得する
//			SqlSession session = null;
//			try {
//				session = DaoUtils.getSqlSessionFactory().openSession();
//				List<?> entityList = session.selectList(mapperName, example);
//				for (int i = 0; i < entityList.size(); i++) {
//					mailParams = new HashMap<String, Object>();
//					Map<String, Object> map = convertEntityToMap(entityList.get(i), false);
//					String address = (String) map.get(addressColumnName);
//					if (!StringUtils.isEmpty(address)) {
//						sendMailFunction = new SendMailFunction();
//						// 送信アドレスを設定する
//						addAddress(function, sendMailFunction);
//						sendMailFunction.addTo(address);
//						// メールテンプレートにbindするmapを生成する
//						createMailParamMap(function.getResult(), mailParams);
//						mailParams.put(addressTalbeName, map);
//						sendMailFunction.sendMail(templateName, mailParams);
//					}
//				}
//			} finally {
//				if (session != null) {
//					session.close();
//				}
//			}
//		}
//	} catch (Exception e) {
//		// TODO:メール送信に失敗。ログを残さない。
//		return false;
//	}
//
//	if (!StringUtils.isEmpty(function.getTarget())){
//		// メール送信情報を登録する
//		AbstractFunctionBean insFunction = new AbstractFunctionBean();
//		insFunction.setType(FunctionEnum.FUNCTION_CRUD.getFunctionName());
//		insFunction.setMethod(CrudEnum.CRUD_INSERT.getMethod());
//		insFunction.setTarget(function.getTarget());
//		insFunction.setResult(function.getResult());
//		return executeCrudFunction(insFunction);
//	}
//
//	return true;
//}
//
//private void createMailParamMap(String resultName, Map<String, Object> mailParams){
//	for (Map.Entry<String, String[]> param : _requestParams.entrySet()) {
//		String[] values = param.getValue();
//		String[] key = param.getKey().split(ControllerConstants.REQUEST_PARAM_NAME_DELIMITER, 3);
//		String daoParamKey = null;
//		Object daoParamValue = null;
//
//		if (key[0].equals(resultName)) {
//			if (ControllerConstants.REQUEST_PARAM_NAME_CRUD_COLUMN.equals(key[1])) {
//				daoParamKey =  key[2];
//				daoParamValue = StringUtils.join(values, ",");
//				if (!mailParams.containsKey(daoParamKey)) {
//					mailParams.put(daoParamKey, daoParamValue);
//				}
//			}
//		}
//	}
//}
//
//private void addAddress(AbstractFunctionBean function, SendMailFunction sendMailFunction) throws AddressException, UnsupportedEncodingException {
//	if (!FunctionEnum.FUNCTION_SENDMAIL.getFunctionName().equals(function.getType())){
//		return;
//	}
//
//	SystemConstant systemConstant = null;
//	if (function.getToList() != null) {
//		for (String value : function.getToList()) {
//			if (StringUtils.isEmpty(value)) {
//				continue;
//			}
//			String[] values = value.split("\\.");
//			if (values.length == 1){
//				systemConstant = _systemConstantCache.getSystemConstant(values[0]);
//			} else {
//				systemConstant = _systemConstantCache.getSystemConstant(values[0], values[1]);
//			}
//			// system_constantに定義されたアドレスを取得する
//			if (systemConstant != null) {
//				if (!StringUtils.isEmpty(systemConstant.getValue())) {
//					sendMailFunction.addTo(systemConstant.getValue());
//				}
//			}
//		}
//	}
//
//	if (function.getCcList() != null) {
//		for (String value : function.getCcList()) {
//			if (StringUtils.isEmpty(value)) {
//				continue;
//			}
//			String[] values = value.split("\\.");
//			if (values.length == 1){
//				systemConstant = _systemConstantCache.getSystemConstant(values[0]);
//			} else {
//				systemConstant = _systemConstantCache.getSystemConstant(values[0], values[1]);
//			}
//			if (systemConstant != null) {
//				if (!StringUtils.isEmpty(systemConstant.getValue())) {
//					sendMailFunction.addCc(systemConstant.getValue());
//				}
//			}
//		}
//	}
//
//	if (function.getBccList() != null) {
//		for (String value : function.getBccList()) {
//			if (StringUtils.isEmpty(value)) {
//				continue;
//			}
//			String[] values = value.split("\\.");
//			if (values.length == 1){
//				systemConstant = _systemConstantCache.getSystemConstant(values[0]);
//			} else {
//				systemConstant = _systemConstantCache.getSystemConstant(values[0], values[1]);
//			}
//
//			if (systemConstant != null) {
//				if (!StringUtils.isEmpty(systemConstant.getValue())) {
//					sendMailFunction.addBcc(systemConstant.getValue());
//				}
//			}
//		}
//	}
//}
//
	/**
	 * DTOをハッシュマップに展開しメールを送信します
	 *
	 * @param templateName テンプレート名
	 * @param dto テンプレートへassignするDTO
	 * @throws Exception 例外
	 */
	public void sendMail(String templateName, Map<String, Object> obj) throws Exception {

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

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

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

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

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

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

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

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

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

	// freeMarker用メソッド
	///////////////////////////////////////////////////////
	/**
	 * ハッシュマップに値を設定します
	 * @param key
	 * @param value
	 */
	public void assign(String key, Object value) {
		this._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"); //$NON-NLS-1$
		return this._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 = this.SUBJECT_PATTERN.matcher(message);
		if (m.find()) {
			this._mailer.setSubject(m.group(1).trim());
			message = m.replaceFirst(""); //$NON-NLS-1$
		}
		// Toを取得
		m = this.TO_PATTERN.matcher(message);
		if (m.find()) {
			addTo(m.group(1));
			message = m.replaceFirst(""); //$NON-NLS-1$
		}
		// Ccを取得
		m = this.CC_PATTERN.matcher(message);
		if (m.find()) {
			addCc(m.group(1));
			message = m.replaceFirst(""); //$NON-NLS-1$
		}
		// Bccを取得
		m = this.BCC_PATTERN.matcher(message);
		if (m.find()) {
			addBcc(m.group(1));
			message = m.replaceFirst(""); //$NON-NLS-1$
		}

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

}
