/*******************************************************************************
* 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/iw_signal_transform.h"
#include "iw_owns.h"

#include "ipps.h"

IW_DECL(IppStatus) llwsDCT_Fwd(const void* pSrc, void* pDst, int len, IppDataType dataType, IppHintAlgorithm algoMode);
IW_DECL(IppStatus) llwsDCT_Inv(const void* pSrc, void* pDst, int len, IppDataType dataType, IppHintAlgorithm algoMode);

/* /////////////////////////////////////////////////////////////////////////////
//                   iwsDCT
///////////////////////////////////////////////////////////////////////////// */
IW_DECL(IppStatus) iwsDCT(const IwsVector *pSrcVector, IwsVector *pDstVector, IwTransDirection direction, const IwsDCTParams *pAuxParams)
{
    IppStatus    status;
    IwsDCTParams auxParams;

    status = ownsCheckVectorRead(pSrcVector);
    if(status)
        return status;
    status = ownsCheckVectorWrite(pDstVector);
    if(status)
        return status;

    if(pSrcVector->m_dataType != pDstVector->m_dataType)
        return ippStsBadArgErr;

    switch(direction)
    {
    case iwTransForward:
    case iwTransInverse:
        break;
    default:
        return ippStsBadArgErr;
    }

    if(pAuxParams)
        auxParams = *pAuxParams;
    else
        iwsDCT_SetDefaultParams(&auxParams);

    {
        const void *pSrc = pSrcVector->m_ptrConst;
        void       *pDst = pDstVector->m_ptr;
        IwSize      size = IPP_MIN(pSrcVector->m_size, pDstVector->m_size);

        // Long compatibility check
        {
            status = ownLongCompatCheckValue(size, NULL);
            if(status < 0)
                return status;

            if(direction == iwTransForward)
                return llwsDCT_Fwd(pSrc, pDst, (int)size, pSrcVector->m_dataType, auxParams.algoMode);
            else if(direction == iwTransInverse)
                return llwsDCT_Inv(pSrc, pDst, (int)size, pSrcVector->m_dataType, auxParams.algoMode);
            else
                return ippStsBadArgErr;
        }
    }
}

/**/////////////////////////////////////////////////////////////////////////////
//                   Low-Level Wrappers
///////////////////////////////////////////////////////////////////////////// */
IW_DECL(IppStatus) llwsDCT_Fwd(const void* pSrc, void* pDst, int len, IppDataType dataType, IppHintAlgorithm algoMode)
{
    IppStatus status;

    IppsDCTFwdSpec_32f *pSpec32  = 0;
    IppsDCTFwdSpec_64f *pSpec64  = 0;
    int                 specSize = 0;

    Ipp8u   *pTmpBuffer    = 0;
    int      tmpBufferSize = 0;
    Ipp8u   *pInitBuf      = 0;
    int      initSize      = 0;

    for(;;)
    {
        // Initialize Intel IPP functions and check parameters
        switch(dataType)
        {
        case ipp32f:
        {
            status = ippsDCTFwdGetSize_32f(len, algoMode, &specSize, &initSize, &tmpBufferSize);
            if(status < 0)
                break;

            pSpec32 = (IppsDCTFwdSpec_32f*)ownSharedMalloc(specSize);
            if(!pSpec32)
            {
                status = ippStsNoMemErr;
                break;
            }
        }
        break;
        case ipp64f:
        {
            status = ippsDCTFwdGetSize_64f(len, algoMode, &specSize, &initSize, &tmpBufferSize);
            if(status < 0)
                break;

            pSpec64 = (IppsDCTFwdSpec_64f*)ownSharedMalloc(specSize);
            if(!pSpec64)
            {
                status = ippStsNoMemErr;
                break;
            }
        }
        break;
        default:
            status = ippStsDataTypeErr; break;
        }
        if(status < 0)
            break;

        pInitBuf = (Ipp8u*)ownSharedMalloc(initSize);
        if(initSize && !pInitBuf)
        {
            status = ippStsNoMemErr;
            break;
        }

        switch(dataType)
        {
        case ipp32f: status = ippsDCTFwdInit_32f(&pSpec32, len, algoMode, (Ipp8u*)pSpec32, pInitBuf); break;
        case ipp64f: status = ippsDCTFwdInit_64f(&pSpec64, len, algoMode, (Ipp8u*)pSpec64, pInitBuf); break;
        default:     status = ippStsDataTypeErr; break;
        }
        if(status < 0)
            break;

        pTmpBuffer = (Ipp8u*)ownSharedMalloc(tmpBufferSize);
        if(tmpBufferSize && !pTmpBuffer)
        {
            status = ippStsNoMemErr;
            break;
        }

        if(pSrc == pDst)
        {
            switch(dataType)
            {
            case ipp32f: status = ippsDCTFwd_32f_I((Ipp32f*)pSrc, pSpec32, pTmpBuffer); break;
            case ipp64f: status = ippsDCTFwd_64f_I((Ipp64f*)pSrc, pSpec64, pTmpBuffer); break;
            default:     status = ippStsDataTypeErr; break;
            }
            break;
        }
        else
        {
            switch(dataType)
            {
            case ipp32f: status = ippsDCTFwd_32f((Ipp32f*)pSrc, (Ipp32f*)pDst, pSpec32, pTmpBuffer); break;
            case ipp64f: status = ippsDCTFwd_64f((Ipp64f*)pSrc, (Ipp64f*)pDst, pSpec64, pTmpBuffer); break;
            default:     status = ippStsDataTypeErr; break;
            }
            break;
        }
    }

    if(pInitBuf)
        ownSharedFree(pInitBuf);
    if(pSpec32)
        ownSharedFree(pSpec32);
    if(pSpec64)
        ownSharedFree(pSpec64);
    if(pTmpBuffer)
        ownSharedFree(pTmpBuffer);

    return status;
}

IW_DECL(IppStatus) llwsDCT_Inv(const void* pSrc, void* pDst, int len, IppDataType dataType, IppHintAlgorithm algoMode)
{
    IppStatus status;

    IppsDCTInvSpec_32f *pSpec32  = 0;
    IppsDCTInvSpec_64f *pSpec64  = 0;
    int                 specSize = 0;

    Ipp8u   *pTmpBuffer    = 0;
    int      tmpBufferSize = 0;
    Ipp8u   *pInitBuf      = 0;
    int      initSize      = 0;

    for(;;)
    {
        // Initialize Intel IPP functions and check parameters
        switch(dataType)
        {
        case ipp32f:
        {
            status = ippsDCTInvGetSize_32f(len, algoMode, &specSize, &initSize, &tmpBufferSize);
            if(status < 0)
                break;

            pSpec32 = (IppsDCTInvSpec_32f*)ownSharedMalloc(specSize);
            if(!pSpec32)
            {
                status = ippStsNoMemErr;
                break;
            }
        }
        break;
        case ipp64f:
        {
            status = ippsDCTInvGetSize_64f(len, algoMode, &specSize, &initSize, &tmpBufferSize);
            if(status < 0)
                break;

            pSpec64 = (IppsDCTInvSpec_64f*)ownSharedMalloc(specSize);
            if(!pSpec64)
            {
                status = ippStsNoMemErr;
                break;
            }
        }
        break;
        default:
            status = ippStsDataTypeErr; break;
        }
        if(status < 0)
            break;

        pInitBuf = (Ipp8u*)ownSharedMalloc(initSize);
        if(initSize && !pInitBuf)
        {
            status = ippStsNoMemErr;
            break;
        }

        switch(dataType)
        {
        case ipp32f: status = ippsDCTInvInit_32f(&pSpec32, len, algoMode, (Ipp8u*)pSpec32, pInitBuf); break;
        case ipp64f: status = ippsDCTInvInit_64f(&pSpec64, len, algoMode, (Ipp8u*)pSpec64, pInitBuf); break;
        default:     status = ippStsDataTypeErr; break;
        }
        if(status < 0)
            break;

        pTmpBuffer = (Ipp8u*)ownSharedMalloc(tmpBufferSize);
        if(tmpBufferSize && !pTmpBuffer)
        {
            status = ippStsNoMemErr;
            break;
        }

        if(pSrc == pDst)
        {
            switch(dataType)
            {
            case ipp32f: status = ippsDCTInv_32f_I((Ipp32f*)pSrc, pSpec32, pTmpBuffer); break;
            case ipp64f: status = ippsDCTInv_64f_I((Ipp64f*)pSrc, pSpec64, pTmpBuffer); break;
            default:     status = ippStsDataTypeErr; break;
            }
            break;
        }
        else
        {
            switch(dataType)
            {
            case ipp32f: status = ippsDCTInv_32f((Ipp32f*)pSrc, (Ipp32f*)pDst, pSpec32, pTmpBuffer); break;
            case ipp64f: status = ippsDCTInv_64f((Ipp64f*)pSrc, (Ipp64f*)pDst, pSpec64, pTmpBuffer); break;
            default:     status = ippStsDataTypeErr; break;
            }
            break;
        }
    }

    if(pInitBuf)
        ownSharedFree(pInitBuf);
    if(pSpec32)
        ownSharedFree(pSpec32);
    if(pSpec64)
        ownSharedFree(pSpec64);
    if(pTmpBuffer)
        ownSharedFree(pTmpBuffer);

    return status;
}
