package water;

import java.io.*;

/**
 * H2O uses Iced classes as the primary means of moving Java Objects around
 * the cluster.
 * <p>
 * Auto-serializer base-class using a delegator pattern (the faster option is
 * to byte-code gen directly in all Iced classes, but this requires all Iced
 * classes go through a ClassLoader).
 * <p>
 * Iced is a marker class, and {@link Freezable} is the companion marker
 * interface.  Marked classes have 2-byte integer type associated with them,
 * and an auto-genned delegate class created to actually do byte-stream and
 * JSON serialization and deserialization.  Byte-stream serialization is
 * extremely dense (includes various compressions), and typically memory-bandwidth
 * bound to generate.
 * <p>
 * During startup time the Weaver creates a parallel set of classes called
 * (classname)$Icer.  These provide bytestream and JSON serializers
 * and deserializers which get called by AutoBuffer.write* and AutoBugger.read*.
 * <p>
 * To debug the automagic serialization code create a transient field in your Iced
 * class called DEBUG_WEAVER.  The generated source code will get written to STDOUT:
 * <p>
 * {@code
 * transient int DEBUG_WEAVER = 1;
 * }
 * @see Freezable
 * @see water.Weaver
 * @see water.AutoBuffer
 */
abstract public class Iced<D extends Iced> implements Freezable<D>, Externalizable {

  // The serialization flavor / delegate.  Lazily set on first use.
  transient private volatile short _ice_id = 0;


  @Override
  public byte [] asBytes(){
    return write(new AutoBuffer()).buf();
  }

  @Override
  public D reloadFromBytes(byte [] ary){
    return read(new AutoBuffer(ary));
  }

  // Return the icer for this instance+class.  Will set on 1st use.
  private Icer<D> icer() {
    int id = _ice_id;
    int tyid;
    if(id != 0) assert id == (tyid =TypeMap.onIce(this)):"incorrectly cashed id " + id + ", typemap has " + tyid + ", type = " + getClass().getName();
    return TypeMap.getIcer(id!=0 ? id : (_ice_id=(short)TypeMap.onIce(this)),this); 
  }

  /** Standard "write thyself into the AutoBuffer" call, using the fast Iced
   *  protocol.  Real work is in the delegate {@link Icer} classes.
   *  @return Returns the original {@link AutoBuffer} for flow-coding. */
  @Override final public AutoBuffer write    (AutoBuffer ab) { return icer().write    (ab,(D)this); }
  /** Standard "write thyself into the AutoBuffer" call, using JSON.  Real work
   *  is in the delegate {@link Icer} classes.
   *  @return Returns the original {@link AutoBuffer} for flow-coding. */
  @Override final public AutoBuffer writeJSON(AutoBuffer ab) { return icer().writeJSON(ab,(D)this); }
  /** Standard "read thyself from the AutoBuffer" call, using the fast Iced protocol.  Real work
   *  is in the delegate {@link Icer} classes.
   *  @return Returns the original {@link AutoBuffer} for flow-coding. */
  @Override final public D read    (AutoBuffer ab) { return icer().read    (ab,(D)this); }
  /** Standard "read thyself from the AutoBuffer" call, using JSON.  Real work
   *  is in the delegate {@link Icer} classes.
   *  @return Returns the original {@link AutoBuffer} for flow-coding. */
  @Override final public D readJSON(AutoBuffer ab) { return icer().readJSON(ab,(D)this); }

  /** Helper for folks that want a JSON String for this object. */
  final public String toJsonString() {
    return new String(this.writeJSON(new AutoBuffer()).buf());
  }

  /** Returns a small dense integer, which is cluster-wide unique per-class.
   *  Useful as an array index.
   *  @return Small integer, unique per-type */
  @Override final public int frozenType() { return icer().frozenType(); }
  /** Clone, without the annoying exception */
  @Override public final D clone() {
    try { return (D)super.clone(); }
    catch( CloneNotSupportedException e ) { throw water.util.Log.throwErr(e); }
  }

  /** Copy over cloned instance 'src' over 'this', field by field. */
  protected void copyOver( D src ) { icer().copyOver((D)this,src); }

  ///////////////////////////////////
  // TODO: make all of these protected!
  ///////////////////////////////////

  /** Implementation of the {@link Iced} serialization protocol, only called by
   *  auto-genned code.  Not intended to be called by user code.  Override only
   *  for custom Iced serializers. */
  //noninspection UnusedDeclaration
//  @Override public AutoBuffer write_impl( AutoBuffer ab ) { return ab; }
  /** Implementation of the {@link Iced} serialization protocol, only called by
   *  auto-genned code.  Not intended to be called by user code.  Override only
   *  for custom Iced serializers. */
  //noninspection UnusedDeclaration
//  @Override public D read_impl( AutoBuffer ab ) { return (D)this; }
  /** Implementation of the {@link Iced} serialization protocol, only called by
   *  auto-genned code.  Not intended to be called by user code.  Override only
   *  for custom Iced serializers. */
  //noninspection UnusedDeclaration
//  public AutoBuffer writeJSON_impl( AutoBuffer ab ) { return ab; }
  /** Implementation of the {@link Iced} serialization protocol, only called by
   *  auto-genned code.  Not intended to be called by user code.  Override only
   *  for custom Iced serializers. */
  //noninspection UnusedDeclaration
//  @Override public D readJSON_impl( AutoBuffer ab ) { return (D)this; }

  // Java serializers use H2Os Icing
  @Override public void readExternal( ObjectInput ois )  throws IOException, ClassNotFoundException {
    int x = ois.readInt();
    byte[] buf = MemoryManager.malloc1(x);
    ois.readFully(buf);
    read(new AutoBuffer(buf));
  }

  @Override public void writeExternal( ObjectOutput oos ) throws IOException {
    byte[] buf = write(new AutoBuffer()).buf();
    oos.writeInt(buf.length);
    oos.write(buf);
  }

}

@@ -3420,24 +3420,329 @@
 uf());%0A  %7D%0A%0A
+  /** Pretty print every Iced object, better than printing the  */%0A  @Override public String toString() %7B%0A    String str = toJsonString();%0A    if (str.length() %3C 10000) %7B%0A      return super.toString() + %22: %22 + str;%0A    %7D else %7B%0A      return super.toString() + %22: %5Btoo large to print as JSON%5D%22;%0A    %7D%0A  %7D%0A%0A
   /** Return

Print Iced objects as JSON in their toString(), but only if shorter than 10k.