package minejava.reg.io;

import minejava.reg.io.BufferedOutputStream;
import minejava.reg.io.IOException;
import minejava.reg.io.InputStream;
import minejava.reg.io.OutputStream;
import minejava.reg.io.OutputStreamWriter;
import minejava.reg.io.Writer;
import minejava.reg.nio.charset.Charset;
import static minejava.reg.base.Preconditions.checkNotNull;

public abstract class ByteSink{

    protected ByteSink(){}

    public CharSink asCharSink(Charset charset){
    return new AsCharSink(charset);
  }

    public abstract OutputStream openStream() throws IOException;

    public OutputStream openBufferedStream() throws IOException{
    OutputStream out = openStream();
    return (out instanceof BufferedOutputStream)
        ? (BufferedOutputStream) out
        : new BufferedOutputStream(out);
  }

    public void write(byte[] bytes) throws IOException{
    checkNotNull(bytes);

    Closer closer = Closer.create();
    try{
      OutputStream out = closer.register(openStream());
      out.write(bytes);
      out.flush();
    }catch (Throwable e){
      throw closer.rethrow(e);
    }finally{
      closer.close();
    }
  }

    public long writeFrom(InputStream input) throws IOException{
    checkNotNull(input);

    Closer closer = Closer.create();
    try{
      OutputStream out = closer.register(openStream());
      long written = ByteStreams.copy(input, out);
      out.flush();
      return written;
    }catch (Throwable e){
      throw closer.rethrow(e);
    }finally{
      closer.close();
    }
  }

  private final class AsCharSink extends CharSink{

    private final Charset charset;

    private AsCharSink(Charset charset){
      this.charset = checkNotNull(charset);
    }

    @Override
    public Writer openStream() throws IOException{
      return new OutputStreamWriter(ByteSink.this.openStream(), charset);
    }

    @Override
    public String toString(){
      return ByteSink.this.toString() + ".asCharSink(" + charset + ")";
    }
  }
}