package jp.agentec.abook.abv.bl.common.util;

import jp.agentec.abook.abv.bl.acms.client.json.content.AbstractJSON;
import jp.agentec.abook.abv.bl.common.exception.ABVException;
import jp.agentec.abook.abv.bl.common.exception.ABVExceptionCode;
import jp.agentec.adf.util.StringUtil;

import org.json.adf.JSONArray;
import org.json.adf.JSONException;
import org.json.adf.JSONObject;

/**
 * JsonUtilクラス
 * 
 * 1. キーがない場合もしくは値の型が不正な場合にnullもしくは空配列を返す（JSONExceptionは投げない）メソッド群
 *    ⇒値がないことを認めていい場合のみ使用する。nullの場合呼び出し元でnullチェックは必須となる。
 *    
 * 2. 上の階層からダイレクトに複数の階層をたどって値にアクセスするメソッド群（キーがない、値の型が不正な場合ABVExceptionを投げる）
 *
 */
public class JsonUtil {
	public static final JSONObject getJSONObject(JSONObject fromJson, String key) {
		try {
			return fromJson.getJSONObject(key);
		} catch (JSONException e) {
			//
		}
		return null;
	}
	
	/**
	 * @param fromJson
	 * @param key
	 * @return 該当するJSONArrayを返す。該当データなしの場合はNULLではなく、JSONArrayのインスタンスを返す
	 */
	public static final JSONArray getJSONArray(JSONObject fromJson, String key) {
		try {
			return fromJson.getJSONArray(key);
		} catch (JSONException e) {
			//
		}
		return new JSONArray(); // 初期値を設定;
	}
	
	public static final JSONObject getJSONObject(JSONArray fromJson, int index) {
		try {
			return fromJson.getJSONObject(index);
		} catch (JSONException e) {
			//
		}
		return null;
	}
	
	public static final String getString(JSONObject fromJson, String key) {
		return getString(fromJson, key, null);
	}
	
	public static final String getString(JSONObject fromJson, String key, String def) {
		try {
			return fromJson.getString(key);
		} catch (JSONException e) {
			// ignore
		}
		return def;
	}

	public static final int getInt(JSONObject fromJson, String key) {
		return getInt(fromJson, key, -1);
	}
	
	public static final int getInt(JSONObject fromJson, String key, int def) {
		try {
			return fromJson.getInt(key);
		} catch (JSONException e) {
			// ignore
		}
		return def;
	}

	public static final long getLong(JSONObject fromJson, String key) {
		return getLong(fromJson, key, -1);
	}

	public static final long getLong(JSONObject fromJson, String key, long def) {
		try {
			return fromJson.getLong(key);
		} catch (JSONException e) {
			//
		}
		return def;
	}

	public static Boolean getBoolean(JSONObject fromJson, String key) {
		return getBoolean(fromJson, key, null);
	}

	public static Boolean getBoolean(JSONObject fromJson, String key, Boolean def) {
		try {
			return fromJson.getBoolean(key);
		} catch (JSONException e) {
		}
		return def;
	}

	public static Float getFloat(JSONObject fromJson, String key) {
		return getFloat(fromJson, key, null);
	}

	public static Float getFloat(JSONObject fromJson, String key, Float def) {
		try {
			return (float)fromJson.getDouble(key);
		} catch (JSONException e) {
			return def;
		}
	}
	
	
	////////////////////////////      以下 、階層を辿って値を返すメソッド群（未使用）     /////////////////////////////////

	public static Boolean getBoolean(AbstractJSON abstractJson, Object... keyOrIndexs) throws ABVException {
		return get(abstractJson, Boolean.class, keyOrIndexs);
	}

	public static Double getDouble(AbstractJSON abstractJson, Object... keyOrIndexs) throws ABVException {
		return get(abstractJson, Double.class, keyOrIndexs);
	}

	public static Float getFloat(AbstractJSON abstractJson, Object... keyOrIndexs) throws ABVException {
		return get(abstractJson, Double.class, keyOrIndexs).floatValue();
	}

	public static String getString(AbstractJSON abstractJson, Object... keyOrIndexs) throws ABVException {
		return get(abstractJson, String.class, keyOrIndexs);
	}

	public static int getInt(AbstractJSON abstractJson, Object... keyOrIndexs) throws ABVException {
		return get(abstractJson, Integer.class, keyOrIndexs);
	}

	public static long getLong(AbstractJSON abstractJson, Object... keyOrIndexs) throws ABVException {
		return get(abstractJson, Long.class, keyOrIndexs);
	}

	public static <T> T get(AbstractJSON abstractJson, Class<T> retType, Object... keyOrIndexs) throws ABVException {
		return get(abstractJson.getRoot(), retType, keyOrIndexs);
	}

	/**
	 * Jsonから指定のキーもしくは配列indexをたどって値を返す。
	 * 
	 * @param json
	 * @param retType Integer.class/String.class/Double.class
	 * @param keyOrIndexs  キー名(String)もしくは配列インデックス(Intger)のみ
	 * @return
	 * @throws IllegalArgumentException パラメータ null の場合のみ（それ以外は、パラメータの指定が不正か、jsonがおかしいのか判断できないのでABVException）
	 * @throws ABVException
	 */
	public static <T> T get(JSONObject json, Class<T> retType, Object... keyOrIndexs) throws ABVException {
		if (json == null) {
			throw new ABVException(ABVExceptionCode.C_E_CONTENT_2001, "json is null.");
		}
		
		if (keyOrIndexs == null) {
			throw new IllegalArgumentException("key is null. json=" + json);
		}
		
		Object obj = json;
		for (int i = 0; i < keyOrIndexs.length; i++) {
			if (!(keyOrIndexs[i] instanceof String) && !(keyOrIndexs[i] instanceof Integer)) {
				throw new IllegalArgumentException("KeyOrIndex is invalid. index=" + i + " arg=" + keyOrIndexs[i] + " keys=" + join('/', keyOrIndexs) + " json=" + json);
			}
			if (obj instanceof JSONObject) {
				if (keyOrIndexs[i] instanceof String) {
					String key = (String)keyOrIndexs[i];
					JSONObject jsonObj = (JSONObject)obj;
					try {
						obj = jsonObj.get(key); // nullもしくはJSONObjectではないときJSONException
					} catch (JSONException e) {
						throw new ABVException(ABVExceptionCode.C_E_CONTENT_2001, e.getMessage() + " keys=" + join('/', keyOrIndexs) + " json=" + json);
					}
					if (i == keyOrIndexs.length - 1) {
						if (obj instanceof JSONArray && (retType.equals(int.class) || retType.equals(Integer.class))) { // 配列の場合カウントを返す
							return (T)(Integer)((JSONArray)obj).length();
						}
						else if (retType.equals(String.class)) {
							return (T) obj.toString();
						}
						else if ((retType.equals(long.class) || retType.equals(Long.class)) && StringUtil.isLongNumber(obj.toString())) {
							return (T)(Long)Long.parseLong(obj.toString());
						}
						else if ((retType.equals(int.class) || retType.equals(Integer.class)) && StringUtil.isLongNumber(obj.toString())) {
							return (T)(Integer)Integer.parseInt(obj.toString());
						}
						else if ((retType.equals(double.class) || retType.equals(Double.class)) && StringUtil.isReal(obj.toString())) {
							return (T)(Double)Double.parseDouble(obj.toString());
						}
						else if (retType.isAssignableFrom(obj.getClass())) {
							return (T)obj; // TではないときClassCastExceptionは投げられない
						}
						else {
							throw new ABVException(ABVExceptionCode.C_E_CONTENT_2001, obj.getClass() + " not able to assign to " + retType.getName() + " key=" + key + " keys=" + join('/', keyOrIndexs) + " json=" + json);
						}
					}
				}
				else {
					throw new ABVException(ABVExceptionCode.C_E_CONTENT_2001, "KeyOrIndex is invalid. index=" + i + " arg=" + keyOrIndexs[i] + " keys=" + join('/', keyOrIndexs) + " json=" + json);
				}
			}
			else if (obj instanceof JSONArray) {
				if (keyOrIndexs[i] instanceof Integer) {
					Integer index = (Integer)keyOrIndexs[i];
					try {
						obj = ((JSONArray)obj).get(index);
					} catch (JSONException e) {
						throw new ABVException(ABVExceptionCode.C_E_CONTENT_2001, e.getMessage() + " keys=" + join('/', keyOrIndexs) + " json=" + json);
					}
				}
				else {
					throw new ABVException(ABVExceptionCode.C_E_CONTENT_2001, "KeyOrIndex is invalid. index=" + i + " arg=" + keyOrIndexs[i] + " keys=" + join('/', keyOrIndexs) + " json=" + json);
				}
			}
			else {
				throw new ABVException(ABVExceptionCode.C_E_CONTENT_2001, "KeyOrIndex is invalid. index=" + i + " arg=" + keyOrIndexs[i] + " keys=" + join('/', keyOrIndexs) + " json=" + json);
			}
		}
		
		throw new ABVException(ABVExceptionCode.C_E_CONTENT_2001, "No chance to come here."); //本来ここに来ることはない
	}

	private static String join(char delimiter, Object[] array) {
		if (array == null) {
			return "";
		}
		
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < array.length; i++) {
			Object object = array[i];
			if (object != null) {
				sb.append(object);
				sb.append(delimiter);
			}
		}
		if (sb.charAt(sb.length() - 1) == delimiter) {
			sb.deleteCharAt(sb.length() - 1);
		}
		
		return sb.toString();
	}

}
