/*
* Copyright (c) 2017, Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//!
//! \file      cm_array.cpp 
//! \brief     Contains  Class CmDynamicArray definitions 
//!

#include "cm_array.h"
#include "cm_mem.h"
namespace CMRT_UMD
{
/*****************************************************************************\

Function:
    CmDynamicArray Constructor

Description:
    Initializes the array

Input:
    const uint32_t initSize - initial size of the array, in elements

Output:
    none

\*****************************************************************************/
CmDynamicArray::CmDynamicArray( const uint32_t initSize )
{
    m_arrayBuffer = nullptr;

    m_usedSize = 0;
    m_actualSize = 0;

    CreateArray( initSize );

}
/*****************************************************************************\

Function:
    CmDynamicArray Constructor

Description:
    Initializes the array

Input:
    none

Output:
    none

\*****************************************************************************/
CmDynamicArray::CmDynamicArray()
{
    m_arrayBuffer = nullptr;
    m_usedSize = 0;
    m_actualSize = 0;
}
/*****************************************************************************\

Function:
    CmDynamicArray Destructor

Description:
    Frees all internal dynamic memory

Input:
    none

Output:
    none

\*****************************************************************************/

CmDynamicArray::~CmDynamicArray( void )
{
    Delete();
}

/*****************************************************************************\

Function:
    CmDynamicArray::GetElement

Description:
    Returns the element at the index in the array

Input:
    const uint32_t index - index of element to reference

Output:
    void* - value of element in array

\*****************************************************************************/

void* CmDynamicArray::GetElement( const uint32_t index )
{
    void* element;

    if( m_arrayBuffer && IsValidIndex( index ) )
    {
        element = m_arrayBuffer[ index ];
    }
    else
    {
        CM_NORMALMESSAGE("Warning: Failed to get the element at the index in the array.");
        CmSafeMemSet( &element, 0, sizeof(void*) );
    }
    return element;
}

/*****************************************************************************\

Function:
    CmDynamicArray::SetElement

Description:
    Sets the element at the index in the array to the given element

Input:
    const uint32_t index - index of element to reference
    const void* element - value of element to set

Output:
    bool - SUCCESS or FAIL

\*****************************************************************************/

bool CmDynamicArray::SetElement( const uint32_t index, const void* element )
{
    bool success = false;

    // If the index is larger than the size of the array then grow the array
    if( !IsValidIndex( index ) )
    {
        CreateArray( index + 1 );
    }

    if( m_arrayBuffer && IsValidIndex( index ) )
    {
        m_arrayBuffer[ index ] = (void*)element;
        success = true;
    }

    CM_ASSERT( success );
    return success;
}

/*****************************************************************************\

Function:
    CmDynamicArray::GetSize

Description:
    Returns the current number of elements in the array

Input:
    void

Output:
    uint32_t - size of the array in elements

\*****************************************************************************/

uint32_t CmDynamicArray::GetSize( void )
{
    const uint32_t size = m_usedSize;
    return size;
}

/*****************************************************************************\

Function:
    CmDynamicArray::Delete

Description:
    Deletes the internal data

Input:
    void

Output:
    void

\*****************************************************************************/

void CmDynamicArray::Delete( void )
{
    DeleteArray();
    m_usedSize = 0;
}

/*****************************************************************************\

Function:
    CmDynamicArray::operator=

Description:
    Equal operator to copy an array

Input:
    const CmDynamicArray& array - array to copy

Output:
    *this

\*****************************************************************************/

CmDynamicArray& CmDynamicArray::operator= ( const CmDynamicArray &array )
{

    if( array.m_arrayBuffer )
    {
        if( m_usedSize < array.m_usedSize )
        {
            CreateArray( array.m_usedSize );
        }

        if( m_arrayBuffer && ( m_usedSize >= array.m_usedSize ) )
        {
            for( uint32_t i = 0; i < array.m_usedSize; i++ )
            {
                m_arrayBuffer[i] = array.m_arrayBuffer[i];
            }
        }
    }

    return *this;
}

/*****************************************************************************\

Function:
    CmDynamicArray::CreateArray

Description:
    Creates the internal array structure of the specified size

Input:
    const uint32_t size - number of elements

Output:
    void

\*****************************************************************************/

void CmDynamicArray::CreateArray( const uint32_t size )
{
    if( size )
    {
        if( size > GetMaxSize() )
        {
            uint32_t actualSize = GetMaxSize() * 2;

            if( size > actualSize )
            {
                // The minimum allocation size is 32 elements, and
                // the allocations size is in multiples of 32 elements
                actualSize = (uint32_t)Round( Max( size, 32 ), 32 );
            }

            CM_ASSERT( actualSize >= size );
            CM_ASSERT( actualSize > m_actualSize );

            const uint32_t allocSize = actualSize * sizeof(void*);

            void** arrayBuffer = MOS_NewArray(void*, allocSize);

            if( arrayBuffer )
            {
                CmSafeMemSet( arrayBuffer, 0, allocSize );

                if( m_arrayBuffer )
                {
                    for( uint32_t i = 0; i < m_usedSize; i++ )
                    {
                        arrayBuffer[i] = m_arrayBuffer[i];
                    }

                    DeleteArray();
                }

                m_arrayBuffer = arrayBuffer;
                m_actualSize = actualSize;
                m_usedSize = size;
            }
            else
            {
                CM_ASSERTMESSAGE("Failed to create the internal array structure of the specified size.");
                return;
            }
        }
        else
        {
            // Update the array length
            m_usedSize = size;
        }
    }
}

/*****************************************************************************\

Function:
    CmDynamicArray::DeleteArray

Description:
    Deletes the internal array structure

Input:
    void

Output:
    void

\*****************************************************************************/
void CmDynamicArray::DeleteArray( void )
{
    if( m_arrayBuffer )
    {
        MOS_DeleteArray(m_arrayBuffer);
        m_arrayBuffer = nullptr;
    }

    m_actualSize = 0;
}

/*****************************************************************************\

Function:
    CmDynamicArray::GetMaxSize

Description:
    Returns the maximum number of elements in the array

Input:
    void

Output:
    uint32_t length

\*****************************************************************************/
uint32_t CmDynamicArray::GetMaxSize( void )
{
    return m_actualSize;
}

/*****************************************************************************\

Function:
    CmDynamicArray::IsValidIndex

Description:
    Determines if the index is in the array

Input:
    const uint32_t index

Output:
    bool

\*****************************************************************************/

bool CmDynamicArray::IsValidIndex( const uint32_t index )
{
    return ( index < GetSize() );
}

/*****************************************************************************\

Function:
    CmDynamicArray::GetFirstFreeIndex()

Description:
    Returns the index of the first free slot in the array.

Input:
    void

Output:
    Returns the index of the first free slot in the array.
    If all the slots are occupied, it will return the max size of Array.
\*****************************************************************************/
uint32_t CmDynamicArray::GetFirstFreeIndex()
{
    uint32_t index = 0;
    for(  index = 0; index < GetMaxSize(); index++ )
    {
        if( m_arrayBuffer[ index ] == nullptr)
        { // Find the first free slot in array
            return index;
        }
    }
    return index;
}

/*****************************************************************************\

Function:
    CmDynamicArray::SetElementIntoFreeSlot(const void* element)

Description:
    Set the element into the first available slot in the array
    If all the slots are occupied, it will expend the array first.

Input:
    void

Output:

\*****************************************************************************/

bool  CmDynamicArray::SetElementIntoFreeSlot(const void* element)
{
    uint32_t index = GetFirstFreeIndex();

    return SetElement(index, element);
}
}