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

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jp.co.headwaters.webappos.controller.cache.SchemaColumnCache;
import jp.co.headwaters.webappos.controller.cache.SystemConstantCache;
import jp.co.headwaters.webappos.controller.cache.bean.AbstractFunctionBean;
import jp.co.headwaters.webappos.controller.enumation.DataTypeEnum;
import jp.co.headwaters.webappos.controller.model.AbstractEntity;
import jp.co.headwaters.webappos.controller.model.SchemaColumn;

import org.apache.commons.lang3.StringUtils;

public abstract class AbstractFunction {

	/** リクエストパラメータを保持するMap */
	protected Map<String, String[]> _requestParams;
	/** 処理結果を保持するMap */
	protected Map<String, Object> _resultMap;
	/** スキーマカラム情報 */
	protected SchemaColumnCache _schemaColumnCache;
	/** 定数情報 */
	protected SystemConstantCache _systemConstantCache;

	public void execute(Map<String, String[]> requestParams, Map<String, Object> resultMap,
			AbstractFunctionBean function) throws Exception {

		this._requestParams = requestParams;
		this._resultMap = resultMap;

		initialize();

		execute(function);
	}

	protected void initialize(){
		this._schemaColumnCache = SchemaColumnCache.getInstance();
		this._systemConstantCache = SystemConstantCache.getInstance();
	}

	protected abstract void execute(AbstractFunctionBean function) throws Exception;

	protected static 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")){ //$NON-NLS-1$ //$NON-NLS-2$
				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")); //$NON-NLS-1$
						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 = camelToSnake(StringUtils.substringAfter(method.getName(), "get")); //$NON-NLS-1$
						recordMap.put(key, value);
					}
				}
			}
		}
		return recordMap;
	}

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

	protected static Object convertDataType(String value, String dataType) {
		if (value == null || dataType == null) {
			return null;
		}

		Object result = null;
		if (DataTypeEnum.DATA_TYPE_SMALLINT.getDataType().equals(dataType)) {
			result = Short.valueOf(value);
		} else if (DataTypeEnum.DATA_TYPE_INTEGER.getDataType().equals(dataType)) {
			result = Integer.valueOf(value);
		} else if (DataTypeEnum.DATA_TYPE_BIGINT.getDataType().equals(dataType)) {
			result = Long.valueOf(value);
		} else if (DataTypeEnum.DATA_TYPE_NUMERIC.getDataType().equals(dataType)) {
			result = new BigDecimal(value);
		} else if (DataTypeEnum.DATA_TYPE_REAL.getDataType().equals(dataType)) {
			result = Float.valueOf(value);
		} else if (DataTypeEnum.DATA_TYPE_DOUBLE_PRECISION.getDataType().equals(dataType)) {
			result = Double.valueOf(value);
		} else if (DataTypeEnum.DATA_TYPE_CHARACTER_VARYING.getDataType().equals(dataType)
				|| DataTypeEnum.DATA_TYPE_CHARACTER.getDataType().equals(dataType)
				|| DataTypeEnum.DATA_TYPE_TEXT.getDataType().equals(dataType)) {
			result = value;
		} else if (DataTypeEnum.DATA_TYPE_TIMESTAMP_WITHOUT_TIME_ZONE.getDataType().equals(dataType)
				|| (DataTypeEnum.DATA_TYPE_TIMESTAMP_WITH_TIME_ZONE.getDataType().equals(dataType))) {
			try {
				// TODO:日付のフォーマット
				// TODO:これだと時間が入らない！
				result = new java.sql.Timestamp(new SimpleDateFormat("yyyy/MM/dd").parse(value).getTime());
			} catch (ParseException e) {
				// TODO:ワーニング
			}
		} else if (DataTypeEnum.DATA_TYPE_DATE.getDataType().equals(dataType)) {
			try {
				// TODO:日付のフォーマット
				result = new java.sql.Date(new SimpleDateFormat("yyyy/MM/dd").parse(value).getTime());
			} catch (ParseException e) {
				// TODO:ワーニング
			}
		} else if (DataTypeEnum.DATA_TYPE_TIME_WITHOUT_TIME_ZONE.getDataType().equals(dataType)
				|| DataTypeEnum.DATA_TYPE_TIME_WITH_TIME_ZONE.getDataType().equals(dataType)) {
			try {
				// TODO:日付のフォーマット
				result = new java.sql.Time(new SimpleDateFormat("yyyy/MM/dd").parse(value).getTime());
			} catch (ParseException e) {
				// TODO:ワーニング
			}
		} else if (DataTypeEnum.DATA_TYPE_BYTEA.getDataType().equals(dataType)) {
			result = Byte.valueOf(value.toString());
		} else if (DataTypeEnum.DATA_TYPE_BOOLEAN.getDataType().equals(dataType)) {
			result = Boolean.valueOf(value.toString());
		} else if (DataTypeEnum.DATA_TYPE_BIT.getDataType().equals(dataType)
				|| DataTypeEnum.DATA_TYPE_BIT_VARYING.getDataType().equals(dataType)) {
			result = value;
		} else {
			// TODO:サポート外の型。ワーニング
			result = null;
		}
		return result;
	}

	protected static String camelToSnake(String target) {
		String convertedStr = target
				.replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2") //$NON-NLS-1$ //$NON-NLS-2$
				.replaceAll("([a-z])([A-Z])", "$1_$2");  //$NON-NLS-1$//$NON-NLS-2$
		return convertedStr.toLowerCase();
	}

	protected static String snakeToCamel(String target) {
		return snakeToCamel(target, false);
	}

	protected static String snakeToCamel(String target, boolean firstCharacterUppercase) {
		Pattern p = Pattern.compile("_([a-z])"); //$NON-NLS-1$
		Matcher m = p.matcher(target.toLowerCase());

		StringBuffer sb = new StringBuffer(target.length());
		while (m.find()) {
			m.appendReplacement(sb, m.group(1).toUpperCase());
		}
		m.appendTail(sb);
		if (firstCharacterUppercase) {
			sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
		}
		return sb.toString();
	}
}
