/**
 * Copyright 2016-2018 Dell Inc. or its subsidiaries. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
package com.emc.ecs.nfsclient.network;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;

/**
 * To receive the entire response. We do not actually decode the rpc packet here.
 * Just get the size from the packet and then put them in internal buffer until all data arrive.
 * 
 * @author seibed
 */
public class RPCRecordDecoder extends FrameDecoder {

    /**
     * Holds the calculated record length for each channel until the Channel is ready for buffering.
     * Reset to 0 after that for the next channel.
     */
    private int _recordLength = 0;

    /* (non-Javadoc)
     * @see org.jboss.netty.handler.codec.frame.FrameDecoder#decode(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.Channel, org.jboss.netty.buffer.ChannelBuffer)
     */
    protected Object decode(ChannelHandlerContext channelHandlerContext, Channel channel, ChannelBuffer channelBuffer) throws Exception {
        // Wait until the length prefix is available.
        if (channelBuffer.readableBytes() < 4) {
            // If null is returned, it means there is not enough data yet.
            // FrameDecoder will call again when there is a sufficient amount of data available.
            return null;
        }

        //marking the current reading position
        channelBuffer.markReaderIndex();

        //get the fragment size and wait until the entire fragment is available.
        long fragSize = channelBuffer.readUnsignedInt();
        boolean lastFragment = RecordMarkingUtil.isLastFragment(fragSize);
        fragSize = RecordMarkingUtil.maskFragmentSize(fragSize);
        if (channelBuffer.readableBytes() < fragSize) {
            channelBuffer.resetReaderIndex();
            return null;
        }

        //seek to the beginning of the next fragment
        channelBuffer.skipBytes((int) fragSize);

        _recordLength += 4 + (int) fragSize;

        //check the last fragment
        if (!lastFragment) {
            //not the last fragment, the data is put in an internally maintained cumulative buffer
            return null;
        }

        byte[] rpcResponse = new byte[_recordLength];
        channelBuffer.readerIndex(channelBuffer.readerIndex() - _recordLength);
        channelBuffer.readBytes(rpcResponse, 0, _recordLength);

        _recordLength = 0;
        return rpcResponse;
    }
}