package gov.va.med.cds.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.dom4j.CDATA;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;


public class QueryCacheValue 
{
	private long lastUpdateTime;
	private List<Object> cachedValue;
	
	@SuppressWarnings("unchecked")
	public QueryCacheValue(List<Object> results)
	{
		if ( results==null ) throw new IllegalArgumentException("The constructor argument results cannot be null");
		this.cachedValue = (List<Object>)copy(results);
		lastUpdateTime = System.currentTimeMillis();		
	}
	
	
	public long getLastUpdateTime() {
		return lastUpdateTime;
	}

	@SuppressWarnings("unchecked")
	public List<Object> getCachedValue() 
	{
		return (List<Object>)copy(cachedValue);
	}

	/** indicates whether this QueryCacheValue's age is greater than the argument time in seconds. **/
	public boolean exceedsMaxAge( long maxAgeInSeconds)
	{
		if ( maxAgeInSeconds<=0) return false;
		long maxAgeTime = maxAgeInSeconds*1000;
		long currentTime = System.currentTimeMillis();
		return (currentTime - maxAgeTime)>lastUpdateTime;
	}
	
	@Override
	public String toString()
	{
		return toElement().asXML();
	}
	
	public Element toElement()
	{
		DocumentFactory df = new DocumentFactory();
		Element e = df.createElement(getClass().getSimpleName());
		
		Date dt = new Date();
		dt.setTime(lastUpdateTime);
		e.add(df.createAttribute(e, "lastUpdateTime", dt.toString()));
		Element contents = df.createElement("cachedValue");
		for ( Object item: getCachedValue())
		{
			if ( item instanceof Element ) contents.add((Element)item);
			else
			{
				CDATA cdata = df.createCDATA(item.toString());
				contents.add(cdata);				
			}
		}
		e.add(contents);
		return e;
	}
	
	protected Object cloneList(List<Object> list) 
	{     
		List<Object> clonedList = new ArrayList<Object>(list.size());     
		for(Object item: list)
		{
			if ( item instanceof Element ) clonedList.add( ((Element)item).clone() );
			else clonedList.add(copy(item));			
		}
		return clonedList; 
	} 
	
	@SuppressWarnings("unchecked")
	public Object copy(Object original) 
    {
		if ( original instanceof List<?>) return cloneList((List<Object>)original);
    	if ( original instanceof Element) return ((Element)original).clone();
    	if ( original instanceof Serializable) return copyViaSerialization((Serializable)original);
    	throw new RuntimeException("object to copy ["+original+"] could not be copied via cloning nor by Serialization");
    }

	protected Object copyViaSerialization( Serializable original )
	{
		if ( original==null ) throw new NullPointerException("Serializable argument cannot be null");
		
        try 
        {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(bos);
            out.writeObject(original);
            out.flush();
            out.close();
            ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
            return in.readObject();
        }
        catch(Exception e) 
        {
        	throw new RuntimeException(e);
        }
	}
	
	
	@Override
	public boolean equals(Object other) 
	{
		try
		{
			return cachedValue.equals(((QueryCacheValue)other).cachedValue);
		}
		catch ( Exception e)
		{
			return false;
		}
	}

	@Override
	public int hashCode() 
	{
		return Arrays.hashCode(new Object[] { cachedValue });
	}
	

}
