package com.jscape.util.io;

import java.io.IOException;
import java.io.OutputStream;

import static com.jscape.util.Parameters.assertNotNull;

/**
 * @author Alex Usun
 */
public class CountingOutputStream
        extends OutputStream {

    private final OutputStream base;
    private final boolean owner;
    private final Listener listener;
    private long writtenBytes;
    
    public CountingOutputStream(OutputStream base, boolean owner, Listener listener) {
        assertNotNull(base);
        this.base = base;
        
        this.owner = owner;

        assertNotNull(listener);
        this.listener = listener;

        this.writtenBytes = 0L;
    }

    public CountingOutputStream(OutputStream base, boolean owner) {
        this(base, owner, Listener.NULL);
    }

    public CountingOutputStream(OutputStream base) {
        this(base, true);
    }

    @Override
    public void write(int b)
            throws IOException {
        this.base.write(b);
        ++this.writtenBytes;
        this.listener.onDataWritten(this, this.writtenBytes);
    }

    @Override
    public void write(byte[] data, int offset, int length)
            throws IOException {
        this.base.write(data, offset, length);
        this.writtenBytes += length;
        this.listener.onDataWritten(this, this.writtenBytes);
    }

    @Override
    public void flush()
            throws IOException {
        this.base.flush();
    }

    @Override
    public void close()
            throws IOException {
        if (this.owner) {
            this.base.close();
        }

        this.listener.onClosed(this, this.writtenBytes);
    }

    public long writtenBytes() {
        return this.writtenBytes;
    }
    
    @Override
    public String toString() {
        return "CountingOutputStream {"
               + "base=" + this.base
               + ", owner=" + this.owner
               + ", writtenBytes=" + this.writtenBytes
               + '}';
    }

    public interface Listener {

        Listener NULL = new Listener() {
            @Override
            public void onDataWritten(CountingOutputStream stream, long writtenBytes) {
                // empty
            }

            @Override
            public void onClosed(CountingOutputStream stream, long writtenBytes) {
                // empty
            }
        };

        void onDataWritten(CountingOutputStream stream, long writtenBytes);

        void onClosed(CountingOutputStream stream, long writtenBytes);
    }
}
