package com.lts.remoting.codec;

import com.lts.remoting.RemotingCommandBody;
import com.lts.remoting.protocol.RemotingCommand;
import com.lts.remoting.serialize.RemotingSerializable;

import java.nio.ByteBuffer;

/**
 * @author Robert HG (254963746@qq.com) on 11/5/15.
 *
 * 协议格式
 *    | <length> | <serializable id> | <header length> | <header data> | <body length> | <body data> | <body class> |
 *    |    1     |        2          |        3        |       4       |       5       |      6      |      7       |
 *
 *    1、大端4个字节整数，等于2、3、4、5、6, 7长度总和
 *    1、大端4个字节整数，等 serializable id
 *    3、header 信息长度 大端4个字节整数，等于3的长度
 *    4、header 信息内容
 *    5、body 信息长度  大端4个字节整数，等于5的长度
 *    6、body 信息内容
 *    7、body 的class名称
 */
public class DefaultCodec extends AbstractCodec {

    @Override
    public RemotingCommand decode(ByteBuffer byteBuffer) throws Exception {

        int length = byteBuffer.limit();
        int serializableId = byteBuffer.getInt();

        RemotingSerializable serializable =
                getRemotingSerializable(serializableId);

        int headerLength = byteBuffer.getInt();
        byte[] headerData = new byte[headerLength];
        byteBuffer.get(headerData);

        RemotingCommand cmd = serializable.deserialize(headerData, RemotingCommand.class);

        int remaining = length - 4 - 4 - headerLength;

        if (remaining > 0) {

            int bodyLength = byteBuffer.getInt();
            int bodyClassLength = remaining - 4 - bodyLength;

            if (bodyLength > 0) {

                byte[] bodyData = new byte[bodyLength];
                byteBuffer.get(bodyData);

                byte[] bodyClassData = new byte[bodyClassLength];
                byteBuffer.get(bodyClassData);

                cmd.setBody((RemotingCommandBody) serializable.deserialize(bodyData, Class.forName(new String(bodyClassData))));
            }
        }
        return cmd;
    }

    @Override
    public ByteBuffer encode(RemotingCommand remotingCommand) throws Exception {

        RemotingSerializable serializable =
                getRemotingSerializable(remotingCommand.getSid());

        // header length size
        int length = 4;

        // serializable id (int)
        length += 4;

        //  header data length
        byte[] headerData = serializable.serialize(remotingCommand);
        length += headerData.length;

        byte[] bodyData = null;
        byte[] bodyClass = null;

        RemotingCommandBody body = remotingCommand.getBody();

        if (body != null) {
            // body data
            bodyData = serializable.serialize(body);
            length += bodyData.length;

            bodyClass = body.getClass().getName().getBytes();
            length += bodyClass.length;

            length += 4;
        }

        ByteBuffer result = ByteBuffer.allocate(4 + length);

        // length
        result.putInt(length);

        // serializable Id
        result.putInt(serializable.getId());

        // header length
        result.putInt(headerData.length);

        // header data
        result.put(headerData);

        if (bodyData != null) {
            //  body length
            result.putInt(bodyData.length);
            //  body data
            result.put(bodyData);
            // body class
            result.put(bodyClass);
        }

        result.flip();

        return result;
    }
}
