package jp.co.headwaters.webappos.action;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.mail.internet.AddressException;

import jp.co.headwaters.webappos.cache.ActionCache;
import jp.co.headwaters.webappos.cache.SchemaColumnCache;
import jp.co.headwaters.webappos.cache.SystemConstantCache;
import jp.co.headwaters.webappos.cache.bean.ActionBean;
import jp.co.headwaters.webappos.cache.bean.ConditionBean;
import jp.co.headwaters.webappos.cache.bean.ExecuteBean;
import jp.co.headwaters.webappos.cache.bean.FunctionBean;
import jp.co.headwaters.webappos.cache.bean.PagerBean;
import jp.co.headwaters.webappos.cache.bean.ResultBean;
import jp.co.headwaters.webappos.common.AppConstants;
import jp.co.headwaters.webappos.common.PagingUtility;
import jp.co.headwaters.webappos.common.WebAppOSUtils;
import jp.co.headwaters.webappos.common.enumation.CrudEnum;
import jp.co.headwaters.webappos.common.enumation.DataTypeEnum;
import jp.co.headwaters.webappos.common.enumation.FunctionEnum;
import jp.co.headwaters.webappos.dao.DaoExample;
import jp.co.headwaters.webappos.dao.DaoExample.Criteria;
import jp.co.headwaters.webappos.dao.DaoUtil;
import jp.co.headwaters.webappos.dao.bean.SchemaColumn;
import jp.co.headwaters.webappos.dao.bean.SystemConstant;
import jp.co.headwaters.webappos.fuction.SendMailFunction;
import jp.co.headwaters.webappos.model.AbstractEntity;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class GenericAction extends ActionSupport {

	private static final Log logger = LogFactory.getLog(GenericAction.class);

	/** リクエストパラメータを保持するMap */
	private Map<String, String[]> _requestParams;
	/** 処理結果を保持するMap */
	private Map<String, Object> _resultMap = new HashMap<String, Object>();
	/** action名 */
	private String _actionName;
	/** action別実行情報 */
	private ActionBean _actionBean;
	/** スキーマカラム情報 */
	private SchemaColumnCache _schemaColumnCache;
	/** 定数情報 */
	private SystemConstantCache _systemConstantCache;

	public String execute() throws Exception {

		// ------------------------------------------------------------
		// 変数初期化
		// ------------------------------------------------------------
		// 実行、返却情報を保持する変数を初期化する
		ExecuteBean submitExecuteInfo = null;
		ExecuteBean loadExecuteInfo = null;
		ResultBean resultBean = null;

		// ------------------------------------------------------------
		// 各種キャッシュのインスタンスを取得する
		// ------------------------------------------------------------
		_schemaColumnCache = SchemaColumnCache.getInstance();
		_systemConstantCache = SystemConstantCache.getInstance();

		// ------------------------------------------------------------
		// リクエストパラメータを取得する
		// ------------------------------------------------------------
		_requestParams = new HashMap<String, String[]>(ServletActionContext.getRequest().getParameterMap());
		// リクエストパラメータを返却Mapにputする
		setResultMapFromRequestParams();
		// アクション名を取得する
		_actionName = ActionContext.getContext().getName();

		// ------------------------------------------------------------
		// TODO:リクエストパラメータのバリデーション
		// ------------------------------------------------------------
		// バリデーションエラーの場合は、resultNameに「_error」を付与する
//			if ("complete".equals(resultName)){
//				addActionError("名前を入力してください");
//				return resultName + "_error";
//			}

		// TODO:初期値設定

		// ------------------------------------------------------------
		// 実行情報および遷移先名を取得する
		// ------------------------------------------------------------
		// form識別子が存在した場合、submitされたと判断する
		_actionBean = ActionCache.getInstance().getActionMap().get(_actionName);
		if (_actionBean == null) {
			// TODO:エラー画面へ遷移する
			return "success";
		}

		String formId = null;
		String resultName = null;
		String jspFilePath = null;
		// form識別子を取得する
		if (_requestParams != null && _requestParams.get("formId") != null){
			formId = _requestParams.get("formId")[0];
		}
		if (!StringUtils.isEmpty(formId)) {
			submitExecuteInfo = _actionBean.getSubmitExecuteMap().get(formId);
			if (submitExecuteInfo == null){
				return AppConstants.DEFAULT_RESULT_NAME;
			}
			resultBean = submitExecuteInfo.getResult();
			resultName = resultBean.getName();
			jspFilePath = resultBean.getValue();
		} else if (_requestParams.get("result") != null
				&& _requestParams.get("result").length > 0
				&& !StringUtils.isEmpty(_requestParams.get("result")[0])) {
			// リクエストパラメータにresultNameが設定されていれば採用
			resultName = _requestParams.get("result")[0];
			jspFilePath = _actionBean.getDirectAccessMap().get(resultName);
		} else {
			resultName = AppConstants.DEFAULT_RESULT_NAME;
			jspFilePath = System.getProperty("file.separator") + AppConstants.JSP_DIR + _actionBean.getHtmlPath() + AppConstants.DEFAULT_RESULT_FILE_NAME;
		}
		if (StringUtils.isEmpty(resultName) || StringUtils.isEmpty(jspFilePath)) {
			// TODO:エラー画面へ遷移する
			return "success";
		}
		logger.debug("resultName:" + resultName);

		// ------------------------------------------------------------
		// 命令実行(submit時)
		// ------------------------------------------------------------
		if (submitExecuteInfo != null) {
			for (FunctionBean function : submitExecuteInfo.getFunctionList()) {
				function.getType();
				if (FunctionEnum.FUNCTION_CRUD.getFunctionName().equals(function.getType())) {
					executeCrudFunction(function);
				}else if (FunctionEnum.FUNCTION_SENDMAIL.getFunctionName().equals(function.getType())){
					executeSendMailFunction(function);
				}
			}
		}

		// ------------------------------------------------------------
		// 命令実行(load時)
		// ------------------------------------------------------------
		if (resultBean != null) {
		} else {
		}
		if (_actionBean != null
				&& _actionBean.getLoadExecuteMap() != null
				&& _actionBean.getLoadExecuteMap().get(jspFilePath) != null) {
			// 遷移先の画面にロード時に実行する処理があれば、実行する
			loadExecuteInfo = _actionBean.getLoadExecuteMap().get(jspFilePath);
			for (FunctionBean function : loadExecuteInfo.getFunctionList()) {
				function.getType();
				if (FunctionEnum.FUNCTION_CRUD.getFunctionName().equals(function.getType())) {
					executeCrudFunctionForLoad(function);
				}else if (FunctionEnum.FUNCTION_SENDMAIL.getFunctionName().equals(function.getType())){
					executeSendMailFunction(function);
				}
			}
		}

		return resultName;
	}

	private void setResultMapFromRequestParams() {
		for (Entry<String, String[]> entry : _requestParams.entrySet()) {
			String[] key = entry.getKey().split(AppConstants.REQUEST_PARAM_NAME_DELIMITER, 5);
			String[] values = entry.getValue();
			if (key.length < 2) {
				continue;
			}

			String resultName = key[0];
			String paramKind = key[1];

			if (!_resultMap.containsKey(resultName)){
				_resultMap.put(resultName, new HashMap<String ,Object>());
			}
			@SuppressWarnings("unchecked")
			HashMap<String ,Object> map = (HashMap<String, Object>) _resultMap.get(resultName);

			if (AppConstants.REQUEST_PARAM_NAME_CRUD_COLUMN.equals(paramKind)) {
				if (key.length != 3) {
					continue;
				}
				if (values.length > 1) {
					map.put(key[2], values);
				} else if (values.length == 1) {
					map.put(key[2], values[0]);
				}
			} else if (AppConstants.REQUEST_PARAM_NAME_CRUD_CONDITION.equals(paramKind)) {
				if (key.length != 5) {
					continue;
				}
				if (!map.containsKey("cond")) {
					map.put("cond", new HashMap<String ,Object>());
				}
				@SuppressWarnings("unchecked")
				HashMap<String ,Object> condMap = (HashMap<String, Object>) map.get("cond");
				if (values.length > 1) {
					condMap.put(key[2], values);
				} else if (values.length == 1) {
					condMap.put(key[2], values[0]);
				}
			}
		}
	}

	private boolean executeCrudFunction(FunctionBean function)
			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {

		String resultName = function.getResult();
		String mapperName = "jp.co.headwaters.webappos.mapper."
				+ WebAppOSUtils.getMapperName(function.getTarget())
				+ "."
				+ function.getMethod();

		Map<String, Object> daoParams = new HashMap<String, Object>();
		DaoExample example = new DaoExample();
		Object primaryKey = null;

		if (CrudEnum.CRUD_SELECT_BY_PRIMARYKEY.getType().equals(function.getMethod())
				|| CrudEnum.CRUD_SELECT_ALL_BY_PRIMARYKEY.getType().equals(function.getMethod())
				|| CrudEnum.CRUD_UPDATE_BY_PRIMARYKEY.getType().equals(function.getMethod())
				|| CrudEnum.CRUD_DELETE_BY_PRIMARYKEY.getType().equals(function.getMethod())) {
			// PK名は、テーブル情報から取得するようにする
			String[] pkey = _requestParams.get("id");
			if (pkey == null) {
				// TODO：パラメータ不正。エラー画面に遷移するようにする
				return false;
			}
			primaryKey = convertDataType(pkey[0], _schemaColumnCache.getSchemaColumn("id"));
		}

		if (CrudEnum.CRUD_SELECT_BY_EXAMPLE.getType().equals(function.getMethod())
				|| CrudEnum.CRUD_SELECT_ALL_BY_EXAMPLE.getType().equals(function.getMethod())
				|| CrudEnum.CRUD_INSERT.getType().equals(function.getMethod())
				|| CrudEnum.CRUD_UPDATE.getType().equals(function.getMethod())
				|| CrudEnum.CRUD_UPDATE_BY_PRIMARYKEY.getType().equals(function.getMethod())
				|| CrudEnum.CRUD_DELETE.getType().equals(function.getMethod())) {
			Criteria criteria = null;

			// リクエストパラメータから当該CURD向けのパラメータを抽出する
			for (Map.Entry<String, String[]> param : _requestParams.entrySet()) {
				String[] values = param.getValue();
				String[] key = param.getKey().split(AppConstants.REQUEST_PARAM_NAME_DELIMITER, 5);
				String daoParamKey = null;
				Object daoParamValue = null;

				if (key[0].equals(resultName)) {
					if (AppConstants.REQUEST_PARAM_NAME_CRUD_COLUMN.equals(key[1])) {
						// 登録、更新対象カラム
						// func_crud:result識別子:col:カラム名
						daoParamKey =  WebAppOSUtils.snakeToCamel(key[2]);
						// 同名パラメータが存在した場合、先勝ち
						SchemaColumn schemaColumn = _schemaColumnCache.getSchemaColumn(key[2]);
						String dataType = schemaColumn.getDataType().toLowerCase();

						if (DataTypeEnum.DATA_TYPE_CHARACTER_VARYING.getDataType().equals(dataType)
								|| DataTypeEnum.DATA_TYPE_CHARACTER.getDataType().equals(dataType)
								|| DataTypeEnum.DATA_TYPE_TEXT.getDataType().equals(dataType)) {
							daoParamValue = StringUtils.join(values, ",");
						}else{
							daoParamValue = convertDataType(values[0], schemaColumn);
						}

						if (!daoParams.containsKey(daoParamKey)) {
							daoParams.put(daoParamKey, daoParamValue);
						}
					} else if (AppConstants.REQUEST_PARAM_NAME_CRUD_CONDITION.equals(key[1])) {
						// パラメータのフォーマットが不正な場合は、無視する
						if (key.length != 5) {
							continue;
						}
						if (criteria == null){
							criteria = example.createCriteria();
						}
						// WHERE条件をDaoExampleに追加する
						createConditon(key, values, function, criteria);
					} else if (AppConstants.REQUEST_PARAM_NAME_CRUD_SORT.equals(key[1])) {
						example.setOrderByClause(values[0]);
					}
				}
			}
		}

		if (StringUtils.isEmpty(example.getOrderByClause())) {
			if (!StringUtils.isEmpty(function.getOrderBy())) {
				example.setOrderByClause(function.getOrderBy());
			}
		}

		SqlSession session = DaoUtil.getSqlSessionFactory().openSession();
		try {
			if (CrudEnum.CRUD_SELECT_BY_PRIMARYKEY.getType().equals(function.getMethod())) {
				session = DaoUtil.getSqlSessionFactory().openSession();
				AbstractEntity resultEntity = session.selectOne(mapperName, primaryKey);
				_resultMap.put(resultName, convertEntityToMap(resultEntity, false));
			} else if (CrudEnum.CRUD_SELECT_ALL_BY_PRIMARYKEY.getType().equals(function.getMethod())) {
				session = DaoUtil.getSqlSessionFactory().openSession();
				AbstractEntity resultEntity = session.selectOne(mapperName, primaryKey);
				_resultMap.put(resultName, convertEntityToMap(resultEntity, true));
			} else if (CrudEnum.CRUD_SELECT_BY_EXAMPLE.getType().equals(function.getMethod())) {
				session = DaoUtil.getSqlSessionFactory().openSession();

				List<?> entityList = session.selectList(mapperName, example);
				List<HashMap<String, Object>> crudResutlMap = new ArrayList<HashMap<String, Object>>();
				_resultMap.put(resultName, crudResutlMap);
				for (int i = 0; i < entityList.size(); i++) {
					crudResutlMap.add(convertEntityToMap(entityList.get(i), false));
				}
			} else if (CrudEnum.CRUD_SELECT_ALL_BY_EXAMPLE.getType().equals(function.getMethod())) {
				session = DaoUtil.getSqlSessionFactory().openSession();
				List<?> entityList = session.selectList(mapperName, example);
				List<HashMap<String, Object>> crudResutlMap = new ArrayList<HashMap<String, Object>>();
				_resultMap.put(resultName, crudResutlMap);
				for (int i = 0; i < entityList.size(); i++) {
					crudResutlMap.add(convertEntityToMap(entityList.get(i), true));
				}
			} else if (CrudEnum.CRUD_INSERT.getType().equals(function.getMethod())) {
				session = DaoUtil.getSqlSessionFactory().openSession(true);
				Map<String, Object> insertMap = new HashMap<String, Object>();
				insertMap.put("record", daoParams);
				session.insert(mapperName, insertMap);
			} else if (CrudEnum.CRUD_UPDATE.getType().equals(function.getMethod())) {
				if (example == null || example.getOredCriteria().size() == 0) {
					// TODO:条件なし。。
					return false;
				}
				session = DaoUtil.getSqlSessionFactory().openSession(true);
				Map<String, Object> updateMap = new HashMap<String, Object>();
				updateMap.put("record", daoParams);
				updateMap.put("example", example);
				session.update(mapperName, updateMap);
			} else if (CrudEnum.CRUD_UPDATE_BY_PRIMARYKEY.getType().equals(function.getMethod())) {
				session = DaoUtil.getSqlSessionFactory().openSession(true);
				Map<String, Object> updateMap = new HashMap<String, Object>();
				updateMap.put("record", daoParams);
				updateMap.put("id", primaryKey);
				session.update(mapperName, updateMap);
			} else if (CrudEnum.CRUD_DELETE.getType().equals(function.getMethod())) {
				if (example == null || example.getOredCriteria().size() == 0) {
					// TODO:条件なし。。
					return false;
				}
				session = DaoUtil.getSqlSessionFactory().openSession(true);
				session.delete(mapperName, example);
			} else if (CrudEnum.CRUD_DELETE_BY_PRIMARYKEY.getType().equals(function.getMethod())) {
				session = DaoUtil.getSqlSessionFactory().openSession(true);
				session.delete(mapperName, primaryKey);
			}
		} finally {
			if (session != null) {
				session.close();
			}
		}
		return true;
	}

	private boolean executeCrudFunctionForLoad(FunctionBean function)
			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {

		String resultName = function.getResult();
		String mapperName = "jp.co.headwaters.webappos.mapper."
				+ WebAppOSUtils.getMapperName(function.getTarget())
				+ "."
				+ function.getMethod();

		Map<String, Object> daoParams = new HashMap<String, Object>();
		DaoExample example = null;
		Object primaryKey = null;

		if (!CrudEnum.CRUD_SELECT_BY_PRIMARYKEY.getType().equals(function.getMethod())
				&& !CrudEnum.CRUD_SELECT_ALL_BY_PRIMARYKEY.getType().equals(function.getMethod())
				&& !CrudEnum.CRUD_SELECT_BY_EXAMPLE.getType().equals(function.getMethod())
				&& !CrudEnum.CRUD_SELECT_ALL_BY_EXAMPLE.getType().equals(function.getMethod())) {
			// TODO:select以外は後で対応
			return false;
		}

		if (!_resultMap.containsKey("request")){
			_resultMap.put("request", new HashMap<String, String>());
		}
		HashMap<String ,Object> requestMap = (HashMap<String, Object>) _resultMap.get("request");

		example = new DaoExample();
		Criteria criteria = null;
		for (ConditionBean cond : function.getCondList()) {
			if (criteria == null){
				criteria = example.createCriteria();
			}

			String[] values = _requestParams.get(cond.getParamName());
			if (values != null && values.length > 0) {
				values = _requestParams.get(cond.getParamName());
			} else {
				// リクエストパラメータに存在しない場合
				values = new String[1];
				values[0] = cond.getParamValue();
			}

			if (values != null && values.length > 0) {
				requestMap.put(cond.getParamName(), values[0]);

				if (CrudEnum.CRUD_SELECT_BY_PRIMARYKEY.getType().equals(function.getMethod())
						|| CrudEnum.CRUD_SELECT_ALL_BY_PRIMARYKEY.getType().equals(function.getMethod())) {
					primaryKey = convertDataType(values[0], _schemaColumnCache.getSchemaColumn(cond.getColumnName()));
				} else {
					createConditon(function.getResult(),
							cond.getTableName(),
							cond.getColumnName(),
							cond.getOption(),
							cond.getTableName() + "." + cond.getColumnName(),
							cond.getOperator(),
							values,
							criteria);
				}
			} else {
				requestMap.put(cond.getParamName(), "");
			}
		}

		List<String> sortList = function.getSortList();
		if (sortList != null && sortList.size() > 0) {
			example.setOrderByClause(StringUtils.join(sortList, ","));
		}

		SqlSession session = DaoUtil.getSqlSessionFactory().openSession();
		try {
			if (CrudEnum.CRUD_SELECT_BY_PRIMARYKEY.getType().equals(function.getMethod())) {
				session = DaoUtil.getSqlSessionFactory().openSession();
				AbstractEntity resultEntity = session.selectOne(mapperName, primaryKey);
				_resultMap.put(resultName, convertEntityToMap(resultEntity, false));
			} else if (CrudEnum.CRUD_SELECT_ALL_BY_PRIMARYKEY.getType().equals(function.getMethod())) {
				session = DaoUtil.getSqlSessionFactory().openSession();
				AbstractEntity resultEntity = session.selectOne(mapperName, primaryKey);
				_resultMap.put(resultName, convertEntityToMap(resultEntity, true));
			} else if (CrudEnum.CRUD_SELECT_BY_EXAMPLE.getType().equals(function.getMethod())) {

				session = DaoUtil.getSqlSessionFactory().openSession();
				Integer count = session.selectOne(
						"jp.co.headwaters.webappos.mapper."
						+ WebAppOSUtils.getMapperName(function.getTarget())
						+ ".countByExample", example);

				if (count > 0) {
					List<?> entityList = null;
					int pageNo = 0;
					int offset = 0;
					int limit = 0;
					RowBounds rowBounds = null;
					PagerBean pager = function.getPager();
					if (pager != null){
						if (_requestParams.get("pageNo") != null
								&& _requestParams.get("pageNo").length > 0
								&& !StringUtils.isEmpty(_requestParams.get("pageNo")[0])) {
							pageNo = Integer.parseInt(_requestParams.get("pageNo")[0]);
						} else {
							pageNo = 1;
						}
						requestMap.put("pageNo", pageNo);

						PagingUtility pageInfo = new PagingUtility(
								_resultMap, resultName,
								count, pageNo,
								Integer.parseInt(pager.getPerPage()), Integer.parseInt(pager.getPagerCount()));

						// pageNoを元にoffsetとlimitを算出する
						offset = pageInfo.recordBeginNo - 1;
						limit = pageInfo.perPage;
					} else {
						if (!StringUtils.isEmpty(function.getOffset())) {
							offset = Integer.parseInt(function.getOffset());
						}
						if (!StringUtils.isEmpty(function.getLimit())) {
							limit = Integer.parseInt(function.getLimit());
						}
					}

					if (offset != 0 || limit != 0) {
						rowBounds = new RowBounds(offset, limit);
					}
					if (rowBounds == null) {
						entityList = session.selectList(mapperName, example);
					} else {
						entityList = session.selectList(mapperName, example, rowBounds);
					}
					List<HashMap<String, Object>> crudResutlMap = new ArrayList<HashMap<String, Object>>();
					_resultMap.put(resultName, crudResutlMap);
					for (int i = 0; i < entityList.size(); i++) {
						crudResutlMap.add(convertEntityToMap(entityList.get(i), false));
					}
				} else {
					_resultMap.put(resultName, new ArrayList<HashMap<String, Object>>());
				}
			} else if (CrudEnum.CRUD_SELECT_ALL_BY_EXAMPLE.getType().equals(function.getMethod())) {
				session = DaoUtil.getSqlSessionFactory().openSession();
				List<?> entityList = session.selectList(mapperName, example);
				List<HashMap<String, Object>> crudResutlMap = new ArrayList<HashMap<String, Object>>();
				_resultMap.put(resultName, crudResutlMap);
				for (int i = 0; i < entityList.size(); i++) {
					crudResutlMap.add(convertEntityToMap(entityList.get(i), true));
				}
			}
		} finally {
			if (session != null) {
				session.close();
			}
		}
		return true;
	}

	private boolean executeSendMailFunction(FunctionBean 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() + AppConstants.REQUEST_PARAM_NAME_DELIMITER + "col" + AppConstants.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 = "jp.co.headwaters.webappos.mapper."
						+ StringUtils.capitalize(addressTalbeName)
						+ "Mapper."
						+ CrudEnum.CRUD_SELECT_BY_EXAMPLE.getType();
				DaoExample example = new DaoExample();
				Criteria criteria = example.createCriteria();
				// TODO:PKのカラム名を固定にしたいな
				// TODO:テーブルコメントに関連書いて取得かな。。
				criteria.andIn("id", idList);

				// 送信アドレスを取得する
				SqlSession session = null;
				try {
					session = DaoUtil.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())){
			// メール送信情報を登録する
			FunctionBean insFunction = new FunctionBean();
			insFunction.setType(FunctionEnum.FUNCTION_CRUD.getFunctionName());
			insFunction.setMethod(CrudEnum.CRUD_INSERT.getType());
			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(AppConstants.REQUEST_PARAM_NAME_DELIMITER, 3);
			String daoParamKey = null;
			Object daoParamValue = null;

			if (key[0].equals(resultName)) {
				if (AppConstants.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(FunctionBean 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());
					}
				}
			}
		}
	}

	private void createConditon(String resultName, String tableName, String columnName, String option, String target,
			String operator, String[] values, Criteria criteria) {
		Object value = null;
		if (!"isnull".equals(operator)
				&& !"isnotnull".equals(operator)
				&& !"between".equals(operator)
				&& !"notbetween".equals(operator)) {
			if (StringUtils.isEmpty(values[0])) {
				return;
			}
		}

		// TODO:後で定数にする
		if ("isnull".equals(operator)) {
			criteria.andIsNull(target);
		} else if ("isnotnull".equals(operator)) {
			criteria.andIsNotNull(target);
		} else if ("eq".equals(operator)) {
			value = convertDataType(values[0], _schemaColumnCache.getSchemaColumn(columnName));
			criteria.andEqualTo(target, value);
		} else if ("nq".equals(operator)) {
			value = convertDataType(values[0], _schemaColumnCache.getSchemaColumn(columnName));
			criteria.andNotEqualTo(target, value);
		} else if ("gt".equals(operator)) {
			value = convertDataType(values[0], _schemaColumnCache.getSchemaColumn(columnName));
			criteria.andGreaterThan(target, value);
		} else if ("ge".equals(operator)) {
			value = convertDataType(values[0], _schemaColumnCache.getSchemaColumn(columnName));
			criteria.andGreaterThanOrEqualTo(target, value);
		} else if ("lt".equals(operator)) {
			value = convertDataType(values[0], _schemaColumnCache.getSchemaColumn(columnName));
			criteria.andLessThan(target, value);
		} else if ("le".equals(operator)) {
			value = convertDataType(values[0], _schemaColumnCache.getSchemaColumn(columnName));
			criteria.andLessThanOrEqualTo(target, value);
		} else if ("%like".equals(operator)) {
			criteria.andLike(target, "%" + values[0]);
		} else if ("%like%".equals(operator)) {
			criteria.andLike(target, "%" + values[0] + "%");
		} else if ("like%".equals(operator)) {
			criteria.andLike(target, values[0] + "%");
		} else if ("%notlike".equals(operator)) {
			criteria.andNotLike(target, "%" + values[0]);
		} else if ("%notlike%".equals(operator)) {
			criteria.andNotLike(target, "%" + values[0] + "%");
		} else if ("notlike%".equals(operator)) {
			criteria.andNotLike(target, values[0] + "%");
		} else if ("in".equals(operator)) {
			List<Object> paramList = new ArrayList<Object>();
			for (String val1 : values) {
				for (String val2 : Arrays.asList(val1.split(","))) {
					if (StringUtils.isEmpty(val2)) {
						continue;
					}
					paramList.add(convertDataType(val2, _schemaColumnCache.getSchemaColumn(columnName)));
				}
			}
			criteria.andIn(target, paramList);
		} else if ("notin".equals(operator)) {
			List<Object> paramList = new ArrayList<Object>();
			for (String val1 : values) {
				for (String val2 : Arrays.asList(val1.split(","))) {
					if (StringUtils.isEmpty(val2)) {
						continue;
					}
					paramList.add(convertDataType(val2, _schemaColumnCache.getSchemaColumn(columnName)));
				}
			}
			criteria.andNotIn(target, paramList);
		} else if ("between".equals(operator)) {
			if ("from".equals(option)){
				String toKey = resultName + "__cond__" + tableName + "__" + columnName + "__to";
				String[] toValue = _requestParams.get(toKey);
				if (!StringUtils.isEmpty(values[0]) && !StringUtils.isEmpty(toValue[0])) {
					criteria.andBetween(target,
							convertDataType(values[0], _schemaColumnCache.getSchemaColumn(columnName)),
							convertDataType(toValue[0], _schemaColumnCache.getSchemaColumn(columnName)));
				} else if (!StringUtils.isEmpty(values[0])) {
					value = convertDataType(values[0], _schemaColumnCache.getSchemaColumn(columnName));
					criteria.andGreaterThanOrEqualTo(target, value);
				} else if (!StringUtils.isEmpty(toValue[0])) {
					value = convertDataType(toValue[0], _schemaColumnCache.getSchemaColumn(columnName));
					criteria.andLessThanOrEqualTo(target, value);
				}
			}
		} else if ("notbetween".equals(operator)) {
			if ("from".equals(option)){
				String toKey = resultName + "__cond__" + tableName + "__" + columnName + "__to";
				String[] toValue = _requestParams.get(toKey);

				if (!StringUtils.isEmpty(values[0]) && !StringUtils.isEmpty(toValue[0])) {
					criteria.andNotBetween(target,
							convertDataType(values[0], _schemaColumnCache.getSchemaColumn(columnName)),
							convertDataType(toValue[0], _schemaColumnCache.getSchemaColumn(columnName)));
				} else if (!StringUtils.isEmpty(values[0])) {
					value = convertDataType(values[0], _schemaColumnCache.getSchemaColumn(columnName));
					criteria.andLessThan(target, value);
				} else if (!StringUtils.isEmpty(toValue[0])) {
					value = convertDataType(toValue[0], _schemaColumnCache.getSchemaColumn(columnName));
					criteria.andGreaterThan(target, value);
				}
			}
		} else {
			// TODO:オペレータが不正。WARN出力
		}
	}

	private void createConditon(String[] key, String[] values, FunctionBean function, Criteria criteria) {
		// key=0:result識別子,1:cond,2:テーブル名,3:カラム名,4:オプション（オペレータによって異なる）
		String resultName = function.getResult();
		String tableName = key[2];
		String columnName = key[3];
		String option = key[4];
		String target = tableName + "." + columnName;
		String operator = function.getCondMap().get(tableName + columnName);

		createConditon(resultName, tableName, columnName, option, target, operator, values, criteria);
	}

	private HashMap<String, Object> convertEntityToMap(Object obj, boolean isRecursive)
			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{

		HashMap<String, Object> recordMap = new HashMap<String, Object>();

		if (obj == null) return null;
		String key = null;
		Object value = null;
		for (java.lang.reflect.Method method : obj.getClass().getMethods()){
			if (method.getName().startsWith("get") && !method.getName().equals("getClass")){
				value = method.invoke(obj);
				if (value != null){
					if (value instanceof List){
						if (!isRecursive) {
							continue;
						}
						List<?> list = (List<?>)value;
						List<HashMap<String, Object>> multipleList = new ArrayList<HashMap<String, Object>>();
						key = StringUtils.uncapitalize(StringUtils.substringAfter(method.getName(), "get"));
						recordMap.put(key, multipleList);
						for (int i = 0; i < list.size(); i++) {
							multipleList.add(convertEntityToMap(list.get(i), isRecursive));
						}
					} else if (value instanceof AbstractEntity) {
						if (!isRecursive) {
							continue;
						}
						recordMap.putAll(convertEntityToMap(value, isRecursive));
					} else {
						key = WebAppOSUtils.camelToSnake(StringUtils.substringAfter(method.getName(), "get"));
						recordMap.put(key, value);
					}
				}
			}
		}
		return recordMap;
	}

	private Object convertDataType(String value, SchemaColumn schemaColumn) {
		if (schemaColumn == null) {
			return null;
		}
		return WebAppOSUtils.convertDataType(value, schemaColumn.getDataType().toLowerCase());
	}

	// ---------------- setter/getter ----------------
	public Map<String, Object> getResultMap() {
		return _resultMap;
	}
}
