/*
 * Copyright 2008-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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 net.hasor.rsf.protocol.rsf.v1;
import io.netty.buffer.ByteBuf;
import net.hasor.rsf.utils.IOUtils;
import net.hasor.rsf.utils.ProtocolUtils;

/**
 * 池上限为 0~4095条数据，单条数据最大约16MB。
 * 下面是数据格式：<pre>
 * ---------------------------------bytes =6 ~ 8192
 * byte[2]  attrPool-size (0~4095)  池大小 0x0FFF
 *     byte[4] att-length           属性1大小
 *     byte[4] att-length           属性2大小
 *     ...
 * ---------------------------------bytes =n
 * dataBody                         数据内容
 *     bytes[...]
 * </pre>
 * @version : 2014年10月25日
 * @author 赵永春 (zyc@hasor.net)
 */
public class PoolBlock {
    public static final int     NULL_MARK   = 0xFFFFFFFF; //表示NULL
    public static       int     DataMaxSize = 0x00FFFFFF; //单条数据最大约16MB
    public static       short   PoolMaxSize = 0x0FFF;     //池上限为 0~4095条
    private             int[]   poolMap     = {};
    private             ByteBuf poolData    = null;

    public PoolBlock() {
        this.poolData = ProtocolUtils.newByteBuf();
    }

    public void fillFrom(ByteBuf formData) {
        if (formData == null) {
            return;
        }
        //
        short attrPoolSize = (short) (PoolMaxSize & formData.readShort());
        for (int i = 0; i < attrPoolSize; i++) {
            int length = formData.readInt();
            this.poolMap = ArrayUtils.add(this.poolMap, length);
        }
        this.poolData.writeBytes(formData);
    }

    public void fillTo(ByteBuf toData) {
        if (toData == null)
            return;
        //
        toData.writeShort(poolMap.length);
        for (int i = 0; i < poolMap.length; i++) {
            toData.writeInt(poolMap[i]);
        }
        toData.writeBytes(this.poolData);
    }

    /**添加请求参数。*/
    public short pushData(byte[] dataArray) {
        if (this.poolMap.length >= PoolMaxSize) {
            throw new IndexOutOfBoundsException("poolMax size is " + PoolMaxSize);
        }
        //
        int datalength = (dataArray == null) ? NULL_MARK : dataArray.length;
        this.poolMap = ArrayUtils.add(this.poolMap, datalength);
        if (datalength > 0) {
            this.poolData.writeBytes(dataArray);
        }
        return (short) (this.poolMap.length - 1);
    }

    /**池长度*/
    public int getPoolLength() {
        return this.poolMap.length;
    }

    /**池大小*/
    public int getPoolSize() {
        int rawSize = 0;
        for (int i = 0; i < this.poolMap.length; i++) {
            int atPoolData = this.poolMap[i];
            if (atPoolData <= 0)
                continue;
            rawSize += this.poolMap[i];
        }
        return rawSize;
    }

    /**池数据*/
    public int[] getPoolData() {
        return this.poolMap;
    }

    /**内容所处起始位置*/
    public byte[] readPool(short attrIndex) {
        if (this.poolMap[attrIndex] == NULL_MARK) {
            return null;
        }
        //
        int rawIndex = 0;
        for (int i = 0; i < this.poolMap.length; i++) {
            if (i == attrIndex)
                break;
            if (this.poolMap[i] != NULL_MARK)
                rawIndex += this.poolMap[i];
        }
        int readLength = this.poolMap[attrIndex];//内容长度
        if (readLength == NULL_MARK)
            return null;
        //
        byte[] data = new byte[readLength];
        this.poolData.getBytes(rawIndex, data, 0, readLength);
        return data;
    }

    public void release() {
        IOUtils.releaseByteBuf(this.poolData);
        this.poolMap = null;
    }

    @Override
    protected void finalize() throws Throwable {
        this.release();
        super.finalize();
    }
}