/*******************************************************************************
* Copyright 2016-2020 Intel Corporation.
*
* This software and the related documents are Intel copyrighted  materials,  and
* your use of  them is  governed by the  express license  under which  they were
* provided to you (License).  Unless the License provides otherwise, you may not
* use, modify, copy, publish, distribute,  disclose or transmit this software or
* the related documents without Intel's prior written permission.
*
* This software and the related documents  are provided as  is,  with no express
* or implied  warranties,  other  than those  that are  expressly stated  in the
* License.
*******************************************************************************/

#include "iw_owns.h"
#include "iw/iw_signal.h"

/* /////////////////////////////////////////////////////////////////////////////
//                   IwsVector - Vector structure
///////////////////////////////////////////////////////////////////////////// */
IW_DECL(void) iwsVector_Init(IwsVector *pVector)
{
    if(!pVector)
        return;

    pVector->m_dataType    = ipp8u;
    pVector->m_typeSize    = 0;
    pVector->m_size        = 0;
    pVector->m_pBuffer     = NULL;
    pVector->m_ptr         = NULL;
    pVector->m_ptrConst    = NULL;

    pVector->m_inMemSize.left = pVector->m_inMemSize.right  = 0;
}

IW_DECL(IppStatus) iwsVector_InitExternal(IwsVector *pVector, IwSize size, IppDataType dataType, IwsBorderSize const *pInMemBorder, void *pBuffer)
{
    if(!pVector)
        return ippStsNullPtrErr;

    iwsVector_Init(pVector);
    if(size < 0)
        return ippStsSizeErr;

    pVector->m_typeSize = iwTypeToSize(dataType);
    if(!pVector->m_typeSize)
        return ippStsDataTypeErr;

    pVector->m_dataType = dataType;
    pVector->m_size     = size;

    if(pInMemBorder)
    {
        if(ownsBorderSizeIsNegative(pInMemBorder))
            return iwStsBorderNegSizeErr;

        pVector->m_inMemSize.left   = pInMemBorder->left;
        pVector->m_inMemSize.right  = pInMemBorder->right;
    }

    pVector->m_ptr      = pBuffer;
    pVector->m_ptrConst = pVector->m_ptr;

    return ippStsNoErr;
}

IW_DECL(IppStatus) iwsVector_InitExternalConst(IwsVector *pVector, IwSize size, IppDataType dataType, IwsBorderSize const *pInMemBorder, const void *pBuffer)
{
    if(!pVector)
        return ippStsNullPtrErr;

    iwsVector_Init(pVector);

    if(size < 0)
        return ippStsSizeErr;

    pVector->m_typeSize = iwTypeToSize(dataType);
    if(!pVector->m_typeSize)
        return ippStsDataTypeErr;

    pVector->m_dataType = dataType;
    pVector->m_size     = size;

    if(pInMemBorder)
    {
        if(ownsBorderSizeIsNegative(pInMemBorder))
            return iwStsBorderNegSizeErr;

        pVector->m_inMemSize.left   = pInMemBorder->left;
        pVector->m_inMemSize.right  = pInMemBorder->right;
    }

    pVector->m_ptrConst = pBuffer;

    return ippStsNoErr;
}

IW_DECL(IppStatus) iwsVector_Alloc(IwsVector *pVector, IwSize size, IppDataType dataType, IwsBorderSize const *pInMemBorder)
{
    IwSize allocSize;

    if(!pVector)
        return ippStsNullPtrErr;

    iwsVector_Release(pVector);

    if(size < 0)
        return ippStsSizeErr;

    pVector->m_typeSize = iwTypeToSize(dataType);
    if(!pVector->m_typeSize)
        return ippStsDataTypeErr;

    pVector->m_dataType = dataType;
    pVector->m_size     = size;

    if(pInMemBorder)
    {
        if(ownsBorderSizeIsNegative(pInMemBorder))
            return iwStsBorderNegSizeErr;

        pVector->m_inMemSize.left   = pInMemBorder->left;
        pVector->m_inMemSize.right  = pInMemBorder->right;
    }
    allocSize = (pVector->m_size + pVector->m_inMemSize.left + pVector->m_inMemSize.right)*pVector->m_typeSize;
    if(!allocSize) // zero size memory request
        return ippStsNoErr;

    pVector->m_pBuffer = ippsMalloc_8u_L(allocSize);
    if(!pVector->m_pBuffer)
        return ippStsMemAllocErr;

    pVector->m_ptr      = (Ipp8u*)pVector->m_pBuffer + pVector->m_inMemSize.left*pVector->m_typeSize;
    pVector->m_ptrConst = pVector->m_ptr;

    return ippStsNoErr;
}

IW_DECL(void) iwsVector_Release(IwsVector *pVector)
{
    if(!pVector)
        return;

    if(pVector->m_pBuffer)
    {
        ippsFree(pVector->m_pBuffer);
        pVector->m_pBuffer  = NULL;
        pVector->m_ptr      = NULL;
        pVector->m_ptrConst = NULL;
    }
}

IW_DECL(void*) iwsVector_GetPtr(const IwsVector *pVector, IwSize x)
{
    if(!pVector || !pVector->m_ptr)
        return NULL;
    return (((Ipp8u*)pVector->m_ptr) + x*pVector->m_typeSize);
}

IW_DECL(const void*) iwsVector_GetPtrConst(const IwsVector *pVector, IwSize x)
{
    if(!pVector || !pVector->m_ptrConst)
        return NULL;
    return (((const Ipp8u*)pVector->m_ptrConst) + x*pVector->m_typeSize);
}

IW_DECL(IppStatus) iwsVector_BorderAdd(IwsVector *pVector, IwsBorderSize borderSize)
{
    if(!pVector || !pVector->m_ptrConst)
        return ippStsNullPtrErr;

    if(ownsBorderSizeIsNegative(&borderSize))
        return iwStsBorderNegSizeErr;

    if(borderSize.left + borderSize.right >= pVector->m_size)
        return ippStsSizeErr;

    if(pVector->m_ptr)
        pVector->m_ptrConst = pVector->m_ptr = iwsVector_GetPtr(pVector, borderSize.left);
    else
        pVector->m_ptrConst = iwsVector_GetPtrConst(pVector, borderSize.left);
    pVector->m_size = pVector->m_size - borderSize.left - borderSize.right;
    pVector->m_inMemSize.left   += borderSize.left;
    pVector->m_inMemSize.right  += borderSize.right;

    return ippStsNoErr;
}

IW_DECL(IppStatus) iwsVector_BorderSub(IwsVector *pVector, IwsBorderSize borderSize)
{
    if(!pVector || !pVector->m_ptrConst)
        return ippStsNullPtrErr;

    if(ownsBorderSizeIsNegative(&borderSize))
        return iwStsBorderNegSizeErr;

    if(borderSize.left > pVector->m_inMemSize.left)
        return ippStsOutOfRangeErr;
    if(borderSize.right > pVector->m_inMemSize.right)
        return ippStsOutOfRangeErr;

    if(pVector->m_ptr)
        pVector->m_ptrConst = pVector->m_ptr = iwsVector_GetPtr(pVector, -borderSize.left);
    else
        pVector->m_ptrConst = iwsVector_GetPtrConst(pVector, -borderSize.left);
    pVector->m_size = pVector->m_size + borderSize.left + borderSize.right;
    pVector->m_inMemSize.left   -= borderSize.left;
    pVector->m_inMemSize.right  -= borderSize.right;

    return ippStsNoErr;
}

IW_DECL(IppStatus) iwsVector_BorderSet(IwsVector *pVector, IwsBorderSize borderSize)
{
    IwSize diffLeft, diffRight;

    if(!pVector || !pVector->m_ptrConst)
        return ippStsNullPtrErr;

    if(ownsBorderSizeIsNegative(&borderSize))
        return iwStsBorderNegSizeErr;

    diffLeft    = borderSize.left    - pVector->m_inMemSize.left;
    diffRight   = borderSize.right   - pVector->m_inMemSize.right;

    if(diffLeft+diffRight >= pVector->m_size)
        return ippStsSizeErr;

    if(pVector->m_ptr)
        pVector->m_ptrConst = pVector->m_ptr = iwsVector_GetPtr(pVector, diffLeft);
    else
        pVector->m_ptrConst = iwsVector_GetPtrConst(pVector, diffLeft);
    pVector->m_size = pVector->m_size - (diffLeft + diffRight);
    pVector->m_inMemSize   = borderSize;

    return ippStsNoErr;
}

IW_DECL(IppStatus) iwsVector_RoiSet(IwsVector *pVector, IwsRoi roi)
{
    if(!pVector || !pVector->m_ptrConst)
        return ippStsNullPtrErr;

    // Unroll border
    if(pVector->m_ptr)
        pVector->m_ptrConst = pVector->m_ptr = iwsVector_GetPtr(pVector, -pVector->m_inMemSize.left);
    else
        pVector->m_ptrConst = iwsVector_GetPtrConst(pVector, -pVector->m_inMemSize.left);
    pVector->m_size = pVector->m_size + pVector->m_inMemSize.left + pVector->m_inMemSize.right;
    roi.x += pVector->m_inMemSize.left;

    // ROI saturation
    if(roi.len < 0) // Inverted ROI
    {
        roi.x     = roi.x + roi.len;
        roi.len = -roi.len;
    }
    if(roi.x < 0) // "Left" saturation
    {
        roi.len += roi.x;
        roi.x = 0;
    }
    if(roi.x + roi.len > pVector->m_size) // "Right" saturation
    {
        if(roi.x > pVector->m_size)
        {
            roi.x = pVector->m_size;
            roi.len = 0;
        }
        else
            roi.len = pVector->m_size - roi.x;
    }

    // Rebuild border
    pVector->m_inMemSize.left   = roi.x;
    pVector->m_inMemSize.right  = pVector->m_size  - roi.x - roi.len;
    pVector->m_size = roi.len;
    if(pVector->m_ptr)
        pVector->m_ptrConst = pVector->m_ptr = iwsVector_GetPtr(pVector, pVector->m_inMemSize.left);
    else
        pVector->m_ptrConst = iwsVector_GetPtrConst(pVector, pVector->m_inMemSize.left);

    return ippStsNoErr;
}

IW_DECL(IwsVector) iwsVector_GetRoiVector(const IwsVector *pVector, IwsRoi roi)
{
    IwsVector vector;

    iwsVector_Init(&vector);
    if(!pVector || !pVector->m_ptrConst)
        return vector;

    if(pVector->m_ptr)
    {
        if(iwsVector_InitExternal(&vector, pVector->m_size, pVector->m_dataType, &pVector->m_inMemSize, pVector->m_ptr) < 0)
        {
            iwsVector_Init(&vector);
            return vector;
        }
    }
    else
    {
        if(iwsVector_InitExternalConst(&vector, pVector->m_size, pVector->m_dataType, &pVector->m_inMemSize, pVector->m_ptrConst) < 0)
        {
            iwsVector_Init(&vector);
            return vector;
        }
    }
    if(iwsVector_RoiSet(&vector, roi) < 0)
    {
        iwsVector_Init(&vector);
        return vector;
    }
    return vector;
}

/* /////////////////////////////////////////////////////////////////////////////
//                   IW Tiling
///////////////////////////////////////////////////////////////////////////// */
IW_DECL(int) ownsTile_BoundToSize(IwsRoi *pRoi, IwSize *pMinSize)
{
    if(pRoi->x >= *pMinSize)
        return 0;
    else if(pRoi->x < 0)
        pRoi->x = 0;

    if(pRoi->len + pRoi->x > *pMinSize)
        pRoi->len = *pMinSize - pRoi->x;

    if(pRoi->len <= 0)
        return 0;
    else
    {
        *pMinSize = pRoi->len;
        return 1;
    }
}

/* /////////////////////////////////////////////////////////////////////////////
//                   IwsTile tiling structure
///////////////////////////////////////////////////////////////////////////// */
IW_DECL(IwsTile) iwsTile_SetRoi(IwsRoi tileRoi)
{
    IwsTile roi = {0};
    roi.m_dstRoi      = tileRoi;
    roi.m_initialized = ownTileInitSimple;
    return roi;
}
