/*
 * Copyright (c) 2009 OrangeSignal.com All rights reserved.
 * 
 * これは Apache ライセンス Version 2.0 (以下、このライセンスと記述) に
 * 従っています。このライセンスに準拠する場合以外、このファイルを使用
 * してはなりません。このライセンスのコピーは以下から入手できます。
 * 
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 * 
 * 適用可能な法律がある、あるいは文書によって明記されている場合を除き、
 * このライセンスの下で配布されているソフトウェアは、明示的であるか暗黙の
 * うちであるかを問わず、「保証やあらゆる種類の条件を含んでおらず」、
 * 「あるがまま」の状態で提供されるものとします。
 * このライセンスが適用される特定の許諾と制限については、このライセンス
 * を参照してください。
 */

package jp.sf.orangesignal.csv.handlers;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * {@link CsvValueConverter} を実装したデフォルトのシンプルな実装クラスを提供します。
 *
 * @author 杉澤 浩二
 */
public class SimpleCsvValueConverter implements CsvValueConverter {

	/**
	 * プリミティブ型とプリミティブ型デフォルト値のマップです。
	 */
	private static final Map<Class<?>, Object> primitiveDefaults;

	private static final Map<String, Boolean> booleanDefaults;

	static {
		primitiveDefaults = new HashMap<Class<?>, Object>();
		primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
		primitiveDefaults.put(Byte.TYPE, Byte.valueOf((byte) 0));
		primitiveDefaults.put(Character.TYPE, Character.valueOf('\u0000'));
		primitiveDefaults.put(Short.TYPE, Short.valueOf((short) 0));
		primitiveDefaults.put(Integer.TYPE, Integer.valueOf(0));
		primitiveDefaults.put(Long.TYPE, Long.valueOf(0L));
		primitiveDefaults.put(Float.TYPE, Float.valueOf(0F));
		primitiveDefaults.put(Double.TYPE, Double.valueOf(0D));

		booleanDefaults = new HashMap<String, Boolean>();
//		booleanDefaults.put("", Boolean.FALSE);
		booleanDefaults.put("0", Boolean.FALSE);
		booleanDefaults.put("1", Boolean.TRUE);
		booleanDefaults.put("false", Boolean.FALSE);
		booleanDefaults.put("true", Boolean.TRUE);
		booleanDefaults.put("f", Boolean.FALSE);
		booleanDefaults.put("t", Boolean.TRUE);
		booleanDefaults.put("no", Boolean.FALSE);
		booleanDefaults.put("yes", Boolean.TRUE);
		booleanDefaults.put("n", Boolean.FALSE);
		booleanDefaults.put("y", Boolean.TRUE);
		booleanDefaults.put("off", Boolean.FALSE);
		booleanDefaults.put("on", Boolean.TRUE);
		booleanDefaults.put("x", Boolean.FALSE);
		booleanDefaults.put("o", Boolean.TRUE);
	}

	/**
	 * 日時書式を保持します。
	 * 
	 * @deprecated 使用を廃止します。1.3 で削除予定。
	 */
	private DateFormat dateFormat;

	/**
	 * デフォルトコンストラクタです。
	 */
	public SimpleCsvValueConverter() {}

	/**
	 * 日時書式を返します。
	 * 
	 * @return 日時書式
	 * @deprecated 使用を廃止します。1.3 で削除予定。
	 */
	public DateFormat getDateFormat() { return dateFormat; }

	/**
	 * 日時書式を設定します。
	 * 
	 * @param dateFormat 日時書式
	 * @deprecated 使用を廃止します。1.3 で削除予定。
	 */
	public void setDateFormat(final DateFormat dateFormat) { this.dateFormat = dateFormat; }

	@Override
	public Object convert(final String str, final Class<?> type) {
		// 入力パラメータを検証します。
		if (type == null) {
			throw new IllegalArgumentException("Class must not be null");
		}

		if (str == null) {
			if (type.isPrimitive()) {
				return primitiveDefaults.get(type);
			}
			return null;
		}
		if (type.equals(String.class)) {
			return str;
		}

		if (type.equals(Boolean.TYPE) || type.equals(Boolean.class)) {
			for (final Map.Entry<String, Boolean> entry : booleanDefaults.entrySet()) {
				if (entry.getKey().equalsIgnoreCase(str)) {
					return entry.getValue();
				}
			}
		} else if (type.equals(Byte.TYPE) || type.equals(Byte.class)) {
			return Byte.valueOf(str);
//		} else if (type.equals(Character.TYPE) || type.equals(Character.class)) {
//			return Character.valueOf(str);
		} else if (type.equals(Short.TYPE) || type.equals(Short.class)) {
			return Short.valueOf(str);
		} else if (type.equals(Integer.TYPE) || type.equals(Integer.class)) {
			return Integer.valueOf(str);
		} else if (type.equals(Long.TYPE) || type.equals(Long.class)) {
			return Long.valueOf(str);
		} else if (type.equals(Float.TYPE) || type.equals(Float.class)) {
			return Float.valueOf(str);
		} else if (type.equals(Double.TYPE) || type.equals(Double.class)) {
			return Double.valueOf(str);
		} else if (type.equals(BigInteger.class)) {
			return new BigInteger(str);
		} else if (type.equals(Number.class) || type.equals(BigDecimal.class)) {
			return new BigDecimal(str);
		} else if (dateFormat != null && Date.class.isAssignableFrom(type)) {
			try {
				return type.getConstructor(Long.TYPE).newInstance(dateFormat.parse(str).getTime());
			} catch (Exception e) {
				throw new IllegalArgumentException(e.getMessage(), e);
			}
		}

		throw new IllegalArgumentException(String.format("Unknown convert type %s", type.getName()));
	}

	@Override
	public String convert(final Object value) {
		if (value == null) {
			return null;
		}
		if ((value instanceof Date) && (dateFormat != null)) {
			return dateFormat.format(value);
		}
		return value.toString();
	}

}
