package org.artisanlogiciel.games.minetest.core;

import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.zip.Inflater;

public class Serialize {

    public final static int BUFFER_SIZE  = 16386;

    public static int readS16(byte buffer[], int offset, int length)
            throws PacketException
    {
        if ( offset + 2 < length) {
            return 256 * buffer[offset] + buffer[offset + 1];
        }
        else
        {
            throw new PacketException("out of bound offset U16");
        }
    }

    // MSB network order
    public static int readU32(byte[] buffer, int offset, int length)
            throws PacketException
    {
        if ( offset + 4 < length) {
            return 16777216 * Byte.toUnsignedInt(buffer[offset]) +
                    65536 * Byte.toUnsignedInt(buffer[offset + 1]) +
                    256 * Byte.toUnsignedInt(buffer[offset + 2]) +
                    Byte.toUnsignedInt(buffer[offset + 3]);
        }
        else
        {
            throw new PacketException("out of bound offset U32");
        }
    }

    // MSB network order
    public static int readU16(byte[] buffer, int offset, int length)
            throws PacketException
    {
        if ( offset + 2 < length) {
            return 256 * Byte.toUnsignedInt(buffer[offset]) +
                    Byte.toUnsignedInt(buffer[offset + 1]);
        }
        else
        {
            throw new PacketException("out of bound offset U16");
        }
    }

    // will update position
    public static int readU16(ByteBuffer byteBuffer)
            throws PacketException
    {
        int u16 = readU16(byteBuffer.array(), byteBuffer.position(), byteBuffer.array().length);
        byteBuffer.position(byteBuffer.position()+2);
        return u16;
    }

    public static int readU8(byte[] buffer, int offset, int length)
            throws PacketException
    {
        if ( offset + 1 < length) {
            return Byte.toUnsignedInt(buffer[offset]);
        }
        else
        {
            throw new PacketException("out of bound offset U8");
        }
    }

    // will update position
    public static int readU8(ByteBuffer byteBuffer)
            throws PacketException
    {
        int u8 = readU8(byteBuffer.array(), byteBuffer.position(), byteBuffer.array().length);
        byteBuffer.position(byteBuffer.position()+1);
        return u8;
    }

    public static v3s16 readV3S16(byte buffer[], int offset, int length)
            throws PacketException
    {
        if ( offset + 6 <= length) {
            int x = readS16(buffer, offset, length);
            int y = readS16(buffer, offset + 2, length);
            int z = readS16(buffer, offset + 4, length);
            return new v3s16(x, y, z);
        }
        else
        {
            throw new PacketException("out of bound offset v3s16");
        }
    }

    // Zlib decompression
    public static byte[] decompress(ByteBuffer byteBuffer) throws Exception
    {
        final Inflater inflater = new Inflater(false);
        inflater.setInput(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit() - byteBuffer.position());

        try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(byteBuffer.limit()))
        {
            byte[] buffer = new byte[BUFFER_SIZE];
            while (!inflater.finished())
            {
                final int count = inflater.inflate(buffer);
                outputStream.write(buffer, 0, count);
            }

            return outputStream.toByteArray();
        }
    }
}
