package org.prevayler.foundation;
import org.prevayler.foundation.serialization.JavaSerializer;
import org.prevayler.foundation.serialization.Serializer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class DeepCopier {
  /** 
 * Same as deepCopy(original, new JavaSerializer()).
 * @throws ClassNotFoundException
 * @throws IOException
 */
  public static Object deepCopy(  Object original){
    return deepCopy(original,new JavaSerializer());
  }
  /** 
 * Produce a deep copy of the given object. Serializes the entire object to a byte array in memory. Recommended for
 * relatively small objects, such as individual transactions.
 */
  public static Object deepCopy(  Object original,  Serializer serializer){
    try {
      ByteArrayOutputStream byteOut=new ByteArrayOutputStream();
      serializer.writeObject(byteOut,original);
      ByteArrayInputStream byteIn=new ByteArrayInputStream(byteOut.toByteArray());
      return serializer.readObject(byteIn);
    }
 catch (    Exception e) {
      Cool.unexpected(e);
      return null;
    }
  }
  /** 
 * Produce a deep copy of the given object. Serializes the object through a pipe between two threads. Recommended for
 * very large objects, such as an entire prevalent system. The current thread is used for serializing the original
 * object in order to respect any synchronization the caller may have around it, and a new thread is used for
 * deserializing the copy.
 */
  public static Object deepCopyParallel(  Object original,  Serializer serializer) throws IOException, ClassNotFoundException {
    PipedOutputStream outputStream=new PipedOutputStream();
    PipedInputStream inputStream=new PipedInputStream(outputStream);
    Receiver receiver=new Receiver(inputStream,serializer);
    try {
      serializer.writeObject(outputStream,original);
    }
  finally {
      outputStream.close();
    }
    return receiver.getResult();
  }
private static class Receiver extends Thread {
    private InputStream _inputStream;
    private Serializer _serializer;
    private Object _result;
    private IOException _ioException;
    private ClassNotFoundException _classNotFoundException;
    private RuntimeException _runtimeException;
    private Error _error;
    public Receiver(    InputStream inputStream,    Serializer serializer){
      _inputStream=inputStream;
      _serializer=serializer;
      start();
    }
    public void run(){
      try {
        _result=_serializer.readObject(_inputStream);
      }
 catch (      IOException e) {
        _ioException=e;
      }
catch (      ClassNotFoundException e) {
        _classNotFoundException=e;
      }
catch (      RuntimeException e) {
        _runtimeException=e;
      }
catch (      Error e) {
        _error=e;
        throw e;
      }
      try {
        while (_inputStream.read() != -1) {
        }
      }
 catch (      IOException e) {
      }
    }
    public Object getResult() throws ClassNotFoundException, IOException {
      try {
        join();
      }
 catch (      InterruptedException e) {
        throw new RuntimeException("Unexpected InterruptedException",e);
      }
      if (_error != null)       throw new RuntimeException("Error during deserialization",_error);
      if (_runtimeException != null)       throw _runtimeException;
      if (_classNotFoundException != null)       throw _classNotFoundException;
      if (_ioException != null)       throw _ioException;
      if (_result == null)       throw new RuntimeException("Deep copy failed in an unknown way");
      return _result;
    }
  }
}
