/*
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
 *
 * HDF is dual licensed: you can use it either under the terms of
 * the GPL, or the BSD license, at your option.
 * See the LICENSE file in the root of this repository for complete details.
 */
 
#include "hdf_block_buffer.h"
#include "osal_mem.h"
#include "securec.h"

static const unsigned int OFFSET = 8;

struct HdfHdfBlockBuffer *HdfHdfBlockBufferNew(const uint8_t *data, uint16_t size)
{
    uint16_t wantedSize;
    struct HdfBlockBuffer *buffer = NULL;
    if (size == 0) {
        return NULL;
    }
    wantedSize = sizeof(struct HdfBlockBuffer) + size;
    buffer = (struct HdfBlockBuffer *)OsalMemAlloc(wantedSize);
    if (buffer == NULL) {
        return NULL;
    }
    buffer->dataSize = size;
    buffer->position = 0;
    if (data != NULL) {
        if (memcpy_s(buffer->data, buffer->dataSize, data, size) != EOK) {
            OsalMemFree(buffer);
            return NULL;
        }
    }
    return buffer;
}

void HdfBlockBufferFree(const struct HdfBlockBuffer *buffer)
{
    if (buffer != NULL) {
        OsalMemFree(buffer);
    }
}

uint16_t HdfBlockBufferGetDataSize(struct HdfBlockBuffer *buffer)
{
    return (buffer == NULL) ? 0 : buffer->dataSize;
}

uint16_t HdfBlockBufferGetAvailableSize(struct HdfBlockBuffer *buffer)
{
    return (buffer == NULL) ? 0 : (buffer->dataSize - buffer->position);
}

uint8_t *HdfBlockBufferRead(struct HdfBlockBuffer *buffer, uint16_t size)
{
    uint8_t *dataPtr = NULL;
    if ((buffer == NULL) || (buffer->dataSize - buffer->position < size)) {
        return NULL;
    }
    dataPtr = &buffer->data[buffer->position];
    buffer->position += size;
    return dataPtr;
}

uint8_t *HdfBlockBufferGetData(struct HdfBlockBuffer *buffer)
{
    return (buffer == NULL) ? NULL : &buffer->data[0];
}

uint16_t HdfBlockBufferGetPoistion(struct HdfBlockBuffer *buffer)
{
    return (buffer == NULL) ? 0 : buffer->position;
}


uint16_t HdfBlockBufferSkipBytes(struct HdfBlockBuffer *buffer, uint16_t bytes)
{
    if (buffer == NULL) {
        return 0;
    }
    if (buffer->position + bytes <= buffer->dataSize) {
        buffer->position += bytes;
        return buffer->dataSize - buffer->position;
    } else {
        buffer->position = buffer->dataSize;
        return 0;
    }
}

bool HdfBlockBufferReadUint8(struct HdfBlockBuffer *buffer, uint8_t *outValue)
{
    if (buffer == NULL) {
        return false;
    }
    if (buffer->position + BYTES_UINT8 <= buffer->dataSize) {
        *outValue = buffer->data[buffer->position++];
        return true;
    }
    return false;
}

bool HdfBlockBufferReadUint8At(struct HdfBlockBuffer *buffer, uint16_t idx, uint8_t *outValue)
{
    if (buffer == NULL) {
        return false;
    }
    if (idx + BYTES_UINT8 <= buffer->dataSize) {
        *outValue = buffer->data[idx];
        return true;
    }
    return false;
}

bool HdfBlockBufferReadUint16(struct HdfBlockBuffer *buffer, uint16_t *outValue)
{
    if (buffer == NULL) {
        return false;
    }
    if (buffer->position + BYTES_UINT16 <= buffer->dataSize) {
        *outValue = read_be16(buffer->data, buffer->position);
        buffer->position += BYTES_UINT16;
        return true;
    }
    return false;
}

bool HdfBlockBufferReadUint16At(struct HdfBlockBuffer *buffer, size_t idx, uint16_t *outValue)
{
    if (buffer == NULL) {
        return false;
    }
    if (idx + BYTES_UINT16 <= buffer->dataSize) {
        *outValue = read_be16(buffer->data, idx);
        return true;
    }
    return false;
}

void HdfBlockBufferRewind(struct HdfBlockBuffer *buffer)
{
    if (buffer != NULL) {
        buffer->position = 0;
    }
}

bool HdfBlockBufferWriteUint8(struct HdfBlockBuffer *buffer, uint8_t value)
{
    if (buffer == NULL) {
        return false;
    }
    if (buffer->position + sizeof(uint8_t) <= buffer->dataSize) {
        buffer->data[buffer->position++] = value;
        return true;
    }
    return false;
}

bool HdfBlockBufferWriteUint16(struct HdfBlockBuffer *buffer, uint16_t inValue)
{
    if (buffer == NULL) {
        return false;
    }
    if (buffer->position + BYTES_UINT16 <= buffer->dataSize) {
        buffer->data[buffer->position++] = (uint8_t) (inValue >> OFFSET);
        buffer->data[buffer->position++] = (uint8_t) (inValue & 0xFF);
        return true;
    }
    return false;
}

bool HdfBlockBufferWriteData(struct HdfBlockBuffer *buffer, uint8_t *data, size_t length)
{
    uint16_t residualSize;
    if (buffer == NULL) {
        return false;
    }
    residualSize = buffer->dataSize - buffer->position;
    if (memcpy_s(buffer->data + buffer->position, residualSize, data, length) != EOK) {
        return false;
    }
    buffer->position += length;
    return true;
}

struct HdfBlockBuffer *HdfBlockBufferDuplicate(const struct HdfBlockBuffer *buffer, uint16_t start, uint16_t end)
{
    uint16_t bufferSize = HdfBlockBufferGetDataSize(buffer);
    uint16_t newBufferSize;
    struct HdfBlockBuffer *newBuffer = NULL;
    if ((buffer == NULL) || (start > end)) {
        return NULL;
    }
    if ((end > bufferSize) || (start > bufferSize)) {
        return NULL;
    }
    newBufferSize = end - start + 1;
    newBuffer = HdfBlockBufferNew(newBufferSize);
    if (newBuffer == NULL) {
        return NULL;
    }
    if (memcpy_s(newBuffer->data, newBufferSize, buffer->data + start, newBufferSize) != EOK) {
        OsalMemFree(newBuffer);
        return NULL;
    }

    return newBuffer;
}

bool HdfBlockBufferWriteBuff(struct HdfBlockBuffer *dst, struct HdfBlockBuffer *src)
{
    if (memcpy_s(&dst->data[dst->position], dst->dataSize - dst->position, src->data, src->dataSize) != EOK) {
        return false;
    }
    dst->position += src->dataSize;
    return true;
}