/*
@license.text@
 */
package com.hammurapi.convert;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import com.hammurapi.common.Context;

/**
 * @author Pavel Vlasov
 * @version $Revision: 1.7 $
 */
public class FoundationAtomicConvertersBundle implements AtomicConvertersBundle {

	private List<AtomicConverter<? extends Object,? extends Object>> converters=new ArrayList<AtomicConverter<? extends Object,? extends Object>>();
	
	public Collection<AtomicConverter<? extends Object,? extends Object>> getConverters() {
		return Collections.unmodifiableCollection(converters);
	}

    /**
     * Creates new composite converter populated by default with some generic converters.
     */
	@SuppressWarnings("unchecked")
	public FoundationAtomicConvertersBundle() {
		addConverter(Number.class, byte.class, "byteValue", null);
		addConverter(Number.class, double.class, "doubleValue", null);
		addConverter(Number.class, float.class, "floatValue", null);
		addConverter(Number.class, int.class, "intValue", null);
		addConverter(Number.class, long.class, "longValue", null);
		addConverter(Number.class, short.class, "shortValue", null);
		
		addConverter(Number.class, Byte.class, "byteValue", null);
		addConverter(Number.class, Double.class, "doubleValue", null);
		addConverter(Number.class, Float.class, "floatValue", null);
		addConverter(Number.class, Integer.class, "intValue", null);
		addConverter(Number.class, Long.class, "longValue", null);
		addConverter(Number.class, Short.class, "shortValue", null);
		
		addConverter(Object.class, String.class, "toString", null);
		
		
		addConverter(Boolean.class, boolean.class, "booleanValue", null);
		
		converters.addAll(ReflectionConverter.discoverConstructorConverters(String.class));
		converters.addAll(ReflectionConverter.discoverConstructorConverters(Integer.class));
		converters.addAll(ReflectionConverter.discoverConstructorConverters(Long.class));
		converters.addAll(ReflectionConverter.discoverConstructorConverters(Double.class));
		converters.addAll(ReflectionConverter.discoverConstructorConverters(Float.class));
		converters.addAll(ReflectionConverter.discoverConstructorConverters(Byte.class));
		converters.addAll(ReflectionConverter.discoverConstructorConverters(Short.class));
		converters.addAll(ReflectionConverter.discoverConstructorConverters(BigDecimal.class));
		
		try {
			addConverter(File.class, FileReader.class, null, FileReader.class.getConstructor(File.class));
			addConverter(File.class, FileInputStream.class, null, FileInputStream.class.getConstructor(File.class));
			
			addConverter(File.class, FileOutputStream.class, null, FileOutputStream.class.getConstructor(File.class));
			addConverter(File.class, FileWriter.class, null, FileWriter.class.getConstructor(File.class));
			
			addConverter(InputStream.class, InputStreamReader.class, null, InputStreamReader.class.getConstructor(InputStream.class));
			addConverter(URL.class, InputStream.class, "openStream", null);
		} catch (SecurityException e) {
			throw new ConversionException(e);
		} catch (NoSuchMethodException e) {
			throw new ConversionException(e);
		}
		
		converters.add(new AtomicConverterBase(String.class, boolean.class) {

			public Object convert(Object source, Converter master, Context context, ClassLoader classLoader) {
				if (source==null) {
					return Boolean.FALSE;
				}
				
				String str = ((String) source).trim();
				if (str.length()==0 || "no".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str) || "0".equals(source) || "".equals(source)) {
					return Boolean.FALSE;
				} else if ("yes".equalsIgnoreCase(str) || "true".equalsIgnoreCase(str) || "1".equals(source) || "on".equals(source)) {
					return Boolean.TRUE;
				} 
				
				return null;
			}
			
		});
		
		converters.add(new AtomicConverterBase<String,Class>(String.class, Class.class) {

			public Class convert(String source, Converter master, Context context, ClassLoader classLoader) {
				if (source==null) {
					return null;
				}
				
				try {
					return classLoader.loadClass(source.trim());
				} catch (ClassNotFoundException e) {
					throw new ConversionException(e);
				}
			}
			
		});
		
		converters.add(new AtomicConverterBase<ByteArrayOutputStream,byte[]>(ByteArrayOutputStream.class, byte[].class) {

			@Override
			public byte[] convert(ByteArrayOutputStream source,	Converter master, Context context, ClassLoader classLoader) {
				if (source==null) {
					return null;
				}
				
				try {
					source.close();
				} catch (IOException e) {
					throw new ConversionException(e);
				}
				
				return source.toByteArray();
			}
			
		});
		
		converters.add(new AtomicConverterBase<ByteArrayOutputStream,InputStream>(ByteArrayOutputStream.class, InputStream.class) {

			@Override
			public InputStream convert(ByteArrayOutputStream source,	Converter master, Context context, ClassLoader classLoader) {
				if (source==null) {
					return null;
				}
				
				try {
					source.close();
				} catch (IOException e) {
					throw new ConversionException(e);
				}
				
				return new ByteArrayInputStream(source.toByteArray());
			}
			
		});
		
		converters.add(new AtomicConverterBase<StringWriter,String>(StringWriter.class, String.class) {

			@Override
			public String convert(StringWriter source,	Converter master, Context context, ClassLoader classLoader) {
				if (source==null) {
					return null;
				}
				
				try {
					source.close();
				} catch (IOException e) {
					throw new ConversionException(e);
				}
				
				return source.toString();
			}
			
		});
		
		converters.add(new AtomicConverterBase<StringWriter, Reader>(StringWriter.class, Reader.class) {

			@Override
			public Reader convert(StringWriter source, Converter master, Context context, ClassLoader classLoader) {
				if (source==null) {
					return null;
				}
				
				try {
					source.close();
				} catch (IOException e) {
					throw new ConversionException(e);
				}
				
				return new StringReader(source.toString());
			}
			
		});
		
		converters.add(new AtomicConverterBase(String.class, byte.class) {

			public Object convert(Object source, Converter master, Context context, ClassLoader classLoader) {
				return new Byte((String) source);
			}
			
		});
		
		converters.add(new AtomicConverterBase(String.class, double.class) {

			public Object convert(Object source, Converter master, Context context, ClassLoader classLoader) {
				return new Double((String) source);
			}
			
		});
		
		converters.add(new AtomicConverterBase(String.class, float.class) {

			public Object convert(Object source, Converter master, Context context, ClassLoader classLoader) {
				return new Float((String) source);
			}
			
		});
		

		converters.add(new AtomicConverterBase(String.class, int.class) {

			public Object convert(Object source, Converter master, Context context, ClassLoader classLoader) {
				return new Integer((String) source);
			}
			
		});
		

		converters.add(new AtomicConverterBase(String.class, long.class) {

			public Object convert(Object source, Converter master, Context context, ClassLoader classLoader) {
				return new Long((String) source);
			}
			
		});
		

		converters.add(new AtomicConverterBase(String.class, short.class) {

			public Object convert(Object source, Converter master, Context context, ClassLoader classLoader) {
				return new Short((String) source);
			}
			
		});
				
		converters.add(new AtomicConverterBase(String.class, Class.class) {

			public Object convert(Object source, Converter master, Context context, ClassLoader classLoader) {
				try {
					return Class.forName((String) source);
				} catch (ClassNotFoundException e) {
					throw new ConversionException(e);
				}
			}
			
		});		
		
    }
		
	private <S, T> void  addConverter(Class<S> source, Class<T> target, String accessor, Constructor<? extends T> constructor) {
		try {
			converters.add(new ReflectionConverter<S, T>(accessor==null ? null : source.getMethod(accessor), constructor));
		} catch (Exception e) {
			throw new ConversionException(e);
		}
	}
		
}
