/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

#ifndef _VECTOR_H_
#define _VECTOR_H_

/*! \brief Contains public interface to various functions related
 *         to a wrapped fix-size array
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/

#include <stdint.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

/*******************************************************************************
 * the defines
 ******************************************************************************/

/*******************************************************************************
 * the typedefs
 ******************************************************************************/

/*! \brief  Declaration of Vector prototype
 */
typedef struct _VectorType_
{
    void    *pContainer;    /*!< The pointer to the container, which must be an 1-D array */
    uint32_t containerSize; /*!< The size of the container */
    uint32_t length;        /*!< The current occupied length */

} VectorType;

/*******************************************************************************
 * the globals
 ******************************************************************************/

/*******************************************************************************
 * the function prototypes
 ******************************************************************************/

/*! \brief Initializes the Vector instance.
 *
 * This function initializes the Vector instance
 *
 * \param[in] obj : pointer to Vector instance
 * \param[in] pContainer : the container (1-D array) for this table
 * \param[in] size : the size of the container array
 */
static inline void Vector_Init(VectorType *obj, void *pContainer, uint32_t size)
{
    obj->pContainer    = pContainer;
    obj->containerSize = size;
    obj->length        = 0;
}

/*! \brief Gets the current length of the vector
 *
 * This function gets the current length of the vector
 *
 * \param[in] obj : pointer to Vector instance
 * \return the current length of the vector
 */
static inline uint32_t Vector_Length(const VectorType *obj)
{
    return obj->length;
}

/*! \brief Inserts an element at the given index
 *
 * This function inserts an element at the given index
 *
 * \note If the given index is larger than the last element index+1,
 *       or the vector is full, the function will do nothing
 *
 * \param[in] obj : pointer to Vector instance
 * \param[in] pElem : pointer to the element
 * \param[in] elemSize : the size of element in byte
 * \param[in] index : index where the element is inserted
 * \return the result of the insert operation
 *         - true : success
 *         - false : failed
 */
static inline bool Vector_Insert(VectorType *obj, const void *pElem, uint32_t elemSize, uint32_t index)
{
    /* If full, we cannot insert more elements */
    if(obj->length == obj->containerSize)
    {
        return false;
    }

    /* If no enough elements */
    if(index > obj->length)
    {
        return false;
    }

    unsigned char *src;
    unsigned char *dest;

    /* Move items by one position in memory */
    if(obj->length > 0)
    {
        uint32_t i = 0;
        for(i = obj->length; i > index; --i)
        {
            src  = (unsigned char *)(obj->pContainer) + elemSize * (i - 1);
            dest = (unsigned char *)(obj->pContainer) + elemSize * i;
            uint32_t byte;
            for(byte = 0; byte < elemSize; byte++)
            {
                *dest++ = *src++;
            }
        }
    }

    /* Assign the value */
    src  = (unsigned char *)pElem;
    dest = (unsigned char *)(obj->pContainer) + elemSize * index;
    uint32_t byte;
    for(byte = 0; byte < elemSize; byte++)
    {
        *dest++ = *src++;
    }

    /* Increase the length */
    obj->length++;

    return true;
}

/*! \brief Gets the element at the given index
 *
 * This function gets the element at the given index
 *
 * \note index must be in the range of 0-size
 *
 * \param[in] obj : pointer to Vector instance
 * \param[in] pElem : pointer to the element
 * \param[in] elemSize : the size of element in byte
 * \param[in] index : index of the element to get
 * \return the result of the get operation
 *         - true : success
 *         - false : failed
 */
static inline bool Vector_GetElement(const VectorType *obj, void *pElem, uint32_t elemSize, uint32_t index)
{
    if(index >= obj->length)
    {
        return false;
    }

    int            i = 0;
    unsigned char *src;
    unsigned char *dest;

    dest = (unsigned char *)pElem;
    src  = (unsigned char *)(obj->pContainer) + elemSize * index;

    for(i = 0; i < elemSize; i++)
    {
        *dest++ = *src++;
    }

    return true;
}

/*! \brief Get the pointer to the element at the given index
 *
 * This function gets the pointer to the element at the given index
 *
 * \note index must be in the range of 0-size
 *
 * \param[in] obj : pointer to Vector instance
 * \param[in] elemSize : the size of element in byte
 * \param[in] index : index of the element to get
 * \return pointer to the element
 */
static inline const void *Vector_At(const VectorType *obj, uint32_t elemSize, uint32_t index)
{
    if(index >= obj->length)
    {
        return ((void *)0);
    }

    return (void *)((unsigned char *)(obj->pContainer) + elemSize * index);
}

/*! \brief Clears the whole vector
 *
 * This function clears the whole vector
 *
 * \param[in] obj : pointer to Vector instance
 */
static inline void Vector_Clear(VectorType *obj)
{
    obj->length = 0;
}

/*! \brief Remove the element at the given index
 *
 * This function removes the element at the at the given index
 *
 * \param[in] obj : pointer to Vector instance
 * \param[in] elemSize : the size of element in byte
 * \param[in] index : index of the element to remove
 * \return the result of the remove operation
 *         - true : success
 *         - false : failed
 */
static inline bool Vector_Remove(VectorType *obj, uint32_t elemSize, uint32_t index)
{
    /* If empty, we cannot remove any elements */
    if(obj->length == 0)
    {
        return false;
    }

    /* If no enough elements */
    if(index >= obj->length)
    {
        return false;
    }

    unsigned char *src;
    unsigned char *dest;

    /* Move items by one position in memory */
    if(obj->length - 1 > index)
    {
        uint32_t i = 0;
        for(i = index; i < obj->length; ++i)
        {
            src  = (unsigned char *)(obj->pContainer) + elemSize * (i + 1);
            dest = (unsigned char *)(obj->pContainer) + elemSize * i;
            uint32_t byte;
            for(byte = 0; byte < elemSize; byte++)
            {
                *dest++ = *src++;
            }
        }
    }

    /* Decrease the length */
    obj->length--;

    return true;
}

/*! \brief Whether the vector is full
 *
 * This function gets whether the vector is full
 *
 * \param[in] obj : pointer to Vector instance
 * \return whether the vector is full
 *         - true : full
 *         - false : not full
 */
static inline bool Vector_IsFull(const VectorType *obj)
{
    return obj->length == obj->containerSize;
}

/*! \brief Whether the vector is empty
 *
 * This function gets whether the vector is empty
 *
 * \param[in] obj : pointer to Vector instance
 * \return whether the vector is empty
 *         - true : empty
 *         - false : not empty
 */
static inline bool Vector_IsEmpty(const VectorType *obj)
{
    return obj->length == 0;
}

#ifdef __cplusplus
}
#endif /* extern "C" */

#endif /* _VECTOR_H_ */
