package ru.yandex.webmaster3.core.util.trie;

/**
 * @author avhaliullin
 */
public class TrieBufferWriter {
    private byte[] buffer = new byte[1024];

    public void assureSize(int bufferLength) {
        if (bufferLength > buffer.length) {
            byte[] newBuff = new byte[Math.max((int) (buffer.length * 1.5), bufferLength)];
            System.arraycopy(buffer, 0, newBuff, 0, buffer.length);
            buffer = newBuff;
        }
    }

    public int writeInt(int offset, int value) {
        assureSize(offset + 4);
        buffer[offset] = (byte) (value >> 24);
        buffer[offset + 1] = (byte) (value >> 16);
        buffer[offset + 2] = (byte) (value >> 8);
        buffer[offset + 3] = (byte) (value);
        return offset + 4;
    }

    public int writeUInt16(int offset, int value) {
        int maxValue = Short.MAX_VALUE * 2 + 1;
        if (maxValue < value) {
            throw new RuntimeException("Value " + value + " cannot be placed into uint16");
        }
        assureSize(offset + 2);
        buffer[offset] = (byte) (value >> 8);
        buffer[offset + 1] = (byte) (value);
        return offset + 2;
    }

    public int writeLong(int offset, long value) {
        assureSize(offset + 8);
        buffer[offset] = (byte) (value >> 56);
        buffer[offset + 1] = (byte) (value >> 48);
        buffer[offset + 2] = (byte) (value >> 40);
        buffer[offset + 3] = (byte) (value >> 32);
        buffer[offset + 4] = (byte) (value >> 24);
        buffer[offset + 5] = (byte) (value >> 16);
        buffer[offset + 6] = (byte) (value >> 8);
        buffer[offset + 7] = (byte) (value);
        return offset + 8;
    }

    public int writeBytes(int offset, byte[] bytes, int srcOffset, int len) {
        assureSize(offset + len);
        System.arraycopy(bytes, srcOffset, buffer, offset, len);
        return offset + len;
    }

    public int writeByte(int offset, byte value) {
        assureSize(offset + 1);
        buffer[offset] = value;
        return offset + 1;
    }

    public int writeVarUInt32(int offset, int value) {
        while (true) {
            if ((value & ~0x7F) == 0) {
                return writeByte(offset, (byte) value);
            } else {
                offset = writeByte(offset, (byte) ((value & 0x7F) | 0x80));
                value >>>= 7;
            }
        }
    }

    public byte[] shrink(int size) {
        if ((1d * buffer.length) / size < 1.1d) { // нефиг все перекопировать ради 10%
            return buffer;
        }
        byte[] result = new byte[size];
        System.arraycopy(buffer, 0, result, 0, size);
        return result;
    }

    public byte[] getBuffer() {
        return buffer;
    }

    public void setBuffer(byte[] buffer) {
        this.buffer = buffer;
    }
}
