/*
 * Copyright 2006 Takahiro Nakamura.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package woolpack.el.convert;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;

import woolpack.el.ELTargetRuntimeException;
import woolpack.text.BooleanFormat;
import woolpack.text.ToCharacterFormat;
import woolpack.text.ToSQLDateFormat;
import woolpack.text.ToStringFormat;
import woolpack.utils.SwitchBuilder;
import woolpack.utils.Switchable;

/**
 * 単純型を変換する{@link Convertable}。
 * @author nakamura
 *
 */
public class SimpleConverter implements Convertable {
	private final Switchable<Class,Convertable> parseSwitchable;
	
	private final NumberFormat numberFormat;
	private final DateFormat dateFormat;
	private final BooleanFormat booleanFormat;
	
	/**
	 * コンストラクタ。
	 * @param numberFormat 数値フォーマット。
	 * @param dateFormat 日付フォーマット。
	 * @param booleanFormat 真偽値フォーマット。
	 * @throws NullPointerException 引数のいずれかが null の場合。
	 */
	public SimpleConverter(final NumberFormat numberFormat, final DateFormat dateFormat, final BooleanFormat booleanFormat){
		numberFormat.getClass();
		
		this.numberFormat = numberFormat;
		this.dateFormat = dateFormat;
		this.booleanFormat = booleanFormat;
		
		parseSwitchable = new SwitchBuilder<Class,Convertable>()
		.put(String.class, new Convertable(){
			private final Switchable<Class,Convertable> formatSwitchable = new SwitchBuilder<Class,Convertable>()
			.put(Boolean.class, new FormatConverter(booleanFormat))
			.put(java.util.Date.class, new FormatConverter(dateFormat))
			.put(Number.class, new FormatConverter(numberFormat))
			.put(Byte.class)
			.put(Short.class)
			.put(Integer.class)
			.put(Long.class)
			.put(Float.class)
			.put(Double.class)
			.put(BigInteger.class)
			.put(BigDecimal.class)
			.get(new FormatConverter(new ToStringFormat()));
			public Object convert(final Object beforeValue, final Class toType, final String propertyName) {
				return formatSwitchable.get(beforeValue.getClass()).convert(beforeValue, toType, propertyName);
			}})
		.put(Boolean.class, new ParseConverter(booleanFormat))
		.put(Character.class, new ParseConverter(new ToCharacterFormat()))
		.put(java.sql.Date.class, new ParseConverter(new ToSQLDateFormat(dateFormat)))
		.put(java.util.Date.class, new ParseConverter(dateFormat))
		.put(Number.class, new Convertable(){
			public Object convert(final Object beforeValue, final Class toType, final String propertyName) {
				final Number number;
				if(Number.class.isAssignableFrom(beforeValue.getClass())){
					number = (Number)beforeValue;
				}else{
					try {
						number = numberFormat.parse(beforeValue.toString());
					} catch (final ParseException e) {
						throw new RuntimeException(e);
					}
				}
				return NumberConverter.convert(number, toType);
			}})
		.put(Byte.class)
		.put(Short.class)
		.put(Integer.class)
		.put(Long.class)
		.put(Float.class)
		.put(Double.class)
		.put(BigInteger.class)
		.put(BigDecimal.class)
		.get(ConvertConstants.ECHO);
	}

	public Object convert(final Object beforeValue, final Class toType, final String propertyName)
			throws ELTargetRuntimeException {
		return parseSwitchable.get(toType).convert(beforeValue, toType, propertyName);
	}

	public BooleanFormat getBooleanFormat() {
		return booleanFormat;
	}

	public DateFormat getDateFormat() {
		return dateFormat;
	}

	public NumberFormat getNumberFormat() {
		return numberFormat;
	}
}
