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

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import jp.co.headwaters.webappos.controller.ControllerConstants;
import jp.co.headwaters.webappos.controller.cache.ActionCache;
import jp.co.headwaters.webappos.controller.cache.bean.AbstractFunctionBean;
import jp.co.headwaters.webappos.controller.cache.bean.ActionBean;
import jp.co.headwaters.webappos.controller.cache.bean.ExecuteBean;
import jp.co.headwaters.webappos.controller.cache.bean.LoadFunctionBean;
import jp.co.headwaters.webappos.controller.cache.bean.ResultBean;
import jp.co.headwaters.webappos.controller.cache.bean.SubmitFunctionBean;
import jp.co.headwaters.webappos.controller.exception.NotFoundException;
import jp.co.headwaters.webappos.controller.fuction.AbstractFunction;
import jp.co.headwaters.webappos.controller.fuction.CrudFunction;
import jp.co.headwaters.webappos.controller.fuction.FunctionFactory;
import jp.co.headwaters.webappos.controller.utils.ControllerUtils;
import jp.co.headwaters.webappos.controller.utils.MessageUtils;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.ServletActionContext;

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

public class GenericAction extends ActionSupport {

	private static final long serialVersionUID = 7605002119825309038L;

	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 ActionBean _actionBean;

	public String execute() throws Exception {
		_logger.debug(ActionContext.getContext().getName());

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

		// ------------------------------------------------------------
		// 実行情報および遷移先名を取得する
		// ------------------------------------------------------------
		String actionName = ActionContext.getContext().getName();
		String resultName = (String) ServletActionContext.getRequest().getAttribute(ControllerConstants.ATTR_NAME_RESULT_NAME);
		this._actionBean = ActionCache.getInstance().getActionMap().get(actionName);
		if (this._actionBean == null || resultName == null) {
			throw new NotFoundException();
		}

		// 実行、返却情報を保持する変数を初期化する
		ExecuteBean submitExecuteInfo = null;
		ExecuteBean loadExecute = null;
		ResultBean result = null;
		String formId = null;

		// form識別子を取得する
		if (this._requestParams != null && this._requestParams.get(ControllerConstants.ELEMENT_NAME_FORM_ID) != null){
			formId = this._requestParams.get(ControllerConstants.ELEMENT_NAME_FORM_ID)[0];
		}

		if (!StringUtils.isEmpty(formId)) {
			// TODO:トークンチェック
			// リクエストパラメータにform識別子が存在した場合、sumbitされたと判断する
			submitExecuteInfo = this._actionBean.getSubmitExecuteMap().get(formId);
			if (submitExecuteInfo == null){
				throw new NotFoundException();
			}
			result = submitExecuteInfo.getResult();
		} else {
			loadExecute = getLoadExecute(resultName);
			if (loadExecute != null) {
				result = loadExecute.getResult();
			}
		}

		if (result == null) {
			throw new NotFoundException();
		}

		// ------------------------------------------------------------
		// 命令実行(submit時)
		// ------------------------------------------------------------
		if (submitExecuteInfo != null) {
			for (AbstractFunctionBean function : submitExecuteInfo.getFunctions()) {
				SubmitFunctionBean submitFunction = (SubmitFunctionBean) function;
				AbstractFunction executor = FunctionFactory.create(submitFunction.getType());
				executor.execute(this._requestParams, this._resultMap, submitFunction);
			}
		}

		// ------------------------------------------------------------
		// 命令実行(load時)
		// ------------------------------------------------------------
		if (loadExecute != null) {
			for (AbstractFunctionBean function : loadExecute.getFunctions()) {
				LoadFunctionBean loadFunction = (LoadFunctionBean)function;
				CrudFunction executor = new CrudFunction();
				executor.execute(this._requestParams, this._resultMap, loadFunction);
			}
		}

		_logger.debug("result:" + result.getName()); //$NON-NLS-1$
		return result.getName();
	}

	private void setResultMapFromRequestParams() {
		for (Entry<String, String[]> entry : this._requestParams.entrySet()) {
			String[] keys = entry.getKey().split(ControllerConstants.REQUEST_PARAM_NAME_DELIMITER, 5);
			String[] values = entry.getValue();

			if (keys.length < 2) {
				// cond、bind以外のパラメータと判断
				if (!this._resultMap.containsKey(ControllerConstants.REQUEST_PARAM_MAP_KEY)){
					this._resultMap.put(ControllerConstants.REQUEST_PARAM_MAP_KEY, new HashMap<String ,Object>());
				}

				@SuppressWarnings("unchecked")
				HashMap<String ,Object> reqMap = (HashMap<String, Object>) this._resultMap.get(ControllerConstants.REQUEST_PARAM_MAP_KEY);
				if (values.length > 1) {
					reqMap.put(entry.getKey(), values);
				} else if (values.length == 1) {
					reqMap.put(entry.getKey(), values[0]);
				}
				continue;
			}

			String resultName = keys[0];
			String paramKind = keys[1];
			if (!this._resultMap.containsKey(resultName)){
				this._resultMap.put(resultName, new HashMap<String ,Object>());
			}
			@SuppressWarnings("unchecked")
			HashMap<String ,Object> resultMap = (HashMap<String, Object>) this._resultMap.get(resultName);
			if (ControllerConstants.REQUEST_PARAM_NAME_CRUD_COLUMN.equals(paramKind)) {
				// result識別子__col__カラム名
				if (keys.length != 3) {
					_logger.warn(MessageUtils.getString("warn.100", entry.getKey())); //$NON-NLS-1$
					continue;
				}
				String colName = keys[2];
				if (!resultMap.containsKey(ControllerConstants.REQUEST_PARAM_NAME_CRUD_COLUMN)) {
					resultMap.put(ControllerConstants.REQUEST_PARAM_NAME_CRUD_COLUMN, new HashMap<String ,Object>());
				}
				@SuppressWarnings("unchecked")
				HashMap<String ,Object> colMap = (HashMap<String, Object>) resultMap.get(ControllerConstants.REQUEST_PARAM_NAME_CRUD_COLUMN);
				if (values.length > 1) {
					colMap.put(colName, values);
				} else if (values.length == 1) {
					colMap.put(colName, values[0]);
				}
			} else if (ControllerConstants.REQUEST_PARAM_NAME_CRUD_CONDITION.equals(paramKind)) {
				// result識別子__cond__テーブル名__カラム名__オプション
				if (keys.length != 5) {
					_logger.warn(MessageUtils.getString("warn.101", entry.getKey())); //$NON-NLS-1$
					continue;
				}
				String tableName = keys[2];
				String colName = keys[3];
				if (!resultMap.containsKey(ControllerConstants.REQUEST_PARAM_NAME_CRUD_CONDITION)) {
					resultMap.put(ControllerConstants.REQUEST_PARAM_NAME_CRUD_CONDITION, new HashMap<String ,Object>());
				}
				@SuppressWarnings("unchecked")
				HashMap<String ,Object> condMap = (HashMap<String, Object>) resultMap.get(ControllerConstants.REQUEST_PARAM_NAME_CRUD_CONDITION);
				if (!condMap.containsKey(tableName)) {
					condMap.put(tableName, new HashMap<String ,Object>());
				}
				@SuppressWarnings("unchecked")
				HashMap<String ,Object> tableMap = (HashMap<String, Object>) condMap.get(tableName);
				if (values.length > 1) {
					tableMap.put(colName, values);
				} else if (values.length == 1) {
					tableMap.put(colName, values[0]);
				}
			} else if (ControllerConstants.REQUEST_PARAM_NAME_CRUD_SORT.equals(paramKind)) {
				// ignore
			} else if (ControllerConstants.REQUEST_PARAM_NAME_CRUD_PAGE_NO.equals(paramKind)) {
				// ignore
			} else {
				_logger.warn(MessageUtils.getString("warn.102", entry.getKey())); //$NON-NLS-1$
			}
		}
	}

	private ExecuteBean getLoadExecute(String resultName) {
		StringBuilder sb = new StringBuilder();
		if (!StringUtils.isEmpty(this._actionBean.getHtmlPath())){
			sb.append(this._actionBean.getHtmlPath());
			sb.append(ControllerUtils.getFileSparator());
		}
		sb.append(resultName);
		sb.append(ControllerConstants.JSP_EXTENSION);
		return this._actionBean.getLoadExecuteMap().get(sb.toString());
	}

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