package org.prevayler.implementation;
import org.prevayler.foundation.Chunk;
import org.prevayler.foundation.serialization.Serializer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Serializable;
import java.util.Date;

public abstract class Capsule implements Serializable {
  private final byte[] _serialized;
  protected Capsule(  Object transaction,  Serializer journalSerializer){
    try {
      ByteArrayOutputStream bytes=new ByteArrayOutputStream();
      journalSerializer.writeObject(bytes,transaction);
      _serialized=bytes.toByteArray();
    }
 catch (    Exception exception) {
      throw new Error("Unable to serialize transaction",exception);
    }
  }
  protected Capsule(  byte[] serialized){
    _serialized=serialized;
  }
  /** 
 * Get the serialized representation of the transaction. Callers must not modify the returned array.
 */
  public byte[] serialized(){
    return _serialized;
  }
  /** 
 * Deserialize the contained Transaction or TransactionWithQuery.
 */
  public Object deserialize(  Serializer journalSerializer){
    try {
      return journalSerializer.readObject(new ByteArrayInputStream(_serialized));
    }
 catch (    Exception exception) {
      throw new Error("Unable to deserialize transaction",exception);
    }
  }
  /** 
 * Execute a freshly deserialized copy of the transaction. This method will synchronize on the prevalentSystem
 * while running the transaction but after deserializing it.
 */
  public void executeOn(  Object prevalentSystem,  Date executionTime,  Serializer journalSerializer){
    Object transaction=deserialize(journalSerializer);
synchronized (prevalentSystem) {
      justExecute(transaction,prevalentSystem,executionTime);
    }
  }
  /** 
 * Actually execute the Transaction or TransactionWithQuery. The caller
 * is responsible for synchronizing on the prevalentSystem.
 */
  protected abstract void justExecute(  Object transaction,  Object prevalentSystem,  Date executionTime);
  /** 
 * Make a clean copy of this capsule that will have its own query result fields.
 */
  public abstract Capsule cleanCopy();
  Chunk toChunk(){
    Chunk chunk=new Chunk(_serialized);
    chunk.setParameter("withQuery",String.valueOf(this instanceof TransactionWithQueryCapsule));
    return chunk;
  }
  static Capsule fromChunk(  Chunk chunk){
    boolean withQuery=Boolean.valueOf(chunk.getParameter("withQuery")).booleanValue();
    if (withQuery) {
      return new TransactionWithQueryCapsule(chunk.getBytes());
    }
 else {
      return new TransactionCapsule(chunk.getBytes());
    }
  }
}
