package se.lth.control.labcomm2006;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class EncoderChannel implements Encoder {

  private Writer writer;
  private ByteArrayOutputStream bytes;
  private DataOutputStream data;
  private EncoderRegistry registry;

  public EncoderChannel(Writer writer) throws IOException {
    this.writer = writer;
    bytes = new ByteArrayOutputStream();
    data = new DataOutputStream(bytes);
    registry = new EncoderRegistry();
  }

  public EncoderChannel(OutputStream writer) throws IOException {
    this(new WriterWrapper(writer));
  }

  public void register(SampleDispatcher dispatcher) throws IOException {
    int index = registry.add(dispatcher);
    encodePacked32(Constant.SAMPLE);
    encodePacked32(index);
    encodeString(dispatcher.getName());
    byte[] signature = dispatcher.getSignature();
    for (int i = 0 ; i < signature.length ; i++) {
      encodeByte(signature[i]);
    }
    end(null);
  }

  public void begin(Class<? extends Sample> c) throws IOException {
    encodePacked32(registry.getTag(c));
  }

  public void end(Class<? extends Sample> c) throws IOException {
    data.flush();
    //XXX when writer was a stream, it was probably a bit more GC efficient:
    //bytes.writeTo(writer);
    writer.write(bytes.toByteArray());
    bytes.reset();
  }

  public void encodeBoolean(boolean value) throws IOException{
    data.writeBoolean(value);
  }

  public void encodeByte(byte value) throws IOException {
    data.writeByte(value);
  }

  public void encodeShort(short value) throws IOException {
    data.writeShort(value);
  }

  public void encodeInt(int value) throws IOException {
    data.writeInt(value);
  }

  public void encodeLong(long value) throws IOException {
    data.writeLong(value);
  }

  public void encodeFloat(float value) throws IOException {
    data.writeFloat(value);
  }

  public void encodeDouble(double value) throws IOException {
    data.writeDouble(value);
  }

  public void encodeString(String value) throws IOException {
    data.writeShort(0); // HACK...
    data.writeUTF(value);
  }

  /**
     method for API harmonization with labcomm2014.
     Labcomm2006 encodes lengths etc as 32 bit ints.
  */
  public void encodePacked32(long value) throws IOException {
    if(value > Integer.MAX_VALUE) {
      throw new IllegalArgumentException("Value too large, must fit in 32 bits");
    }
    encodeInt((int) value);
  }
}