// Path: cocaine-core/src/main/java/cocaine/message/Message.java
// public abstract class Message {
// 
//     private static final long SYSTEM_SESSION = 0L;
// 
//     private final MessageType type;
//     private final long session;
// 
//     protected Message(MessageType type, long session) {
//         this.type = type;
//         this.session = session;
//     }
// 
//     protected Message(MessageType type) {
//         this(type, SYSTEM_SESSION);
//     }
// 
//     public MessageType getType() {
//         return type;
//     }
// 
//     public long getSession() {
//         return session;
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) {
//             return true;
//         }
//         if (o == null || getClass() != o.getClass()) {
//             return false;
//         }
// 
//         Message message = (Message) o;
//         return session == message.session && type == message.type;
//     }
// 
//     @Override
//     public int hashCode() {
//         int result = type.hashCode();
//         result = 31 * result + (int) (session ^ (session >>> 32));
//         return result;
//     }
// 
// }
// 
// Path: cocaine-core/src/main/java/cocaine/msgpack/MessageTemplate.java
// public final class MessageTemplate extends AbstractTemplate<Message> {
// 
//     private static final Template<Message> instance = new MessageTemplate();
// 
//     private MessageTemplate() { }
// 
//     public static Template<Message> getInstance() {
//         return instance;
//     }
// 
//     @Override
//     public void write(Packer packer, Message message, boolean required) throws IOException {
//         packer.writeArrayBegin(3);
//         packer.write(message.getType().value());
//         packer.write(message.getSession());
// 
//         switch (message.getType()) {
//             case HANDSHAKE: {
//                 HandshakeMessage handshakeMessage = (HandshakeMessage) message;
//                 packer.writeArrayBegin(1);
//                 UUIDTemplate.getInstance().write(packer, handshakeMessage.getId());
//                 packer.writeArrayEnd();
//                 break;
//             }
//             case TERMINATE: {
//                 TerminateMessage terminateMessage = (TerminateMessage) message;
//                 packer.writeArrayBegin(2);
//                 packer.write(terminateMessage.getReason().value());
//                 packer.write(terminateMessage.getMessage());
//                 packer.writeArrayEnd();
//                 break;
//             }
//             case INVOKE: {
//                 InvokeMessage invokeMessage = (InvokeMessage) message;
//                 packer.writeArrayBegin(1);
//                 packer.write(invokeMessage.getEvent());
//                 packer.writeArrayEnd();
//                 break;
//             }
//             case CHUNK: {
//                 ChunkMessage chunkMessage = (ChunkMessage) message;
//                 packer.writeArrayBegin(1);
//                 packer.write(chunkMessage.getData());
//                 packer.writeArrayEnd();
//                 break;
//             }
//             case ERROR: {
//                 ErrorMessage errorMessage = (ErrorMessage) message;
//                 packer.writeArrayBegin(2);
//                 packer.write(errorMessage.getCode());
//                 packer.write(errorMessage.getMessage());
//                 packer.writeArrayEnd();
//                 break;
//             }
//             case HEARTBEAT:
//             case CHOKE: {
//                 packer.writeArrayBegin(0);
//                 packer.writeArrayEnd();
//                 break;
//             }
//         }
// 
//         packer.writeArrayEnd();
//     }
// 
//     @Override
//     public Message read(Unpacker unpacker, Message message, boolean required) throws IOException {
//         Message result;
// 
//         unpacker.readArrayBegin();
//         MessageType type = MessageType.fromValue(unpacker.readInt());
//         long session = unpacker.readLong();
// 
//         unpacker.readArrayBegin();
//         switch (type) {
//             case HANDSHAKE: {
//                 UUID id = unpacker.read(UUIDTemplate.getInstance());
//                 result = Messages.handshake(id);
//                 break;
//             }
//             case HEARTBEAT: {
//                 result = Messages.heartbeat();
//                 break;
//             }
//             case TERMINATE: {
//                 TerminateMessage.Reason reason = TerminateMessage.Reason.fromValue(unpacker.readInt());
//                 String msg = unpacker.readString();
//                 result = Messages.terminate(reason, msg);
//                 break;
//             }
//             case INVOKE: {
//                 String event = unpacker.readString();
//                 result = Messages.invoke(session, event);
//                 break;
//             }
//             case CHUNK: {
//                 byte[] data = unpacker.readByteArray();
//                 result = Messages.chunk(session, data);
//                 break;
//             }
//             case ERROR: {
//                 int code = unpacker.readInt();
//                 String msg = unpacker.readString();
//                 result = Messages.error(session, code, msg);
//                 break;
//             }
//             case CHOKE: {
//                 result = Messages.choke(session);
//                 break;
//             }
//             default: {
//                 throw new IllegalArgumentException("Unknown message type: " + type.name());
//             }
//         }
//         unpacker.readArrayEnd();
//         unpacker.readArrayEnd();
// 
//         return result;
//     }
// 
// }


import java.io.EOFException;
import java.nio.ByteBuffer;
import java.util.List;
import cocaine.message.Message;
import cocaine.msgpack.MessageTemplate;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.apache.log4j.Logger;
import org.msgpack.MessagePack;
package cocaine.netty;



/**
 * @author Anton Bobukh <abobukh@yandex-team.ru>
 */
public class MessageDecoder extends ByteToMessageDecoder {

    private static final Logger logger = Logger.getLogger(MessageDecoder.class);

    private final MessagePack pack;

    public MessageDecoder(MessagePack pack) {
        this.pack = pack;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        logger.debug("Decoding message");
        in.markReaderIndex();

        ByteBuffer buffer = in.nioBuffer();
        try {
// Path: cocaine-core/src/main/java/cocaine/message/Message.java
// public abstract class Message {
// 
//     private static final long SYSTEM_SESSION = 0L;
// 
//     private final MessageType type;
//     private final long session;
// 
//     protected Message(MessageType type, long session) {
//         this.type = type;
//         this.session = session;
//     }
// 
//     protected Message(MessageType type) {
//         this(type, SYSTEM_SESSION);
//     }
// 
//     public MessageType getType() {
//         return type;
//     }
// 
//     public long getSession() {
//         return session;
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) {
//             return true;
//         }
//         if (o == null || getClass() != o.getClass()) {
//             return false;
//         }
// 
//         Message message = (Message) o;
//         return session == message.session && type == message.type;
//     }
// 
//     @Override
//     public int hashCode() {
//         int result = type.hashCode();
//         result = 31 * result + (int) (session ^ (session >>> 32));
//         return result;
//     }
// 
// }
// 
// Path: cocaine-core/src/main/java/cocaine/msgpack/MessageTemplate.java
// public final class MessageTemplate extends AbstractTemplate<Message> {
// 
//     private static final Template<Message> instance = new MessageTemplate();
// 
//     private MessageTemplate() { }
// 
//     public static Template<Message> getInstance() {
//         return instance;
//     }
// 
//     @Override
//     public void write(Packer packer, Message message, boolean required) throws IOException {
//         packer.writeArrayBegin(3);
//         packer.write(message.getType().value());
//         packer.write(message.getSession());
// 
//         switch (message.getType()) {
//             case HANDSHAKE: {
//                 HandshakeMessage handshakeMessage = (HandshakeMessage) message;
//                 packer.writeArrayBegin(1);
//                 UUIDTemplate.getInstance().write(packer, handshakeMessage.getId());
//                 packer.writeArrayEnd();
//                 break;
//             }
//             case TERMINATE: {
//                 TerminateMessage terminateMessage = (TerminateMessage) message;
//                 packer.writeArrayBegin(2);
//                 packer.write(terminateMessage.getReason().value());
//                 packer.write(terminateMessage.getMessage());
//                 packer.writeArrayEnd();
//                 break;
//             }
//             case INVOKE: {
//                 InvokeMessage invokeMessage = (InvokeMessage) message;
//                 packer.writeArrayBegin(1);
//                 packer.write(invokeMessage.getEvent());
//                 packer.writeArrayEnd();
//                 break;
//             }
//             case CHUNK: {
//                 ChunkMessage chunkMessage = (ChunkMessage) message;
//                 packer.writeArrayBegin(1);
//                 packer.write(chunkMessage.getData());
//                 packer.writeArrayEnd();
//                 break;
//             }
//             case ERROR: {
//                 ErrorMessage errorMessage = (ErrorMessage) message;
//                 packer.writeArrayBegin(2);
//                 packer.write(errorMessage.getCode());
//                 packer.write(errorMessage.getMessage());
//                 packer.writeArrayEnd();
//                 break;
//             }
//             case HEARTBEAT:
//             case CHOKE: {
//                 packer.writeArrayBegin(0);
//                 packer.writeArrayEnd();
//                 break;
//             }
//         }
// 
//         packer.writeArrayEnd();
//     }
// 
//     @Override
//     public Message read(Unpacker unpacker, Message message, boolean required) throws IOException {
//         Message result;
// 
//         unpacker.readArrayBegin();
//         MessageType type = MessageType.fromValue(unpacker.readInt());
//         long session = unpacker.readLong();
// 
//         unpacker.readArrayBegin();
//         switch (type) {
//             case HANDSHAKE: {
//                 UUID id = unpacker.read(UUIDTemplate.getInstance());
//                 result = Messages.handshake(id);
//                 break;
//             }
//             case HEARTBEAT: {
//                 result = Messages.heartbeat();
//                 break;
//             }
//             case TERMINATE: {
//                 TerminateMessage.Reason reason = TerminateMessage.Reason.fromValue(unpacker.readInt());
//                 String msg = unpacker.readString();
//                 result = Messages.terminate(reason, msg);
//                 break;
//             }
//             case INVOKE: {
//                 String event = unpacker.readString();
//                 result = Messages.invoke(session, event);
//                 break;
//             }
//             case CHUNK: {
//                 byte[] data = unpacker.readByteArray();
//                 result = Messages.chunk(session, data);
//                 break;
//             }
//             case ERROR: {
//                 int code = unpacker.readInt();
//                 String msg = unpacker.readString();
//                 result = Messages.error(session, code, msg);
//                 break;
//             }
//             case CHOKE: {
//                 result = Messages.choke(session);
//                 break;
//             }
//             default: {
//                 throw new IllegalArgumentException("Unknown message type: " + type.name());
//             }
//         }
//         unpacker.readArrayEnd();
//         unpacker.readArrayEnd();
// 
//         return result;
//     }
// 
// }


// Path: cocaine-client/src/main/java/cocaine/netty/MessageDecoder.java
import java.io.EOFException;
import java.nio.ByteBuffer;
import java.util.List;
import cocaine.message.Message;
import cocaine.msgpack.MessageTemplate;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.apache.log4j.Logger;
import org.msgpack.MessagePack;

package cocaine.netty;



/**
 * @author Anton Bobukh <abobukh@yandex-team.ru>
 */
public class MessageDecoder extends ByteToMessageDecoder {

    private static final Logger logger = Logger.getLogger(MessageDecoder.class);

    private final MessagePack pack;

    public MessageDecoder(MessagePack pack) {
        this.pack = pack;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        logger.debug("Decoding message");
        in.markReaderIndex();

        ByteBuffer buffer = in.nioBuffer();
        try {
            Message message = pack.read(buffer, MessageTemplate.getInstance());