/********************************************************************
 * Copyright © 2006 VHA. All rights reserved
 ********************************************************************/

package gov.va.med.fw.io;

import gov.va.med.fw.model.AbstractEntity;
import gov.va.med.fw.model.lookup.Lookup;

import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

/**
 * Utility class for RawFileDataContainer implementors.
 * 
 * Created Mar 16, 2006 4:38:24 PM
 * 
 * @author DNS   BOHMEG
 */
public class RawFileDataContainerUtils {
	private static Converter lookupConverter;

	private static Map converters;

	private static Set lookupsToRegister = new HashSet();

	private static Map classLoaderToClassesAutoRegistered = new HashMap();

	public static void autoRegisterConvertersForClasses(Class[] classes) {
		if(classes != null) {
			for(int i=0; i < classes.length; i++) {
				RawFileDataContainerUtils.autoRegisterConvertersForClass(classes[i]);
			}
		}		
	}
	
	public static void autoRegisterConvertersForClass() {
		StackTraceElement[] elements = new Exception().getStackTrace();
		String className = elements[1].getClassName();
		try {
			RawFileDataContainerUtils.autoRegisterConvertersForClass(Class
					.forName(className));
		} catch (Exception e) {
			throw new RuntimeException("Unable to load class: " + className);
		}
	}

	public static void registerConverterForLookupClass(Class clazz) {
		if (!Lookup.class.isAssignableFrom(clazz))
			throw new IllegalArgumentException(
					"Can not register LookupConverter for class that is not assignable from Lookup.class");

		// see if there is a registered Lookup converter
		if (lookupConverter == null) {
			lookupConverter = ConvertUtils.lookup(Lookup.class);
			if (lookupConverter == null) {
				lookupsToRegister.add(clazz); // will be registered later when
												// lookupConverter is detected
				return;
			}
		}

		ConvertUtils.register(lookupConverter, clazz);
	}
	
	public static void changeFileExtensions(String fromExtn, String toExtn, String directoryPath)throws IOException{
		final String fromExtension = fromExtn;
		java.io.File dir = new java.io.File(directoryPath);
        String target = "";
        FilenameFilter filter = new FilenameFilter(){
            public boolean accept(File dir, String name){
                return name.endsWith(fromExtension);
            }
        };
        String[] files = dir.list(filter);
        for(int i=0; i<files.length; i++){
            String extn = getFileExtension(files[i]);
            if(extn.equalsIgnoreCase(fromExtension)){
            	target = files[i].replaceAll(extn, toExtn);
                new File(directoryPath+files[i]).renameTo(new File(directoryPath+target));
            }
        }
	}
	public static String getFileExtension(String f) {
        String ext = "";
        int i = f.lastIndexOf('.');
        if (i > 0 &&  i < f.length() - 1) {
           ext = f.substring(i + 1);
        }
        return ext;
     }

	public synchronized static void cleanUnregisteredLookups(
			Converter registeredLookupConverter) {
		Validate.notNull(registeredLookupConverter,
				"registeredLookupConverter can not be null");
		lookupConverter = registeredLookupConverter;
		if (!lookupsToRegister.isEmpty()) {
			Iterator itr = lookupsToRegister.iterator();
			while (itr.hasNext()) {
				ConvertUtils.register(lookupConverter, (Class) itr.next());
			}
		}
		lookupsToRegister.clear();
	}

	public static void autoRegisterConvertersForClass(Class clazz) {
		// get its properties and register "common" converters
		doAutoRegisterConvertersForClass(clazz, new HashMap());
	}

	private static void doAutoRegisterConvertersForClass(Class clazz,
			Map ignoreTheseClasses) {
		if (ignoreTheseClasses.containsKey(clazz))
			return;

		/*
		 * This ensures we save overheard across multiple calls to this method.
		 * Tracks by ClassLoader since that is how BeanUtilsBean in Commons
		 * BeanUtils does it.
		 */
		ClassLoader currentClassLoader = Thread.currentThread()
				.getContextClassLoader();
		Set classesAutoRegistered = (Set) classLoaderToClassesAutoRegistered
				.get(currentClassLoader);
		if (classesAutoRegistered == null) {
			classesAutoRegistered = new HashSet();
			classLoaderToClassesAutoRegistered.put(currentClassLoader,
					classesAutoRegistered);
		} else if (classesAutoRegistered.contains(clazz)) {
			return;
		}
		classesAutoRegistered.add(clazz);
		
		ignoreTheseClasses.put(clazz, null);
		PropertyDescriptor[] props = PropertyUtils
				.getPropertyDescriptors(clazz);
		Converter wellKnownConverter = null;		
		for (int i = 0; i < props.length; i++) {
			// recurse (if necessary)
			if (AbstractEntity.class.isAssignableFrom(props[i]
					.getPropertyType()))
				doAutoRegisterConvertersForClass(props[i].getPropertyType(),
						ignoreTheseClasses);

			// look for "well-known" converters to register
			wellKnownConverter = converters != null ?
					(Converter) converters.get(props[i].getPropertyType().getName()) : null;
			if (Lookup.class.isAssignableFrom(props[i].getPropertyType())) {
				registerConverterForLookupClass(props[i].getPropertyType());
			} else if (ConvertUtils.lookup(props[i].getPropertyType()) == null &&
					wellKnownConverter != null) {
				ConvertUtils.register(wellKnownConverter, props[i].getPropertyType());
			}
		}
	}

	public static void setConvertedField(Object instance, String property,
			String rawValue) throws Exception {
		if (StringUtils.isNotBlank(rawValue)) {
			// this allows registered Converter instances to come into play
			BeanUtils.copyProperty(instance, property, rawValue);
		}
	}

	public static Lookup convertToLookup(Class clazz, String code) {
		return (Lookup) lookupConverter.convert(clazz, code);
	}

	public static Object[] parseMultipleValues(String rawData,
			String primaryDelimiter, String secondaryDelimiter, int dataSize) {
		if (StringUtils.isBlank(rawData))
			return null;

		String[] splitByPrimary = rawData.split("\\" + primaryDelimiter);
		Object[] groupedData = new Object[splitByPrimary.length];
		if (secondaryDelimiter == null)
			return splitByPrimary;

		String[] splitBySecondaryActual = null;
		String[] splitBySecondary = null;
		for (int i = 0; i < splitByPrimary.length; i++) {
			splitBySecondary = new String[dataSize];
			splitBySecondaryActual = splitByPrimary[i].split("\\"
					+ secondaryDelimiter);
			for (int j = 0; j < splitBySecondaryActual.length; j++) {
				splitBySecondary[j] = splitBySecondaryActual[j];
			}
			groupedData[i] = splitBySecondary;
		}

		/*
		 * System.out.println("originalData: " + rawData); for(int i=0; i<groupedData.length;
		 * i++) { Object[] data = (Object[]) groupedData[i]; for(int j=0; j<
		 * data.length; j++) { System.out.println("groupedData [" + i + ", " + j + " ]: " +
		 * data[j]); } }
		 */

		return groupedData;
	}

	public static String formatMultipleValues(Collection data, String delimiter) {
		StringBuffer buf = new StringBuffer();
		Iterator itr = data != null ? data.iterator() : null;
		Object obj = null;
		while (itr != null && itr.hasNext()) {
			obj = itr.next();
			if (obj instanceof Lookup)
				buf.append(formatLookup((Lookup) obj));
			else
				buf.append(obj);

			if (itr.hasNext())
				buf.append(delimiter);
		}
		return buf.toString();
	}

	public static List parseMultipleValues(String data, String delimiter) {
		List coll = new ArrayList();

		if (StringUtils.isBlank(data))
			return coll;

		String[] tokens = data.split("\\" + delimiter);
		for (int j = 0; tokens != null && j < tokens.length; j++)
			coll.add(tokens[j]);
		return coll;
	}

	public static Set parseMultipleLookupValues(String data, String delimiter,
			Class lookupClass) {
		Set coll = new HashSet();

		if (StringUtils.isBlank(data))
			return coll;

		String[] tokens = data.split("\\" + delimiter);
		for (int j = 0; tokens != null && j < tokens.length; j++)
			coll.add(convertToLookup(lookupClass, tokens[j]));
		return coll;
	}

	public static String formatLookup(Lookup lookup) {
		return lookup != null ? lookup.getCode() : StringUtils.EMPTY;
	}

	/**
	 * @return Returns the converters.
	 */
	public static Map getConverters() {
		return converters;
	}

	/**
	 * @param converters The converters to set.
	 */
	public static void setConverters(Map converters) {
		RawFileDataContainerUtils.converters = converters;
	}
}
