package jp.agentec.adf.core.formatter;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import jp.agentec.adf.util.StringUtil;

/**
 * データベーステーブルのカラム名をJavaクラスのフィールド名やメソッド名に変換する機能を提供します。
 * @author Taejin Hong
 * @version ADF4J 1.0.1
 *
 */
public class PropertyNameFormatter {
	public static final String DefaultGetterPrefix = "get";
	public static final String DefaultSetterPrefix = "set";
	public static final String DefaultDelimiter = "_";
	public static final char CharA = 'A';
	public static final char CharZ = 'Z';
	
	public static final String IgnorePropertyName = "class";
	
	/**
	 * テーブルのカラム名をJavaクラスのプロパーティ（フィールド）名に変換します。<br>
	 * 例えば、カラム名がcolumn_nameだと、プロパーティ（フィールド）名はcolumnNameとなります。
	 * @param databaseFieldName テーブルのカラム名です。
	 * @param databaseFieldNameDelimiter テーブルのカラム名の区切りです。column_nameがカラム名だと、_が区切りです。
	 * @return Javaクラスのフィールド名に変換した文字列を返します。
	 * @since 1.0.1
	 */
	public static String generatePropertyNameFromDatabaseFieldName(String databaseFieldName, String databaseFieldNameDelimiter) {
		StringBuffer propertyName = new StringBuffer();
		
		if (!StringUtil.isNullOrWhiteSpace(databaseFieldName) && !StringUtil.isNullOrWhiteSpace(databaseFieldNameDelimiter)) {
			String[] temp = databaseFieldName.toLowerCase().split(databaseFieldNameDelimiter);
			
			if (temp.length > 0) {
				for (int i = 0; i < temp.length; i++) {
					if (!StringUtil.isNullOrEmpty(temp[i])) {
						if (i == 0) {
							propertyName.append(temp[i].substring(0, 1));
						} else {
							propertyName.append(temp[i].substring(0, 1).toUpperCase());
						}
						
						if (temp[i].length() > 1) {
							propertyName.append(temp[i].substring(1));
						}
					}
				}
			}
		}
		
		return propertyName.toString();
	}
	
	/**
	 * setterメソッドの名前からプロパーティ（フィールド）名を作成します。setterメソッド名はsetから始まる必要があります。
	 * @param setterName setterメソッドの名前です。
	 * @return 作られたプロパーティ（フィールド）名を返します。
	 * @since 1.0.0
	 */
	public static String generatePropertyNameFromSetter(String setterName) {
		return generatePropertyNameFromMethod(setterName, DefaultSetterPrefix);
	}
	
	/**
	 * getterメソッドの名前からプロパーティ（フィールド）名を作成します。getterメソッド名はgetから始まる必要があります。
	 * @param getterName getterメソッドの名前です。
	 * @return 作られたプロパーティ（フィールド）名を返します。
	 * @since 1.0.0
	 */
	public static String generatePropertyNameFromGetter(String getterName) {
		return generatePropertyNameFromMethod(getterName, DefaultGetterPrefix);
	}
	
	/**
	 * getterまたはsetterメソッドの名前からプロパーティ（フィールド）名を作成します。
	 * @param methodName getterまたはsetterメソッドの名前です。
	 * @param methodPrefix getterまたはsetterメソッドのprefixです。（get~のような）
	 * @return 作られたプロパーティ（フィールド）名を返します。
	 * @since 1.0.0
	 */
	public static String generatePropertyNameFromMethod(String methodName, String methodPrefix) {
		StringBuffer propertyName = new StringBuffer();
		
		if (!StringUtil.isNullOrWhiteSpace(methodName) && !StringUtil.isNullOrWhiteSpace(methodPrefix)
				&& methodName.startsWith(methodPrefix) && methodName.length() > methodPrefix.length()) {
			String temp = methodName.substring(methodPrefix.length());
			
			if (!StringUtil.isNullOrWhiteSpace(temp) && !temp.equals("")) {
				propertyName.append(temp.substring(0, 1).toLowerCase());
				
				if (temp.length() > 1) {
					propertyName.append(temp.substring(1));
				}
			}
			
		}
		
		return propertyName.toString();
	}
	
	/**
	 * 指定したオブジェクトのgetterメソッドからプロパティ名（フィールド）を作成します。
	 * @param object プロパティ名を作成するオブジェクトです。
	 * @param objectType object のクラスです。
	 * @return 作られたプロパーティ（フィールド）名の配列を返します。
	 * @since 1.0.0
	 */
	public static String[] generatePropertyNamesFromObject(Object object, Class<?> objectType) {
		String[] result = new String[]{};
		List<String> propertyList = new ArrayList<String>();
		
		if (object != null && objectType != null) {
			Method[] methodList = objectType.getMethods();
			String temp;
			
			for (Method method : methodList) {
				temp = PropertyNameFormatter.generatePropertyNameFromGetter(method.getName());
				
				if (!StringUtil.isNullOrWhiteSpace(temp) && !temp.equals(IgnorePropertyName)) {
					propertyList.add(PropertyNameFormatter.generatePropertyNameFromGetter(method.getName()));
				}
			}
		}
		
		return propertyList.toArray(result);
	}
	
	/**
	 * Javaクラスのプロパーティ（フィールド）名をデータベースのカラム名に変換します。<br>
	 * 例えば、プロパーティ（フィールド）名がcolumnNameだと、カラム名はcolumn_nameのようになります。
	 * @param propertyName Javaクラスのプロパーティ（フィールド）名です。
	 * @return 変換したカラム名を返します。
	 * @since 1.0.0
	 */
	public static String generateDatabaseFieldNameFromPropertyName(String propertyName) {
		return generateDatabaseFieldNameFromPropertyName(propertyName, DefaultDelimiter); 
	}
	
	/**
	 * Javaクラスのプロパーティ（フィールド）名をデータベースのカラム名に変換します。<br>
	 * 例えば、プロパーティ（フィールド）名がcolumnNameで、databaseFieldNameDelimiter がアンダーバー（_）だと、カラム名はcolumn_nameのようになります。
	 * @param propertyName Javaクラスのプロパーティ（フィールド）名です。
	 * @param databaseFieldNameDelimiter カラム名の区切りです。
	 * @return 変換したカラム名を返します。
	 * @since 1.0.0
	 */
	public static String generateDatabaseFieldNameFromPropertyName(String propertyName, String databaseFieldNameDelimiter) {
		StringBuffer databaseFieldName = new StringBuffer();
		
		if (!StringUtil.isNullOrWhiteSpace(propertyName) && !StringUtil.isNullOrWhiteSpace(databaseFieldNameDelimiter)) {
			databaseFieldName.append(propertyName.substring(0, 1).toLowerCase());
			
			if (propertyName.length() > 1) {
				for (int i = 1; i < propertyName.length(); i++) {
					char ch = propertyName.charAt(i);
					
					if (ch >= CharA && ch <= CharZ) {
						databaseFieldName.append(databaseFieldNameDelimiter);
						databaseFieldName.append(propertyName.substring(i, i + 1).toLowerCase());
					} else {
						databaseFieldName.append(ch);
					}
				}
			}
		}
		
		return databaseFieldName.toString();
	}
	
	/**
	 * getterメソッドの名前からカラム名を作成します。getterメソッド名はgetから始まる必要があります。<br>
	 * 例えば、getter名がgetColumnNameで、databaseFieldNameDelimiter がアンダーバー（_）だと、カラム名はcolumn_nameのようになります。
	 * @param getterName 
	 * @return 作成されたカラム名を返します。
	 * @since 1.0.0
	 */
	public static String generateDatabaseFieldNameFromGetter(String getterName) {
		return generateDatabaseFieldNameFromGetter(getterName, DefaultGetterPrefix, DefaultDelimiter);
	}
	
	/**
	 * getterメソッドの名前からカラム名を作成します。<br>
	 * 例えば、getter名がgetColumnNameで、databaseFieldNameDelimiter がアンダーバー（_）だと、カラム名はcolumn_nameのようになります。
	 * @param getterName　getterName getterメソッドの名前です。
	 * @param getterPrefix getterメソッドのprefixです。（get~のような）
	 * @param databaseFieldNameDelimiter カラム名の区切りです。
	 * @return 作成されたカラム名を返します
	 * @since 1.0.0
	 */
	public static String generateDatabaseFieldNameFromGetter(String getterName, String getterPrefix, String databaseFieldNameDelimiter) {
		StringBuffer databaseFieldName = new StringBuffer();
		
		if (!StringUtil.isNullOrWhiteSpace(getterName) && !StringUtil.isNullOrWhiteSpace(getterPrefix)
				&& getterName.startsWith(getterPrefix) && getterName.length() > getterPrefix.length()) {
			String temp = getterName.substring(getterPrefix.length());
			
			if (!StringUtil.isNullOrWhiteSpace(temp)) {
				databaseFieldName.append(temp.substring(0, 1).toLowerCase());
				
				if (temp.length() > 1) {
					for (int i = 1; i < temp.length(); i++) {
						char ch = temp.charAt(i);
						
						if (ch >= CharA && ch <= CharZ) {
							databaseFieldName.append(databaseFieldNameDelimiter);
							databaseFieldName.append(temp.substring(i, i + 1).toLowerCase());
						} else {
							databaseFieldName.append(ch);
						}
					}
				}
			}
			
		}
		
		return databaseFieldName.toString();
	}
	
	/**
	 * テーブルのコラム名などをVOなどのgetterメソッド名に変換します。<br>
	 * 例えば、カラム名がcolumn_nameだと、getterメソッド名はsetColumnNameのようになります。
	 * @param databaseFieldName テーブルのカラム名です。
	 * @param databaseFieldNameDelimiter テーブルのカラム名の区切りです。column_nameがカラム名だと、_が区切りです。
	 * @return　作られたメソッドの名前を返します。
	 * @since 1.0.1
	 */
	public static String generateGetterName(String databaseFieldName, String databaseFieldNameDelimiter) {
		return generateGetterSetterName(databaseFieldName, databaseFieldNameDelimiter, DefaultGetterPrefix);
	}
	
	/**
	 * テーブルのコラム名などをVOなどのsetterメソッド名に変換します。<br>
	 * 例えば、カラム名がcolumn_nameだと、setterメソッド名はsetColumnNameのようになります。
	 * @param databaseFieldName テーブルのカラム名です。
	 * @param databaseFieldNameDelimiter テーブルのカラム名の区切りです。column_nameがカラム名だと、_が区切りです。
	 * @return　作られたメソッドの名前を返します。
	 * @since 1.0.1
	 */
	public static String generateSetterName(String databaseFieldName, String databaseFieldNameDelimiter) {
		return generateGetterSetterName(databaseFieldName, databaseFieldNameDelimiter, DefaultSetterPrefix);
	}
	
	/**
	 * テーブルのコラム名などをVOなどのメソッド名(getter, setter)に変換します。<br>
	 * 例えば、カラム名がcolumn_nameだと、プロパーティ（フィールド）名はgetColumnName又はsetColumnNameのようになります。
	 * @param databaseFieldName テーブルのカラム名です。
	 * @param databaseFieldNameDelimiter テーブルのカラム名の区切りです。column_nameがカラム名だと、_が区切りです。
	 * @param methodPrefix get~ 又は set~ みたいにメソッドの接頭語になる文字列です。
	 * @return 作られたメソッドの名前を返します。
	 * @since 1.0.0
	 */
	public static String generateGetterSetterName(String databaseFieldName, String databaseFieldNameDelimiter, String methodPrefix) {
		StringBuffer methodName = new StringBuffer();
		String propertyName = generatePropertyNameFromDatabaseFieldName(databaseFieldName, databaseFieldNameDelimiter);
		
		if (!StringUtil.isNullOrWhiteSpace(propertyName)) {
			methodName.append(methodPrefix);
			methodName.append(propertyName.substring(0, 1).toUpperCase());
			
			if (propertyName.length() > 1) {
				methodName.append(propertyName.substring(1));
			}
		}
		
		return methodName.toString();
	}
}
