package com.jscape.util.io;

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

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

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

    private static final byte[] WINDOWS_EOL = {'\r', '\n'};
    private static final byte[] UNIX_EOL = {'\n'};
    private static final byte[] MAC_EOL = {'\r'};

    private final OutputStream base;
    private final byte[] baseEol;
    private final byte[] targetEol;
    private final ShiftByteBuffer buffer;

    public static EolFilterOutputStream commonEolFilterStream(OutputStream base, byte[] targetEol) {
        return new EolFilterOutputStream(new EolFilterOutputStream(new EolFilterOutputStream(base, UNIX_EOL, targetEol), MAC_EOL, UNIX_EOL), WINDOWS_EOL, targetEol);
    }

    public EolFilterOutputStream(OutputStream base, byte[] baseEol, byte[] targetEol) {
        assertNotNull(base);
        this.base = base;

        assertGreater(baseEol.length, 0, "Bad base EOL length.");
        this.baseEol = baseEol;

        assertNotNull(targetEol);
        this.targetEol = targetEol;

        this.buffer = new ShiftByteBuffer(this.baseEol.length);
    }

    @Override
    public void write(int data)
            throws IOException {
        this.buffer.put(data);

        if (this.buffer.equalsTo(this.baseEol)) {
            this.buffer.clear();
            this.base.write(this.targetEol);
        } else if (this.buffer.full()) {
            data = this.buffer.get();
            this.base.write(data);
        }
    }

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

    @Override
    public void close()
            throws IOException {
        try {
            while (!this.buffer.empty()) {
                int data = this.buffer.get() & 0xFF;
                this.base.write(data);
            }
        } finally {
            this.base.close();
        }
    }
}
