package net.md_5.bungee.protocol;

import com.google.common.base.Charsets;
import io.netty.buffer.ByteBuf;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public abstract class DefinedPacket
{

    public static void writeString(String s, ByteBuf buf)
    {
        // TODO: Check len - use Guava?
        byte[] b = s.getBytes( Charsets.UTF_8 );
        writeVarInt( b.length, buf );
        buf.writeBytes( b );
    }

    public static String readString(ByteBuf buf)
    {
        int len = readVarInt( buf );
        byte[] b = new byte[ len ];
        buf.readBytes( b );

        return new String( b, Charsets.UTF_8 );
    }

    public static void writeArray(byte[] b, ByteBuf buf)
    {
        // TODO: Check len - use Guava?
        buf.writeShort( b.length );
        buf.writeBytes( b );
    }

    public static byte[] readArray(ByteBuf buf)
    {
        // TODO: Check len - use Guava?
        short len = buf.readShort();
        byte[] ret = new byte[ len ];
        buf.readBytes( ret );
        return ret;
    }

    public static void writeStringArray(String[] s, ByteBuf buf)
    {
        writeVarInt( s.length, buf );
        for ( String str : s )
        {
            writeString( str, buf );
        }
    }

    public static String[] readStringArray(ByteBuf buf)
    {
        int len = readVarInt( buf );
        String[] ret = new String[ len ];
        for ( int i = 0; i < ret.length; i++ )
        {
            ret[i] = readString( buf );
        }
        return ret;
    }

    public static int readVarInt(ByteBuf input)
    {
        int out = 0;
        int bytes = 0;
        byte in;
        while ( true )
        {
            in = input.readByte();

            out |= ( in & 0x7F ) << ( bytes++ * 7 );

            if ( bytes > 32 )
            {
                throw new RuntimeException( "VarInt too big" );
            }

            if ( ( in & 0x80 ) != 0x80 )
            {
                break;
            }
        }

        return out;
    }

    public static void writeVarInt(int value, ByteBuf output)
    {
        int part;
        while ( true )
        {
            part = value & 0x7F;

            value >>>= 7;
            if ( value != 0 )
            {
                part |= 0x80;
            }

            output.writeByte( part );

            if ( value == 0 )
            {
                break;
            }
        }
    }

    public abstract void read(ByteBuf buf);

    public abstract void write(ByteBuf buf);

    public abstract void handle(AbstractPacketHandler handler) throws Exception;

    @Override
    public abstract boolean equals(Object obj);

    @Override
    public abstract int hashCode();

    @Override
    public abstract String toString();
}

@@ -68,16 +68,61 @@
 arsets;%0A
+import com.google.common.base.Preconditions;%0A
 import i
@@ -325,39 +325,152 @@
     
-// TODO: Check len - use Guava?
+Preconditions.checkArgument( s.length() %3C= Short.MAX_VALUE, %22Cannot send string longer than Short.MAX_VALUE (got %25s characters)%22, s.length() );%0A
 %0A   
@@ -673,32 +673,174 @@
 dVarInt( buf );%0A
+        Preconditions.checkArgument( len %3C= Short.MAX_VALUE, %22Cannot receive string longer than Short.MAX_VALUE (got %25s characters)%22, len );%0A%0A
         byte%5B%5D b
@@ -1018,39 +1018,142 @@
     
-// TODO: Check len - use Guava?
+Preconditions.checkArgument( b.length %3C= Short.MAX_VALUE, %22Cannot send array longer than Short.MAX_VALUE (got %25s bytes)%22, b.length );%0A
 %0A   
@@ -1287,77 +1287,173 @@
     
-// TODO: Check len - use Guava?%0A        short
+short len = buf.readShort();%0A        Preconditions.checkArgument(
  len 
+%3C
 = 
-buf.readShort(
+Short.MAX_VALUE, %22Cannot receive array longer than Short.MAX_VALUE (got %25s bytes)%22, len 
 );%0A
+%0A
     

Add some failsafe length preconditions