package zmo.technology.lite.logging;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

public class ZeroLogWriter extends Writer {

    private ByteArrayOutputStream outputStream;

    public ZeroLogWriter() {
        super();
        outputStream = new ByteArrayOutputStream();
    }

    private void ensureOpen() throws IOException {
        if (outputStream == null)
            throw new IOException("Stream closed");
    }

    public static byte[] charsToBytes(char[] chars) {
        Charset cs = Charset.forName("UTF-8");
        CharBuffer cb = CharBuffer.allocate(chars.length);
        cb.put(chars);
        cb.flip();
        ByteBuffer bb = cs.encode(cb);
        return bb.array();
    }

    public static char[] bytesToChars(byte[] bytes) {
        Charset cs = Charset.forName("UTF-8");
        ByteBuffer bb = ByteBuffer.allocate(bytes.length);
        bb.put(bytes).flip();
        CharBuffer cb = cs.decode(bb);
        return cb.array();
    }

    public static byte[] charToByte(char c) {
        byte[] b = new byte[2];
        b[0] = (byte) ((c & 0xFF00) >> 8);
        b[1] = (byte) (c & 0xFF);
        return b;
    }

    public static char byteToChar(byte[] b) {
        int hi = (b[0] & 0xFF) << 8;
        int lo = b[1] & 0xFF;
        return (char) (hi | lo);
    }

    @Override
    public void write(char[] cbuf, int off, int len) throws IOException {
        try {
            synchronized (lock) {
                ensureOpen();
                outputStream.write(charsToBytes(cbuf), off, len);
            }
        } catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        } catch (IOException x) {
            throw x;
        }
    }

    @Override
    public void flush() {
        try {
            synchronized (lock) {
                ensureOpen();
                outputStream.flush();
            }
        } catch (IOException x) {
            x.printStackTrace();
        }
    }

    @Override
    public void close() {
        try {
            synchronized (lock) {
                if (outputStream == null)
                    return;
                outputStream.close();
                outputStream = null;
            }
        } catch (IOException x) {
            x.printStackTrace();
        }
    }

    public List<String> toList() {
        List<String> lines = new ArrayList<>();
        synchronized (lock) {
            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            InputStreamReader reader = new InputStreamReader(inputStream);
            BufferedReader buffer = new BufferedReader(reader);
            String line = null;
            try {
                while ((line = buffer.readLine()) != null) {
                    lines.add(line);
                }
                buffer.close();
                reader.close();
                inputStream.close();
            } catch (InterruptedIOException x) {
                Thread.currentThread().interrupt();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return lines;
    }

    public List<String> toListOnce() {
        List<String> lines = toList();
        close();
        return lines;
    }

    public String[] toArray() {
        List<String> lines = toList();
        return lines.toArray(new String[lines.size()]);
    }

    @Override
    public String toString() {
        List<String> lines = toList();
        return String.join("\n", lines);
    }

    public String toStringOnce() {
        String info = toString();
        close();
        return info;
    }
}
