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


package gov.va.med.fw.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.SerializationException;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.lang.SystemUtils;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.core.io.ClassPathResource;



import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.service.trigger.TriggerEvent;

/**
 * General utility to aid in serializing and deserializing Classes to files.
 * 
 * <p>When deserializing .ser files, it looks (for the .ser files) first in CLASSPATH
 * and then in a relative file location.
 * 
 * Created Feb 27, 2007 3:53:29 PM
 * @author DNS   BOHMEG
 */
public class ObjectVersioningUtil extends AbstractComponent {
	private static String FILE_EXTENSION = ".ser";
	private static String FILE_LOCATION_ROOT = "versionedObjects";
	private static String FILE_LOCATION_RELATIVE = "." + 
		SystemUtils.FILE_SEPARATOR + "config" +
		SystemUtils.FILE_SEPARATOR + "test" +
		SystemUtils.FILE_SEPARATOR + "data" +
		SystemUtils.FILE_SEPARATOR + FILE_LOCATION_ROOT +
		SystemUtils.FILE_SEPARATOR;
		
	
	private static InputStream getSerializedFileInputStream(String className) throws IOException {
		InputStream inputStream = null;
		// first, look in Classpath....if not there, look in relative directory
		ClassPathResource resource = new ClassPathResource(FILE_LOCATION_ROOT +
				SystemUtils.FILE_SEPARATOR + className + FILE_EXTENSION);
		if(resource.exists()) {
			inputStream = resource.getInputStream();
		} else {
			File file = new File(FILE_LOCATION_RELATIVE + className + FILE_EXTENSION);
			if(file.exists())
				inputStream = new FileInputStream(file);
		}
		if(inputStream == null)
			throw new IOException("Unable to create an InputStream for Class " + className +
					" (perhaps the serialized file is not in the path)");
		return inputStream;
	}
	
	public static void deserializeFiles(Class[] targetClasses) throws Exception {
		String className = null;
		List failed = new ArrayList();
		
		for(int i=0; i< targetClasses.length; i++) {
			className = targetClasses[i].getName();
			InputStream istream = null;
			try {
				istream = getSerializedFileInputStream(className);
				System.out.println("Deserialization passed for: " +
						SerializationUtils.deserialize(istream).getClass().getName());
			} catch(Exception e) {
				failed.add(className);
				System.out.println("Deserialization failed for [" + className + "] with exception: " + e);
			} finally {
				if (istream != null) {
					IOUtils.closeQuietly(istream);
				}
			}
		}
		
		if(!failed.isEmpty())
			throw new SerializationException("Failed deserialization results: " + failed);
	}
	 
	
	/* This regenerates the .ser files for testing the evolution of classes.
	 * Do not run this unless you intend to version control the output .ser
	 * files.
	 */ 
	public static void generateSerializedFiles(Class[] targetClasses, String optionalLabel, Object objectFactory)
		throws Exception {
		String className = null;
		List failed = new ArrayList();
		
		File fileLocationRoot = new File(FILE_LOCATION_RELATIVE);
		if(!fileLocationRoot.exists())
			fileLocationRoot.mkdir();
			
		Serializable targetObject = null;
		for(int i=0; i< targetClasses.length; i++) {
			className = targetClasses[i].getName();
			
			try {
				targetObject = (Serializable) Reflector.invoke(objectFactory, "generate" +
						ClassUtils.getShortClassName(targetClasses[i]), null);
				
				// attempt to set the optionalLabel somewhere in the graph for build tracking
				if(targetObject instanceof TriggerEvent)
					((TriggerEvent) targetObject).setName(optionalLabel);
				if(targetObject instanceof BeanNameAware)
					((BeanNameAware) targetObject).setBeanName(optionalLabel);
					
				createSerializedFile(targetObject);
			} catch(Exception e) {
				failed.add(className);
				e.printStackTrace();
				System.out.println("Unable to create serialized file for [" +  className + "] due to exception: " + e);						
			}
		}
		if(!failed.isEmpty())
			throw new SerializationException("Failed serialization results: " + failed);
	}
	
	private static void createSerializedFile(Serializable object) throws Exception {
		FileOutputStream out = null;
		try {
			out = new FileOutputStream(FILE_LOCATION_RELATIVE + 
					object.getClass().getName() + FILE_EXTENSION);
			SerializationUtils.serialize(object, out);
			System.out.println("Serialized class to file: " + object.getClass().getName());
		} finally {
			if (out != null) {
				IOUtils.closeQuietly(out);
			}
		}
	}	
}
