package jp.agentec.adf.net.http;

import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.Map;

import jp.agentec.abook.abv.bl.common.exception.ABVRuntimeException;
import jp.agentec.adf.core.ADFObject;
import jp.agentec.adf.core.formatter.PropertyNameFormatter;

/**
 * HTTP通信を行う際、パラメータとして利用できるオブジェクトです。<br>
 * プロパティの値を任意の文字セットを使ってエンコードし、HTTPプロトコルに適切なフォーマットに変換することができます。<br>
 * デフォルトの文字セットはUTF-8です。
 * @author Taejin Hong
 * @version 1.0.1
 */
public abstract class HttpParameterObject extends ADFObject {
	protected static final String DefaultEncoding = "UTF-8";
	protected static final String DelimiterKeyValue = "=";
	protected static final String DelimiterKeyKey = "&";
	
	/**
	 * このクラス及びこのクラスを継承したクラスが持っているpublicのgetterメソッドの戻り値を纏めた文字列をHTTPのパラメータ形式で取得することができます。<br>
	 * UTF-8エンコーディングを使います。
	 * @return key1=value1&key2=value2　形式の文字列を返します。
	 * @since 1.0.0
	 */
	public String toHttpParameterString() {
		return toHttpParameterString(DefaultEncoding);
	}
	
	/**
	 * 指定したエンコーディングを利用して、このクラス及びこのクラスを継承したクラスが持っているpublicのgetterメソッドの戻り値を纏めた文字列をHTTPのパラメータ形式で取得することができます。
	 * @param encoding パラメータをエンコードする文字セットです。
	 * @return key1=value1&key2=value2　形式の文字列を返します。
	 * @since 1.0.0
	 */
	public String toHttpParameterString(String encoding) {
		try {
			StringBuffer sb = new StringBuffer();
			Class<?> c = getClass();
			Method[] methods = c.getMethods();
			String propertyName;
			
			for (Method method : methods) {
				if (method.getName().startsWith(PropertyNameFormatter.DefaultGetterPrefix)
						&& !method.getName().startsWith(PropertyNameFormatter.IgnorePropertyName)) {
					propertyName = PropertyNameFormatter.generatePropertyNameFromGetter(method.getName());
					
					if (!propertyName.startsWith(PropertyNameFormatter.IgnorePropertyName)) {
						if (propertyName.startsWith("map")) { // mapが名前に含まれている場合かつ、値がMapインスタンスの場合、Mapの中身をパラメータとして追加する。
							Object value = method.invoke(this);
							if (value instanceof Map) {
								@SuppressWarnings("unchecked")
								Map<String, String> map = (Map<String, String>)value;
								for(Map.Entry<String, String> e : map.entrySet()) {
									sb.append(URLEncoder.encode(e.getKey(), encoding));
									sb.append(DelimiterKeyValue);
									sb.append(URLEncoder.encode(e.getValue(), encoding).replace("+", "%20"));
									sb.append(DelimiterKeyKey);
								}
								continue;
							}
						}
						sb.append(URLEncoder.encode(propertyName, encoding));
						sb.append(DelimiterKeyValue);
						
						Object value = method.invoke(this);
						if (value != null) {
							sb.append(URLEncoder.encode(value.toString(), encoding).replace("+", "%20"));
						}
						
						sb.append(DelimiterKeyKey);
					}
				}
			}
			
			if (sb.length() >= DelimiterKeyKey.length()) {
				sb.delete(sb.length() - DelimiterKeyKey.length(), sb.length());
			}
			
			return sb.toString();
		} catch (Exception e) {
			throw new ABVRuntimeException(e);
		}
	}
}
